  /* ─── Wiki modal ──────────────────────────────────────────────────────
     Full-screen overlay with a center panel. Three columns: sidebar
     (categories), entry list, detail pane (3D viewer or icon + stats +
     prose). Backdrop blur to soften the landing scene rendered behind. */
  .wiki-modal {
    position: fixed; inset: 0; z-index: 7000;
    /* rc159 — opaque wiki backdrop. */
    background: var(--panel-bg-2, #0e1426);
    display: flex; align-items: center; justify-content: center;
    padding: 24px;
    color: #e8e6f4;
    font-family: inherit;
    transition: opacity 0.18s ease;
  }
  .wiki-modal.hidden { display: none; opacity: 0; pointer-events: none; }

  .wiki-panel {
    width: min(1180px, 100%);
    height: min(820px, 100%);
    /* rc159 — opaque wiki panel. */
    background: #1c162a;
    border: 1px solid #5e4878;
    border-radius: 18px;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55);
    display: flex; flex-direction: column;
    overflow: hidden;
  }

  .wiki-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: calc(18px + env(safe-area-inset-top, 0px)) 26px 18px;
    border-bottom: 1px solid #3b2f54;
  }
  .wiki-title {
    font-size: 20px; font-weight: 800; letter-spacing: 3px;
    color: #ffc678;
  }
  .wiki-close {
    background: transparent; border: 1px solid rgba(180, 150, 220, 0.2);
    color: #d1aeff; font-size: 22px; line-height: 1;
    width: 36px; height: 36px; border-radius: 8px;
    cursor: pointer;
    transition: background 0.18s, border-color 0.18s, color 0.18s;
  }
  .wiki-close:hover {
    background: rgba(209, 174, 255, 0.1);
    border-color: rgba(209, 174, 255, 0.4);
    color: #ffe6ff;
  }

  .wiki-cols {
    display: grid;
    grid-template-columns: 200px 240px 1fr;
    flex: 1 1 auto;
    min-height: 0;  /* let children scroll */
  }

  .wiki-sidebar {
    display: flex; flex-direction: column;
    padding: 14px 10px;
    border-right: 1px solid rgba(180, 150, 220, 0.12);
    background: rgba(0, 0, 0, 0.2);
    gap: 4px;
  }
  .wiki-cat-btn {
    display: block; width: 100%;
    padding: 10px 14px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 8px;
    color: #b8a8d0;
    font-family: inherit; font-size: 12px; font-weight: 800;
    letter-spacing: 1.4px; text-align: left;
    cursor: pointer;
    transition: background 0.18s, color 0.18s, border-color 0.18s;
  }
  .wiki-cat-btn:hover { color: #e8e6f4; background: rgba(180, 150, 220, 0.06); }
  .wiki-cat-btn.active {
    background: rgba(255, 196, 120, 0.12);
    border-color: rgba(255, 196, 120, 0.3);
    color: #ffc678;
  }

  .wiki-list {
    list-style: none; padding: 12px 8px; margin: 0;
    overflow-y: auto;
    border-right: 1px solid rgba(180, 150, 220, 0.12);
    scrollbar-width: thin;
    scrollbar-color: rgba(180, 150, 220, 0.3) transparent;
  }
  .wiki-list::-webkit-scrollbar { width: 8px; }
  .wiki-list::-webkit-scrollbar-thumb {
    background: rgba(180, 150, 220, 0.3); border-radius: 4px;
  }
  .wiki-list-item {
    padding: 9px 12px;
    border-radius: 6px;
    color: #c9bdde;
    font-size: 13px;
    cursor: pointer;
    transition: background 0.16s, color 0.16s;
  }
  .wiki-list-item:hover { background: rgba(180, 150, 220, 0.08); color: #fff; }
  .wiki-list-item.active {
    background: rgba(255, 196, 120, 0.14);
    color: #ffe6c8;
    box-shadow: inset 3px 0 0 #ffc678;
  }
  /* Unseen entries display as `????` — dim the text + add letter-spacing
     so it visually separates from the actual entry names without making
     the list feel like a wall of placeholders. */
  .wiki-list-item.unseen {
    color: #6a6280;
    letter-spacing: 3px;
    font-style: italic;
  }
  .wiki-list-item.unseen.active { color: #b9a8d8; }

  .wiki-detail {
    overflow-y: auto;
    padding: 22px 28px 28px;
    scrollbar-width: thin;
    scrollbar-color: rgba(180, 150, 220, 0.3) transparent;
  }
  .wiki-detail::-webkit-scrollbar { width: 10px; }
  .wiki-detail::-webkit-scrollbar-thumb {
    background: rgba(180, 150, 220, 0.3); border-radius: 5px;
  }

  .wiki-detail-head { margin-bottom: 16px; }
  .wiki-detail-head h2 {
    margin: 0 0 4px; font-size: 24px; font-weight: 800;
    color: #ffe6c8; letter-spacing: 0.5px;
  }
  .wiki-tag {
    color: #b89cff; font-style: italic; font-size: 14px;
    letter-spacing: 0.3px;
  }

  /* 3D viewer container — width matches the detail-pane layout, height
     scales with width for a square preview frame. */
  .wiki-3d {
    position: relative;
    width: 100%; max-width: 380px;
    aspect-ratio: 1 / 1;
    margin: 8px 0 18px;
    border-radius: 12px;
    background: radial-gradient(circle at 50% 40%, rgba(255, 196, 120, 0.06), rgba(8, 6, 14, 0.6));
    border: 1px solid rgba(180, 150, 220, 0.16);
    overflow: hidden;
  }
  .wiki-3d-large { max-width: 460px; }
  .wiki-3d-canvas {
    display: block; width: 100% !important; height: 100% !important;
  }

  /* Icon viewer (powerups + limit breaks + superweapons) — drops the
     authored SVG large-format with a soft glow. */
  .wiki-icon {
    width: 180px; height: 180px;
    margin: 8px 0 18px;
    padding: 18px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 12px;
    background: radial-gradient(circle at 50% 40%, rgba(255, 196, 120, 0.08), rgba(8, 6, 14, 0.6));
    border: 1px solid rgba(180, 150, 220, 0.16);
  }
  .wiki-icon svg { width: 100%; height: 100%; }
  /* Limit-break entries reuse the EXACT same medallion treatment the relic
     shop uses — circular frame, 36px svg, drop-shadow — so the icon the
     player sees in the wiki is identical pixel-for-pixel to the icon they
     bought in the relic shop. The 180×180 surrounding card stays so the
     detail pane doesn't feel empty; a violet glow ring picks up the limit-
     break theme. The relic-card-icon size (36px) is preserved literally so
     the artwork (authored at 64×64 viewBox to read sharp at small sizes)
     renders at its design intent rather than being blown up. */
  .wiki-icon-limit { box-shadow: 0 0 24px rgba(180, 120, 255, 0.18) inset; }
  .wiki-icon-limit svg {
    width: 96px; height: 96px;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.45));
  }
  .wiki-icon-super { box-shadow: 0 0 28px rgba(255, 196, 120, 0.22) inset; }

  .wiki-stats {
    display: grid;
    grid-template-columns: minmax(120px, 200px) 1fr;
    gap: 6px 18px;
    margin-bottom: 18px;
    padding: 12px 16px;
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.18);
    border: 1px solid rgba(180, 150, 220, 0.1);
  }
  .wiki-stat-row {
    display: contents;
  }
  .wiki-stat-key {
    color: #b8a8d0; font-size: 12px; font-weight: 700;
    letter-spacing: 1.0px; text-transform: uppercase;
    align-self: center;
  }
  .wiki-stat-val {
    color: #e8e6f4; font-size: 14px;
    display: inline-flex; align-items: center; gap: 6px;
  }
  .wiki-color-chip {
    display: inline-block;
    width: 14px; height: 14px;
    border-radius: 3px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    box-shadow: 0 0 8px currentColor;
  }

  .wiki-prose {
    margin-bottom: 18px;
    line-height: 1.55;
  }
  .wiki-prose h3 {
    margin: 0 0 6px; font-size: 13px; letter-spacing: 1.2px;
    color: #ffc678; font-weight: 800; text-transform: uppercase;
  }
  .wiki-prose p {
    margin: 0; color: #d8d2e8; font-size: 14px;
  }

  .wiki-empty {
    padding: 40px 20px; color: #8a809a;
    text-align: center; font-style: italic;
  }

  /* ─── Owner-only DEV blocks (rc331) ───
     Raw stats + cost numbers, shown only when owner mode is on (?devwiki=1).
     Distinct amber-on-dark "developer" treatment so it's unmistakable these
     numbers are NOT part of the player-facing wiki. */
  .wiki-owner-block {
    margin: 0 0 18px;
    padding: 12px 14px 14px;
    border-radius: 10px;
    background: rgba(255, 168, 88, 0.05);
    border: 1px dashed rgba(255, 168, 88, 0.42);
  }
  .wiki-owner-block .wiki-stats {
    margin-bottom: 0;
    background: transparent;
    border: none;
    padding: 0;
  }
  .wiki-owner-tag {
    font-size: 11px; font-weight: 800; letter-spacing: 1.4px;
    text-transform: uppercase;
    color: #ffb45a;
    margin-bottom: 10px;
    display: flex; align-items: center; gap: 6px;
  }
  .wiki-owner-tag::before {
    content: '';
    width: 8px; height: 8px; border-radius: 2px;
    background: #ffb45a;
    box-shadow: 0 0 8px rgba(255, 180, 90, 0.8);
  }
  /* Economy · Dev sidebar category gets the same amber accent so it reads as
     a developer surface, not a normal player section. */
  .wiki-cat-owner { color: #ffb45a !important; }
  .wiki-cat-owner.active { color: #ffc678 !important; }
  .wiki-tag-dev {
    color: #ffb45a !important;
    font-style: normal !important;
    font-weight: 800; letter-spacing: 1.4px; font-size: 11px;
    text-transform: uppercase;
  }
  /* Achievement tier chips reuse the .wiki-tag slot with per-tier color. */
  .wiki-tag-bronze { color: #c9905a !important; font-style: normal !important; font-weight: 800; }
  .wiki-tag-silver { color: #c2cad6 !important; font-style: normal !important; font-weight: 800; }
  .wiki-tag-gold   { color: #ffce5a !important; font-style: normal !important; font-weight: 800; }
  /* Economy prose lists + inline code. */
  .wiki-prose ul { margin: 6px 0 0; padding-left: 20px; color: #d8d2e8; font-size: 14px; line-height: 1.55; }
  .wiki-prose li { margin: 2px 0; }
  .wiki-prose code {
    background: rgba(255, 196, 120, 0.1);
    border: 1px solid rgba(255, 196, 120, 0.18);
    border-radius: 4px;
    padding: 0 4px;
    font-size: 12.5px;
    color: #ffd8a0;
  }

  /* Back button — only visible on mobile when the drill-down is below the
     root category level. Sits to the LEFT of the title. Hidden by default
     (hidden on desktop entirely; on mobile only when at categories level)
     and revealed via the .visible class set by _wikiSetMobileLevel. */
  .wiki-back {
    display: none;
    background: transparent; border: 1px solid rgba(180, 150, 220, 0.2);
    color: #d1aeff; font-size: 22px; line-height: 1;
    width: 36px; height: 36px; border-radius: 8px;
    cursor: pointer; margin-right: 8px;
    transition: background 0.18s, border-color 0.18s, color 0.18s;
  }
  .wiki-back:hover {
    background: rgba(209, 174, 255, 0.1);
    border-color: rgba(209, 174, 255, 0.4);
    color: #ffe6ff;
  }
  /* Glyph + label + chevron for the category buttons. Layout shared
     between desktop (sidebar) and mobile (full-width cards) — the
     mobile-specific media query below tunes the size + visibility of
     the glyph and chevron without changing the markup. */
  .wiki-cat-btn {
    display: flex; align-items: center;
    gap: 10px;
  }
  .wiki-cat-glyph {
    width: 18px; height: 18px;
    flex: 0 0 auto;
    color: #b8a8d0;
    transition: color 0.18s;
  }
  .wiki-cat-btn.active .wiki-cat-glyph { color: #ffc678; }
  .wiki-cat-label { flex: 1 1 auto; }
  /* Chevron is desktop-hidden — mobile only. Helps the card read as
     "tap to go deeper" instead of looking like a stand-alone button. */
  .wiki-cat-chevron {
    display: none;
    color: #6a6280; font-size: 24px; line-height: 1;
    flex: 0 0 auto;
  }

  /* Mobile drill-down. Single pane visible at a time — categories →
     list → detail. .wiki-cols carries data-mobile-level which CSS
     reads to hide/show panes. Each pane goes full height so the
     visible content is never sandwiched between two horizontal-scroll
     bars (the previous design lost ~30% of vertical space to the
     stacked sidebar + list ribbons). Header back-button + breadcrumb
     title give the user a clear path back up the drill-down. */
  @media (max-width: 720px) {
    .wiki-modal { padding: 0; }
    .wiki-panel {
      width: 100%; height: 100%;
      max-width: none; max-height: none;
      border-radius: 0;
      border-left: none; border-right: none;
    }
    .wiki-header {
      padding: calc(12px + env(safe-area-inset-top, 0px)) 14px 12px;
      gap: 4px;
    }
    .wiki-title { font-size: 16px; letter-spacing: 2px; flex: 1 1 auto; text-align: center; }
    .wiki-back.visible { display: inline-flex; align-items: center; justify-content: center; }
    .wiki-cols {
      display: block;       /* override 3-col grid */
      flex: 1 1 auto;
      min-height: 0;
      position: relative;
    }
    /* Drill-down visibility: each pane is its own full-height drawer.
       data-mobile-level on the cols container drives which one is
       visible. */
    .wiki-cols > .wiki-sidebar,
    .wiki-cols > .wiki-list,
    .wiki-cols > .wiki-detail {
      position: absolute; inset: 0;
      width: 100%; height: 100%;
      border: none;
    }
    .wiki-cols[data-mobile-level="categories"] > .wiki-sidebar { display: flex; }
    .wiki-cols[data-mobile-level="categories"] > .wiki-list,
    .wiki-cols[data-mobile-level="categories"] > .wiki-detail { display: none; }
    .wiki-cols[data-mobile-level="list"] > .wiki-list { display: block; }
    .wiki-cols[data-mobile-level="list"] > .wiki-sidebar,
    .wiki-cols[data-mobile-level="list"] > .wiki-detail { display: none; }
    .wiki-cols[data-mobile-level="detail"] > .wiki-detail { display: block; }
    .wiki-cols[data-mobile-level="detail"] > .wiki-sidebar,
    .wiki-cols[data-mobile-level="detail"] > .wiki-list { display: none; }

    /* Categories — vertical stack of full-width cards (44px tap target
       minimum, matches iOS HIG). Glyph + label + chevron, big and
       legible. */
    .wiki-sidebar {
      flex-direction: column;
      padding: 14px 14px 28px;
      overflow-y: auto;
      gap: 8px;
      background: transparent;
    }
    .wiki-cat-btn {
      width: 100%;
      padding: 16px 18px;
      font-size: 14px; letter-spacing: 1.6px;
      background: rgba(180, 150, 220, 0.05);
      border: 1px solid rgba(180, 150, 220, 0.14);
      border-radius: 12px;
      gap: 14px;
    }
    .wiki-cat-btn.active {
      background: rgba(255, 196, 120, 0.12);
      border-color: rgba(255, 196, 120, 0.3);
    }
    .wiki-cat-glyph { width: 26px; height: 26px; }
    .wiki-cat-chevron { display: inline-block; }

    /* List — vertical, full-height. Each row is a real tap target. */
    .wiki-list {
      display: block;
      padding: 10px 12px 28px;
      overflow-y: auto;
      list-style: none;
      margin: 0;
    }
    .wiki-list-item {
      display: flex; align-items: center;
      padding: 14px 16px;
      margin-bottom: 4px;
      font-size: 15px;
      border-radius: 10px;
      background: rgba(180, 150, 220, 0.04);
      border: 1px solid rgba(180, 150, 220, 0.08);
      box-shadow: none !important;
    }
    .wiki-list-item::after {
      content: '\203A'; /* › */
      margin-left: auto;
      color: #6a6280;
      font-size: 22px; line-height: 1;
    }
    .wiki-list-item.active {
      background: rgba(255, 196, 120, 0.12);
      border-color: rgba(255, 196, 120, 0.26);
    }
    .wiki-list-item.unseen { letter-spacing: 4px; }

    /* Detail — full screen with smaller 3D viewer (32vh max) so stats
       + prose are visible without a long scroll. */
    .wiki-detail { padding: 14px 16px 28px; }
    .wiki-detail-head h2 { font-size: 22px; }
    .wiki-3d {
      width: 100%; max-width: 100%;
      max-height: 32vh; aspect-ratio: 1 / 1;
    }
    .wiki-3d-large { max-width: 100%; max-height: 36vh; }
    .wiki-icon { width: 140px; height: 140px; }
    .wiki-stats {
      grid-template-columns: 1fr 1fr;
      gap: 8px 12px;
    }
  }
  /* Sync icon — floats in the overlay's top-right corner so it reads as a
     tucked-away utility instead of competing with SHOP/PLAY. Double-id
     specificity beats the blanket `#overlay button` gradient below. */
  /* Top-right utility cluster: Achievements (mounted first) + Wiki + Sync.
     Flex row, gap between pills. Anchors top-right of #overlay; on
     mobile shifts in slightly. */
  #overlay #landing-top-right-utils {
    position: absolute;
    /* Add the system status bar inset on Android 15 / iOS notch devices.
       env() falls back to 0 in browsers without a safe area, so this
       value collapses to the original 14px on web. The safe-area inset
       is populated by viewport-fit=cover in index.html. */
    top: calc(14px + env(safe-area-inset-top, 0px));
    right: calc(14px + env(safe-area-inset-right, 0px));
    display: flex; flex-direction: row; align-items: center; gap: 8px;
    z-index: 2;
  }
  @media (max-width: 600px) {
    #overlay #landing-top-right-utils {
      top: calc(10px + env(safe-area-inset-top, 0px));
      right: calc(10px + env(safe-area-inset-right, 0px));
      gap: 6px;
    }
  }

  /* Sync-progress nudge. Floats directly below the sync button (top-right
     corner of the overlay) with an up-pointing arrow so the connection to
     the icon reads at a glance. Hidden by default — the boot/landing-visible
     handler flips `.visible` on only when syncNudge.shouldShow() returns
     true. Dismissal permanence lives in localStorage via syncNudge.dismiss().
     Animation: slide-down entrance + gentle bob + slow border glow pulse. */
  #sync-nudge {
    position: absolute; top: 62px; right: 14px;
    display: none; align-items: flex-start; gap: 10px;
    width: 250px; padding: 12px 12px 12px 14px;
    background: linear-gradient(135deg, rgba(18, 32, 64, 0.96), rgba(12, 22, 48, 0.96));
    border: 1px solid rgba(126, 226, 255, 0.45);
    border-radius: 14px;
    box-shadow:
      0 10px 28px rgba(0, 0, 0, 0.45),
      0 0 0 1px rgba(126, 226, 255, 0.12) inset,
      0 0 22px rgba(126, 226, 255, 0.22);
    color: var(--text);
    font-family: inherit;
    pointer-events: auto;
    transform-origin: top right;
    z-index: 2;
  }
  #sync-nudge.visible {
    display: flex;
    animation:
      sync-nudge-in 0.52s cubic-bezier(0.18, 0.9, 0.32, 1.2) both,
      sync-nudge-bob 3.6s ease-in-out 0.55s infinite,
      sync-nudge-glow 2.4s ease-in-out 0.55s infinite;
  }
  #sync-nudge.dismissing {
    animation: sync-nudge-out 0.28s ease-in forwards !important;
    pointer-events: none;
  }
  .sync-nudge-arrow {
    position: absolute; top: -7px; right: 18px;
    width: 14px; height: 14px;
    background: linear-gradient(135deg, rgba(18, 32, 64, 0.96), rgba(12, 22, 48, 0.96));
    border-top: 1px solid rgba(126, 226, 255, 0.45);
    border-left: 1px solid rgba(126, 226, 255, 0.45);
    transform: rotate(45deg);
  }
  .sync-nudge-body {
    flex: 1 1 auto;
    min-width: 0;
    cursor: pointer;
  }
  .sync-nudge-title {
    font-size: 13px; font-weight: 800;
    letter-spacing: 0.6px; text-transform: uppercase;
    color: #cfeeff;
    margin-bottom: 3px;
  }
  .sync-nudge-msg {
    font-size: 12px;
    line-height: 1.35;
    color: var(--text-dim);
  }
  #overlay #sync-nudge-close {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; padding: 0;
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-dim);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 50%;
    cursor: pointer;
    box-shadow: none;
    transition: color 0.18s, border-color 0.18s, background 0.18s, transform 0.18s;
  }
  #overlay #sync-nudge-close:hover,
  #overlay #sync-nudge-close:focus-visible {
    color: var(--text);
    border-color: rgba(255, 255, 255, 0.32);
    background: rgba(255, 255, 255, 0.12);
    transform: scale(1.08);
  }
  /* Entrance: drops from above the sync button with a tiny overshoot. */
  @keyframes sync-nudge-in {
    0%   { opacity: 0; transform: translateY(-18px) scale(0.92); }
    60%  { opacity: 1; transform: translateY(4px)  scale(1.02); }
    100% { opacity: 1; transform: translateY(0)    scale(1); }
  }
  @keyframes sync-nudge-out {
    0%   { opacity: 1; transform: translateY(0)    scale(1); }
    100% { opacity: 0; transform: translateY(-10px) scale(0.94); }
  }
  /* Gentle vertical bob so the pill reads as alive without feeling nervous. */
  @keyframes sync-nudge-bob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-3px); }
  }
  /* Slow border-glow pulse that loops with the bob — draws the eye to the
     arrow pointing at the sync icon. */
  @keyframes sync-nudge-glow {
    0%, 100% {
      border-color: rgba(126, 226, 255, 0.45);
      box-shadow:
        0 10px 28px rgba(0, 0, 0, 0.45),
        0 0 0 1px rgba(126, 226, 255, 0.12) inset,
        0 0 22px rgba(126, 226, 255, 0.22);
    }
    50% {
      border-color: rgba(170, 238, 255, 0.8);
      box-shadow:
        0 10px 28px rgba(0, 0, 0, 0.5),
        0 0 0 1px rgba(170, 238, 255, 0.22) inset,
        0 0 36px rgba(126, 226, 255, 0.45);
    }
  }
  @media (max-width: 600px) {
    #sync-nudge {
      top: 54px; right: 10px;
      width: min(calc(100vw - 20px), 250px);
    }
    .sync-nudge-arrow { right: 14px; }
  }
  @media (prefers-reduced-motion: reduce) {
    #sync-nudge.visible {
      animation: sync-nudge-in 0.2s ease-out both;
    }
  }

  /* rc163 — SHOP tab header. Replaces the old #relic-shop modal's
     two-row header (title + close, then balance + buy). The tab
     panel doesn't need a close × (Play Hub commits per rc162), so
     the layout collapses to a single row: text on the left, balance
     pill + GET MORE CTA on the right. Element IDs preserved for the
     balance count + buy button so renderRelicUi() and the IAP
     wiring in src/iap.js find them without changes. */
  #shop-tab-header {
    display: none;
  }
  #shop-tab-header-text { flex: 1; min-width: 0; }
  #shop-tab-title { font-size: 22px; font-weight: 800; letter-spacing: 1.2px; }
  #shop-tab-subtitle { font-size: 12px; margin-top: 4px; line-height: 1.4; }
  #relic-shop-balance {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 8px 16px;
    border-radius: var(--radius-pill);
    background: #243a55;
    border: 1px solid #7ee2ff;
    font-weight: 700; font-size: 15px;
    color: #d8f4ff;
  }

  /* Spec 4-Android — "Get more" CTA in the relic shop header. JS
     unhides this only on Capacitor native; on web it stays
     [hidden] so we don't advertise an unavailable purchase path.
     Made more prominent now that it owns its own row — bigger tap
     target, more visible gradient, sized to feel like the primary
     action when present. */
  #relic-shop-buy {
    display: inline-flex; align-items: center; gap: 8px;
    margin-left: auto;  /* push to right edge of row */
    padding: 9px 18px;
    border-radius: var(--radius-pill);
    /* rc159 — opaque BUY CTA. */
    background: #7a3d70;
    border: 1px solid #ff8ed3;
    color: #ffd2ec;
    font: inherit; font-weight: 800; font-size: 13px; letter-spacing: 0.6px;
    text-transform: uppercase;
    cursor: pointer;
    transition: background 0.14s, border-color 0.14s, transform 0.12s, box-shadow 0.14s;
    box-shadow: 0 4px 14px rgba(196, 124, 255, 0.35);
  }
  #relic-shop-buy:hover {
    background: #9b4e8d;
    border-color: #ff8ed3;
    transform: translateY(-1px);
    box-shadow: 0 6px 18px rgba(196, 124, 255, 0.5);
  }
  #relic-shop-buy[hidden] { display: none !important; }

  /* Scrollable body — wraps both the upgrade grid and the limit-break
     section so the panel has ONE continuous scroll. Keeps the header
     sticky at the top of the modal. Previously the grid had its own
     nested scroll while the limit-break section flowed below it, which
     crushed the grid to near-zero on mobile. */
  /* ── Shop tab — open storybook (rc331, Figma-matched) ──────────────
     The shop panel is a wood-framed open book illustration. The two
     aged-parchment pages hold the upgrade cards (3 + 2 per page); the
     header sits on the top-left page. Replaces the earlier flat
     CSS-gradient parchment with the painted book art so the page reads
     like the design mockup (warm desk, gilt page edges, sparkles). */
  .play-hub-panel[data-tab="shop"] {
    background: #2a1a0e url('../assets/shop/shop-book-bg.webp') center center / cover no-repeat;
  }
  /* ── Unified Play-Hub HUD (cream parchment) ───────────────────────────
     Every tab shows the full HUD — avatar + level + energy + star balance +
     relic balance + daily-bin + settings — all wearing the shop's
     parchment-pill look. Scoped to #stage-select so these win over the base
     dark-navy HUD styles (further down this file) on every tab, regardless of
     source order. (Previously the shop re-skinned + trimmed the HUD and the
     gear tab hid most of it; both divergences are gone.) */
  #stage-select .play-hub-hud-avatar,
  #stage-select .play-hub-hud-settings {
    background: linear-gradient(180deg, #ecdcb6 0%, #d8c191 100%);
    border: 1.5px solid #8a6a3a;
    color: #3a2a18;
    box-shadow: 0 2px 6px rgba(40, 22, 8, 0.4), inset 0 0 0 2px rgba(120, 88, 45, 0.45), inset 0 1px 0 rgba(255, 250, 235, 0.4);
  }
  #stage-select .play-hub-hud-avatar:hover,
  #stage-select .play-hub-hud-settings:hover {
    background: linear-gradient(180deg, #f3e6c5 0%, #e0caa0 100%);
    border-color: #a07c44;
  }
  #stage-select .play-hub-hud-avatar-initial { color: #3a2a18; }
  /* Balance pills — level / energy / stars / relics share one parchment rect. */
  #stage-select .play-hub-hud-level,
  #stage-select .play-hub-hud-energy,
  #stage-select .play-hub-hud-stars,
  #stage-select .play-hub-hud-relics {
    padding: 6px 13px;
    gap: 7px;
    font-size: 14px;
    font-weight: 700;
    white-space: nowrap;
    background: linear-gradient(180deg, #ecdcb6 0%, #d8c191 100%);
    border: 1.5px solid #8a6a3a;
    border-radius: 11px;
    color: #4a3722;
    box-shadow: 0 2px 6px rgba(40, 22, 8, 0.4), inset 0 1px 0 rgba(255, 250, 235, 0.45), inset 0 0 0 1px rgba(90, 60, 25, 0.32);
  }
  #stage-select .play-hub-hud-relics:hover {
    background: linear-gradient(180deg, #f3e6c5 0%, #e0caa0 100%);
  }
  /* Gilded star + faceted relic gem read vividly against the parchment. */
  #stage-select .play-hub-hud-stars-icon {
    font-size: 15px;
    color: #d99a1c;
    text-shadow: 0 0 4px rgba(245, 197, 24, 0.6);
  }
  #stage-select .play-hub-hud-relics-icon {
    width: 17px; height: 18px;
    background: url('../assets/shop/relic-gem.webp') center / contain no-repeat;
    color: transparent; font-size: 0;
    filter: drop-shadow(0 0 3px rgba(80, 170, 255, 0.4));
  }
  /* Daily-bin gift — parchment disc to match, keeping its gold gift + badge. */
  #stage-select .play-hub-hud-bin {
    background: linear-gradient(180deg, #ecdcb6 0%, #d8c191 100%);
    border: 1.5px solid #8a6a3a;
    color: #b5781b;
    box-shadow: 0 2px 6px rgba(40, 22, 8, 0.4), inset 0 1px 0 rgba(255, 250, 235, 0.4), inset 0 0 0 1px rgba(90, 60, 25, 0.3);
  }
  #stage-select .play-hub-hud-bin:hover {
    background: linear-gradient(180deg, #f3e6c5 0%, #e0caa0 100%);
    box-shadow: 0 0 10px rgba(180, 120, 30, 0.4);
  }
  /* Mobile HUD slimming — text 10%+ smaller and pills/gaps tightened so the
     full row (avatar · level … energy · stars · relics · daily · settings)
     fits a 360px Samsung-class portrait without clipping the right-hand
     buttons. This rule (id-scoped) is the one that actually controls the pill
     text size on the play-hub screen. */
  body.touch:not(.tablet) #stage-select .play-hub-hud-level,
  body.touch:not(.tablet) #stage-select .play-hub-hud-energy,
  body.touch:not(.tablet) #stage-select .play-hub-hud-stars,
  body.touch:not(.tablet) #stage-select .play-hub-hud-relics {
    padding: 3px 8px;
    font-size: 11.5px;
  }
  body.touch:not(.tablet) #stage-select .play-hub-hud-stars { gap: 3px; }
  body.touch:not(.tablet) #stage-select .play-hub-hud-relics { gap: 5px; }
  body.touch:not(.tablet) #stage-select .play-hub-hud-stars-icon { font-size: 10.5px; }
  body.touch:not(.tablet) #stage-select .play-hub-hud-relics-icon { font-size: 11px; }
  /* Daily-bin gift disc shrinks in step with the avatar/settings buttons. */
  body.touch:not(.tablet) #stage-select .play-hub-hud-bin {
    width: 30px; height: 30px;
    font-size: 16px;
    margin: 0 1px;
  }
  /* Emphasize the active bottom-nav tab — dim the inactive ones (kept on the
     shop + gear mocks; other tabs' nav is unchanged). */
  #stage-select[data-active-tab="shop"] .play-hub-tab:not(.is-active),
  #stage-select[data-active-tab="equipment"] .play-hub-tab:not(.is-active) {
    opacity: 0.55;
  }
  #shop-tab-body {
    flex: 1;
    overflow-y: auto;
    will-change: scroll-position;
    contain: paint;
  }
  /* Header sits on the top-left page of the book. The content column is
     width-locked + centred so it lands on the two parchment pages at the
     ~1440 design width and scales down gracefully. */
  #relic-shop-section-head {
    width: min(1180px, 95%);
    margin: 0 auto;
    padding: 60px 0 2px 46px;
    box-sizing: border-box;
  }
  #relic-shop-section-title {
    font-family: 'EB Garamond', 'Cinzel', serif;
    font-size: 25px; font-weight: 700; letter-spacing: 0.5px;
    color: #2e1d10;
    text-shadow: 0 1px 0 rgba(255, 248, 230, 0.35);
  }
  #relic-shop-section-subtitle {
    font-size: 13px; color: #6f5c47;
    margin-top: 3px; line-height: 1.4;
  }
  /* 3 cards · spine gutter · 3 cards — matches the open-book layout and the
     META_UPGRADES order (left page: vitality/potency/scholar + foresight/
     veteran; right page: reflex/swiftness/magnetism + headstart/surge). */
  #relic-shop-grid {
    width: min(1180px, 95%);
    margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(3, 1fr) 10% repeat(3, 1fr);
    grid-auto-rows: auto;
    gap: 22px 10px;
    padding: 10px 54px 16px;
    box-sizing: border-box;
    justify-items: center;
    align-items: start;
  }
  #relic-shop-grid .relic-card:nth-child(1) { grid-column: 1; grid-row: 1; }
  #relic-shop-grid .relic-card:nth-child(2) { grid-column: 2; grid-row: 1; }
  #relic-shop-grid .relic-card:nth-child(3) { grid-column: 3; grid-row: 1; }
  #relic-shop-grid .relic-card:nth-child(4) { grid-column: 5; grid-row: 1; }
  #relic-shop-grid .relic-card:nth-child(5) { grid-column: 6; grid-row: 1; }
  #relic-shop-grid .relic-card:nth-child(6) { grid-column: 7; grid-row: 1; }
  #relic-shop-grid .relic-card:nth-child(7) { grid-column: 1; grid-row: 2; }
  #relic-shop-grid .relic-card:nth-child(8) { grid-column: 2; grid-row: 2; }
  #relic-shop-grid .relic-card:nth-child(9) { grid-column: 5; grid-row: 2; }
  #relic-shop-grid .relic-card:nth-child(10) { grid-column: 6; grid-row: 2; }

  /* PORTRAIT only — drop the two-page conceit, fall back to a simple
     parchment-card grid that still reads as the book theme. Landscape (incl.
     mobile landscape) always keeps the desktop open-book 3+2 layout. */
  @media (max-width: 820px) and (orientation: portrait) {
    #relic-shop-section-head { width: 100%; padding: 50px 20px 4px; }
    #relic-shop-section-title { font-size: 24px; }
    #relic-shop-section-subtitle { font-size: 12px; }
    #relic-shop-grid {
      width: 100%;
      grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
      gap: 10px 8px;
      padding: 10px 14px 18px;
    }
    /* cancel the desktop two-page placement */
    #relic-shop-grid .relic-card:nth-child(n) { grid-column: auto; grid-row: auto; }
    .relic-card { max-width: none; min-height: 210px; }
    .relic-card-icon { width: 60px; height: 60px; }
    .relic-card-name { font-size: 16px; }
    .relic-card-desc { font-size: 11px; max-width: 120px; }
  }
  /* Landscape PHONES — keep the desktop open-book 3+2 layout (per design
     intent) but scale the cards down so all 10 fit a short landscape
     viewport (iPhone 13 = 844×390, S20 Ultra = ~915×412) without cramming
     or overflowing under the bottom tab bar. Tall landscape (desktop) is
     unaffected.

     SPECIFICITY NOTE: the card overrides are scoped with `#relic-shop-grid`
     (id + class = 0,1,1) on purpose — the base `.relic-card` rule (0,1,0) is
     defined LATER in this file, so a bare `.relic-card` override here would
     lose on source order and silently do nothing (the bug this replaces:
     cards stayed at the desktop 242px min-height and row 2 fell off-screen).

     SIZING: heights are clamped against vh so the two rows + header + tab bar
     fit the actual short viewport. The content area is ~314px tall at 390px
     (the 61px tab bar sits below it), so each of the 2 card rows gets ~140px;
     the clamps target that and scale up gracefully toward 540px landscape. */
  /* All selectors are scoped under `#stage-select` (the play-hub modal root, an
     id) so they reliably beat the landscape shop overrides in css/responsive.css
     — that file loads AFTER wiki.css and its single-id rules would otherwise win
     on source order, leaving the cards at the desktop 242px height. The
     `max-height: 720px` range matches responsive.css's landscape breakpoint so
     the open-book layout is preserved across the whole landscape-mobile range
     (instead of responsive.css reflowing the grid to auto-fill). */
  @media (orientation: landscape) and (max-height: 720px) {
    /* Seat the title ON the left parchment page: enough top padding to clear
       the book's top wood frame and enough left padding to clear the left
       frame (otherwise the dark-brown "U" lands on the dark frame corner and
       reads as invisible — it's a contrast issue, not clipping). A light halo
       keeps it legible wherever the parchment texture darkens. */
    #stage-select #relic-shop-section-head { width: min(1180px, 97%); padding: clamp(13px, 3.6vh, 24px) 0 2px clamp(40px, 6vw, 60px); }
    #stage-select #relic-shop-section-title {
      font-size: clamp(15px, 4.3vh, 23px);
      text-shadow: 0 0 5px rgba(250, 243, 226, 0.9), 0 1px 1px rgba(255, 250, 238, 0.7);
    }
    /* Reclaim a line for the short viewport — the title alone carries it. */
    #stage-select #relic-shop-section-subtitle { display: none; }
    #stage-select #relic-shop-grid {
      width: min(1180px, 97%);
      padding: 2px 16px 6px;
      gap: clamp(3px, 1.2vh, 10px) 6px;
      /* Slightly narrower spine so 6 shrunk cards + gutter never exceed the
         centred grid width at 844px wide (was clipping the 6th card). Re-assert
         the open-book 3 + spine + 3 template so it wins over responsive.css's
         auto-fill reflow. */
      grid-template-columns: repeat(3, 1fr) 7% repeat(3, 1fr);
    }
    #stage-select #relic-shop-grid .relic-card {
      max-width: clamp(90px, 12.5vw, 150px);
      min-height: clamp(106px, 29vh, 200px);
      padding: clamp(6px, 1.6vh, 16px) 8px clamp(5px, 1.3vh, 12px);
      gap: 0;
      filter: drop-shadow(0 3px 6px rgba(35, 18, 6, 0.3));
    }
    #stage-select #relic-shop-grid .relic-card:hover { transform: none; }
    #stage-select #relic-shop-grid .relic-card-icon {
      width: clamp(32px, 8.4vh, 58px); height: clamp(32px, 8.4vh, 58px); margin-top: 1px;
    }
    #stage-select #relic-shop-grid .relic-card-name { font-size: clamp(11px, 3vh, 16px); margin-top: 1px; }
    #stage-select #relic-shop-grid .relic-card-desc { font-size: clamp(8px, 1.9vh, 10.5px); max-width: 110px; line-height: 1.22; }
    #stage-select #relic-shop-grid .relic-card-tier { font-size: clamp(9px, 2.2vh, 11px); margin-top: 0; }
    #stage-select #relic-shop-grid .relic-card-buy { width: clamp(40px, 10.5vh, 66px); height: clamp(36px, 9.5vh, 60px); }
    #stage-select #relic-shop-grid .relic-card-cost { font-size: clamp(10px, 2.6vh, 13px); }
  }
  /* Very short landscape (phones ≤ 430px tall, e.g. iPhone 13 = 390, S20 Ultra
     = 412): hide the per-card description — two full rows only fit when the
     desc line is gone. Above this the desc returns (roomier landscape tablets). */
  @media (orientation: landscape) and (max-height: 430px) {
    #stage-select #relic-shop-grid .relic-card-desc { display: none; }
    /* With the description gone, cluster icon/name/tier/seal in the vertical
       centre (small even gaps) instead of pinning the seal to the bottom —
       otherwise margin-top:auto leaves a dead gap mid-card and the card reads
       as sparse. */
    #stage-select #relic-shop-grid .relic-card { justify-content: center; gap: clamp(2px, 1vh, 5px); }
    #stage-select #relic-shop-grid .relic-card-buy { margin-top: clamp(2px, 1vh, 6px); }
  }
  /* Limit break section — sits below the upgrade grid. Visual separator
     already comes from the two section headers; a thin top border
     reinforces the grouping. */
  #limit-break-section {
    padding: 4px 22px 22px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    margin-top: 14px;
  }
  #limit-break-section-head {
    padding: 14px 0 10px;
  }
  #limit-break-section-title {
    font-family: 'Space Grotesk', sans-serif;
    font-size: 14px; font-weight: 700; letter-spacing: 2px;
    color: #ffd166;
    text-shadow: 0 0 8px rgba(255, 215, 102, 0.35);
  }
  #limit-break-section-subtitle {
    font-size: 11.5px; color: var(--text-dim);
    margin-top: 3px;
  }
  #limit-break-grid {
    display: grid; gap: 12px;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  }
  .limit-card.owned {
    border-color: rgba(255, 215, 102, 0.25);
  }
  .limit-card.equipped {
    border-color: rgba(255, 215, 102, 0.75);
    box-shadow: 0 0 20px rgba(255, 180, 80, 0.18), 0 0 4px rgba(255, 215, 102, 0.4) inset;
  }
  /* ── Relic card — ornate parchment card on the book page ─────────── */
  .relic-card {
    position: relative;
    width: 100%; max-width: 162px;
    min-height: 242px;
    /* Painted parchment frame (scrollwork corners + double border). The
       whole art stretches to the card box; cards are near the art's native
       163:230 ratio so distortion is negligible. */
    background: url('../assets/shop/shop-card-frame.webp') center / 100% 100% no-repeat;
    border: none; border-radius: 0;
    padding: 18px 14px 14px;
    display: flex; flex-direction: column; align-items: center;
    gap: 2px;
    text-align: center;
    box-sizing: border-box;
    transition: transform 0.16s;
    cursor: default;
    filter: drop-shadow(0 4px 9px rgba(35, 18, 6, 0.32));
  }
  .relic-card:hover { transform: translateY(-3px); }
  .relic-card-head {
    display: flex; align-items: center; justify-content: center;
  }
  .relic-card-icon {
    width: 58px; height: 58px; flex-shrink: 0;
    margin-top: 3px;
    display: flex; align-items: center; justify-content: center;
    font-size: 30px; line-height: 1;
    /* Calm the raster medallions' baked gloss toward the target's matter
       bronze look. */
    filter: drop-shadow(0 3px 6px rgba(60, 30, 10, 0.32)) brightness(0.96) saturate(0.92);
  }
  .relic-card-icon svg { width: 100%; height: 100%; display: block; }
  /* Painted medallion icons (rc331) — the Figma-matched raster medallions
     replace the inline SVG bezel icons in the shop. The SVG stays in the
     DOM (fallback) but is hidden once a medallion background is present. */
  .relic-card[data-upgrade] .relic-card-icon {
    background: center center / contain no-repeat;
  }
  .relic-card[data-upgrade] .relic-card-icon svg { display: none; }
  .relic-card[data-upgrade="vitality"]  .relic-card-icon { background-image: url('../assets/shop/relic-vitality.webp'); }
  .relic-card[data-upgrade="potency"]   .relic-card-icon { background-image: url('../assets/shop/relic-potency.webp'); }
  .relic-card[data-upgrade="scholar"]   .relic-card-icon { background-image: url('../assets/shop/relic-scholar.webp'); }
  .relic-card[data-upgrade="reflex"]    .relic-card-icon { background-image: url('../assets/shop/relic-reflex.webp'); }
  .relic-card[data-upgrade="swiftness"] .relic-card-icon { background-image: url('../assets/shop/relic-swiftness.webp'); }
  .relic-card[data-upgrade="magnetism"] .relic-card-icon { background-image: url('../assets/shop/relic-magnetism.webp'); }
  .relic-card[data-upgrade="foresight"] .relic-card-icon { background-image: url('../assets/shop/relic-foresight.webp'); }
  .relic-card[data-upgrade="veteran"]   .relic-card-icon { background-image: url('../assets/shop/relic-veteran.webp'); }
  .relic-card[data-upgrade="headstart"] .relic-card-icon { background-image: url('../assets/shop/relic-headstart.webp'); }
  .relic-card[data-upgrade="surge"]     .relic-card-icon { background-image: url('../assets/shop/relic-surge.webp'); }
  .relic-card-name {
    /* Sans (Outfit) — the design's card names are Inter, not the serif
       header face. Upright, medium weight, warm brown. */
    font-family: inherit;
    font-weight: 500; font-size: 16px; letter-spacing: 0.2px;
    color: #5e4e40;
    margin-top: 3px; line-height: 1.1;
  }
  .relic-card-desc {
    font-size: 10.5px; color: #6a5b4d; line-height: 1.28;
    text-align: center;
    max-width: 118px;
  }
  .relic-card-tier {
    display: flex; align-items: center; justify-content: center;
    font-size: 11px; color: #7a6857; letter-spacing: 0.3px;
    font-weight: 400;
    margin-top: 1px;
  }
  .relic-card-bar { display: none; }
  .relic-card-bar-fill { display: none; }

  /* Wax-seal cost badge — painted red-wax stamp with the blue relic gem
     baked into the art (centre-left). We overlay only the cost number to
     the right of the gem so the badge reads "<gem> 350" like the design. */
  .relic-card-buy {
    margin-top: auto;
    align-self: center;
    position: relative;
    padding: 0;
    width: 66px; height: 60px;
    background: url('../assets/shop/shop-wax-seal.webp?v=20260601a') center / contain no-repeat;
    border: none; border-radius: 0; box-shadow: none;
    cursor: pointer;
    transition: transform 0.14s, filter 0.14s;
    filter: drop-shadow(0 3px 6px rgba(30, 12, 4, 0.42));
  }
  .relic-card-buy:hover:not([disabled]):not(.unaffordable) {
    transform: scale(1.07);
    filter: drop-shadow(0 5px 9px rgba(30, 12, 4, 0.52));
  }
  .relic-card-buy[disabled] { cursor: default; }
  .relic-card-buy.unaffordable { opacity: 0.55; cursor: pointer; }
  .relic-card-buy.unaffordable:hover { opacity: 0.72; }
  .relic-card-buy.unaffordable .relic-card-cost { color: #e3c4a2; }
  /* Maxed — green-tint the wax and centre the ✓. */
  .relic-card.maxed .relic-card-buy {
    filter: drop-shadow(0 3px 6px rgba(10, 30, 4, 0.4)) sepia(1) hue-rotate(58deg) saturate(1.5) brightness(0.95);
  }
  /* Star stacked ABOVE the cost number, both centered, so the pair sits inside
     the round wax-seal face instead of running edge-to-edge on one line (which
     made multi-digit costs bleed past the seal's rim). */
  .relic-card-cost {
    position: absolute;
    left: 50%; top: 47%;
    transform: translate(-50%, -50%);
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    line-height: 1;
    font-family: inherit;
    font-weight: 700; font-size: 13px;
    color: #e6b48f;
    text-shadow: 0 1px 1.5px rgba(40, 12, 4, 0.75);
    white-space: nowrap;
    pointer-events: none;
  }
  .relic-card-cost .relic-cost-star { font-size: 0.82em; line-height: 1; margin-bottom: 1px; }
  .relic-card-cost .relic-cost-num  { font-size: 1em; line-height: 1; }
  .relic-card.maxed .relic-card-cost { left: 50%; font-size: 18px; }
  /* The blue gem is painted into the seal art — hide the inline SVG gem. */
  .relic-card-buy .relic-gem { display: none; }

  /* Stage picker — 5 cards on the landing screen. Cards wrap to two rows on
     narrow viewports. Selected card pulses the gradient border; locked
     cards dim their contents and show a padlock + the gate-wave hint. */
  /* rc162 — Play Hub is a full-screen committed state (no popup chrome
     on any viewport, no close-to-landing affordances). The legacy
     windowed-modal look + scale-in animation are gone; backdrop and
     panel now use a 200ms opacity cross-fade. The mobile-specific
     `body.touch:not(.tablet)` override that previously provided this
     full-screen treatment now only handles content-layout differences. */
  #stage-select {
    position: fixed; inset: 0;
    display: none; align-items: center; justify-content: center;
    z-index: 250;
    background: var(--panel-bg-2, #0e1426);
    opacity: 0;
    transition: opacity 0.20s ease;
  }
  #stage-select.visible {
    display: flex;
    opacity: 1;
  }
  #stage-select-panel {
    position: relative;  /* anchor for .play-hub-bottom-nav */
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    max-height: 100vh;
    max-height: 100dvh;
    display: flex; flex-direction: column;
    background: var(--panel-bg, #131a2e);
    border: none;
    border-radius: 0;
    overflow: hidden;
  }
  /* World-map tab no longer needs a height cap — the panel is already
     full-viewport. Keep the rule as a no-op for future tab-specific
     layout tweaks. */
  #stage-select-panel:has(.world-map) {
    height: 100vh;
    height: 100dvh;
  }
  #stage-select-header {
    display: flex; align-items: center; gap: 18px;
    padding: calc(20px + env(safe-area-inset-top, 0px)) 24px 20px;
    border-bottom: 1px solid #2a3454;
  }
  #stage-select-title { font-size: 22px; font-weight: 800; letter-spacing: 1.4px; color: var(--text); }
  #stage-select-subtitle { font-size: 12px; color: var(--text-dim); margin-top: 3px; }

  /* ── Play Hub tab bar ──────────────────────────────────────────────
     Sits at the top of #stage-select-panel above every tab panel.
     5 tab buttons + a close X on the right. The 3rd + 4th tabs
     (events, trophies) are locked at v1 and rendered with a 🔒 affix
     + reduced opacity. */
  /* Floating bottom nav. Anchored to the bottom edge of the modal as
     a rounded pill bar with light elevation. iOS safe-area-inset-bottom
     respect via the wrapping margin so the home indicator doesn't
     clip the bar. 5 equal-width tabs (icon stacked above label).
     Locked tabs (EVENTS/TROPHIES at v1) dim + disable pointer events. */
  .play-hub-bottom-nav {
    position: absolute;
    bottom: max(14px, env(safe-area-inset-bottom, 0px));
    left: 50%;
    transform: translateX(-50%);
    width: calc(100% - 28px);
    max-width: 520px;
    display: flex; align-items: stretch; gap: 4px;
    padding: 6px;
    /* rc159 — opaque bottom nav. */
    background: #1f223a;
    border: 1px solid #ffd766;
    border-radius: 18px;
    box-shadow:
      0 12px 30px rgba(0, 0, 0, 0.55),
      0 0 0 1px #ffd766 inset;
    z-index: 6;
  }
  /* #1 — in portrait the 520px cap left the 5 tabs cramped in the middle of a
     wide-but-narrow phone; let the bar span nearly the full width (≈8px each
     side) so the tabs breathe. Landscape/desktop keep the centred 520px pill. */
  body.portrait .play-hub-bottom-nav {
    width: calc(100% - 16px);
    max-width: none;
  }

  /* ── Play Hub HUD (rc164) ──────────────────────────────────────────
     Thin top bar inside #stage-select-panel, sits above every tab.
     Left: avatar button (32px circle). Right: relic count pill +
     settings cog. The relic pill is button-styled so it reads as
     tappable (it switches to the SHOP tab on click). */
  .play-hub-hud {
    display: flex; align-items: center; gap: 10px;
    padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 10px;
    border-bottom: none;
    background: linear-gradient(to bottom, var(--panel-bg, #131a2e) 0%, transparent 100%);
    position: absolute;
    top: 0; left: 0; right: 0;
    z-index: 4;
  }
  .play-hub-hud-spacer { flex: 1 1 auto; }
  .play-hub-hud-avatar,
  .play-hub-hud-settings {
    width: 36px; height: 36px;
    border-radius: 50%;
    background: #2a3454;
    border: 1px solid #4a5478;
    color: var(--text);
    font-family: inherit;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0;
    overflow: hidden;
    transition: background 0.15s, border-color 0.15s, transform 0.12s;
  }
  .play-hub-hud-avatar:hover,
  .play-hub-hud-settings:hover {
    background: #3a4470;
    border-color: #6a7498;
    transform: translateY(-1px);
  }
  .play-hub-hud-avatar img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
  }
  .play-hub-hud-avatar-initial {
    font-weight: 800;
    font-size: 16px;
    color: var(--text);
    letter-spacing: 0;
  }
  .play-hub-hud-relics {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 6px 14px;
    border-radius: var(--radius-pill, 999px);
    background: #243a55;
    border: 1px solid #7ee2ff;
    color: #d8f4ff;
    font: inherit;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
    transition: background 0.15s, transform 0.12s;
  }
  .play-hub-hud-relics:hover {
    background: #2e4a6e;
    transform: translateY(-1px);
  }
  .play-hub-hud-relics-icon {
    color: #7ee2ff;
    font-size: 13px;
  }
  .play-hub-hud-relics-count {
    font-variant-numeric: tabular-nums;
  }
  .play-hub-hud-level {
    font-size: 13px; font-weight: 700; letter-spacing: 0.5px;
    color: #ffd766;
    padding: 4px 10px;
    border-radius: var(--radius-pill, 999px);
    background: rgba(255, 215, 102, 0.12);
    border: 1px solid rgba(255, 215, 102, 0.35);
  }
  .play-hub-hud-energy {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 4px 10px;
    border-radius: var(--radius-pill, 999px);
    background: rgba(100, 220, 100, 0.12);
    border: 1px solid rgba(100, 220, 100, 0.35);
    color: #88ff88;
    font-size: 13px; font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .play-hub-hud-stars {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 4px 10px;
    border-radius: var(--radius-pill, 999px);
    background: rgba(255, 204, 51, 0.12);
    border: 1px solid rgba(255, 204, 51, 0.35);
    color: #ffcc33;
    font-size: 13px; font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .play-hub-hud-stars-icon { font-size: 12px; }
  .play-hub-hud-stars-count { font-variant-numeric: tabular-nums; }
  /* Total Power — the headline combat rating; styled hotter/bolder than the
     other pills so it reads as the player's overall strength at a glance. */
  .play-hub-hud-power {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 4px 11px;
    border-radius: var(--radius-pill, 999px);
    background: linear-gradient(180deg, rgba(255, 90, 70, 0.22), rgba(255, 60, 40, 0.10));
    border: 1px solid rgba(255, 120, 90, 0.55);
    font-size: 14px; font-weight: 800; letter-spacing: 0.3px;
    box-shadow: inset 0 0 10px rgba(255, 80, 50, 0.18);
  }
  .play-hub-hud-power-icon { color: #ff6a4a; font-size: 13px; }
  .play-hub-hud-power-count { color: #ffd0bf; font-variant-numeric: tabular-nums; }
  /* Touch tightening — slightly smaller bar + buttons so the HUD
     doesn't eat too much vertical budget on phone-portrait. */
  body.touch:not(.tablet) .play-hub-hud {
    padding: calc(8px + env(safe-area-inset-top, 0px)) 10px 8px;
    gap: 6px;
  }
  body.touch:not(.tablet) .play-hub-hud-avatar,
  body.touch:not(.tablet) .play-hub-hud-settings {
    width: 29px; height: 29px;
    font-size: 14px;
  }
  body.touch:not(.tablet) .play-hub-hud-relics {
    padding: 3px 8px;
    font-size: 11.5px;
  }
  body.touch:not(.tablet) .play-hub-hud-power {
    padding: 3px 8px;
    font-size: 12px;
  }
  .play-hub-bottom-nav .play-hub-tab {
    flex: 1 1 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px;
    padding: 9px 4px 8px;
    background: transparent;
    color: var(--text-dim);
    border: none;
    border-radius: 12px;
    font: inherit; font-size: 9.5px; font-weight: 800;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    cursor: pointer;
    transition: color 0.15s, background 0.15s, transform 0.12s;
    min-width: 0;
    text-align: center;
    line-height: 1.1;
    /* Locked/long labels (e.g. "EQUIPMENT") should ellipsize rather
       than overflow the equal-width slot. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .play-hub-bottom-nav .play-hub-tab-icon {
    font-size: 18px;
    line-height: 1;
    opacity: 0.85;
    transition: opacity 0.15s, transform 0.15s;
  }
  .play-hub-bottom-nav .play-hub-tab-label {
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .play-hub-bottom-nav .play-hub-tab:hover:not(:disabled) {
    color: var(--text);
    background: rgba(255, 215, 110, 0.06);
  }
  .play-hub-bottom-nav .play-hub-tab.is-active {
    color: #1a0f04;
    background: linear-gradient(180deg, #ffe28e 0%, #ffb454 60%, #d99528 100%);
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.55),
      0 4px 12px rgba(217, 149, 40, 0.45);
    transform: translateY(-2px);
  }
  .play-hub-bottom-nav .play-hub-tab.is-active .play-hub-tab-icon {
    opacity: 1;
    transform: scale(1.05);
  }
  .play-hub-bottom-nav .play-hub-tab.is-locked,
  .play-hub-bottom-nav .play-hub-tab:disabled {
    opacity: 0.50;
    cursor: not-allowed;
  }
  .play-hub-bottom-nav .play-hub-tab.is-locked:hover {
    background: transparent;
    color: var(--text-dim);
  }

  /* Hub close X — anchored top-right of the modal, floating above
     tab content. Sits on a small dark pill so it's tappable against
     varied tab backgrounds (stage cards, equipment grid, etc.). */
  .play-hub-close {
    position: absolute;
    top: max(12px, env(safe-area-inset-top, 0px));
    right: max(12px, env(safe-area-inset-right, 0px));
    width: 36px; height: 36px;
    display: flex; align-items: center; justify-content: center;
    background: rgba(10, 12, 28, 0.72);
    color: var(--text);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 50%;
    font-size: 20px; line-height: 1;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.15s, transform 0.15s, border-color 0.15s;
    z-index: 7;
  }
  .play-hub-close:hover {
    background: rgba(255, 255, 255, 0.10);
    border-color: rgba(255, 255, 255, 0.28);
    transform: scale(1.05);
  }

  /* Tab panels — each tab's content area inside #stage-select-panel.
     The STAGES panel naturally takes whatever height its contents
     need; the placeholder tabs use the centered empty state below.
     padding-bottom leaves clearance for the floating bottom nav. */
  .play-hub-panel {
    display: flex; flex-direction: column;
    min-height: 0;
    flex: 1 1 auto;
    overflow: hidden;
    /* Reserve space for the floating bottom nav so scrollable content
       inside the panel can scroll past the last item without it being
       hidden under the bar. The bar itself uses position:absolute so
       it doesn't displace the panel — this padding is a manual offset. */
    padding-bottom: 76px;
  }
  .play-hub-panel[hidden] { display: none; }

  /* Empty state used by placeholder tabs (EQUIPMENT/SHOP at v1 +
     EVENTS/TROPHIES indefinitely). Centered title + body + optional
     padlock icon for locked tabs. */
  .play-hub-empty {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 10px;
    padding: 60px 24px;
    min-height: 280px;
    color: var(--text-dim);
    text-align: center;
  }
  .play-hub-empty-icon {
    font-size: 40px;
    opacity: 0.55;
    margin-bottom: 2px;
  }
  .play-hub-empty-title {
    color: var(--text);
    font-size: 16px; font-weight: 800;
    letter-spacing: 1.4px;
  }
  .play-hub-empty-body {
    font-size: 13px; line-height: 1.5;
    max-width: 360px;
    color: var(--text-dim);
  }
  .play-hub-empty.is-locked { opacity: 0.78; }

  /* rc163 — the SHOP tab's old "BROWSE PACKS" launcher card was
     retired when the relic-shop modal was absorbed into the SHOP tab.
     The tab now renders the upgrade grid directly via renderRelicUi().
     The .play-hub-shop-* styles are gone — see #shop-tab-header /
     #shop-tab-body / #relic-shop-grid for the replacement layout. */


  /* First-run tutorial modal. Shown once — between PLAY and stage-select —
     for players with no prior bestRun. Layout is a compact panel with four
     icon+hint rows and one CTA. Desktop and touch copy both live in the DOM;
     body.touch:not(.tablet) flips which hint is visible so we don't need JS to pick. */
  #tutorial {
    position: fixed; inset: 0;
    display: none; align-items: center; justify-content: center;
    z-index: 260;
    background: rgba(6, 8, 18, 0.86);
  }
  #tutorial.visible { display: flex; }
  #tutorial.visible #tutorial-panel {
    animation: tutorialPanelIn 0.42s cubic-bezier(0.34, 1.4, 0.64, 1) both;
  }
  @keyframes tutorialPanelIn {
    0%   { transform: scale(0.9) translateY(14px); opacity: 0; }
    100% { transform: scale(1) translateY(0);      opacity: 1; }
  }
  #tutorial-panel {
    width: min(520px, 92vw);
    max-height: 92vh;
    padding: 26px 26px 22px;
    background: linear-gradient(160deg, rgba(30, 36, 60, 0.96), rgba(22, 18, 40, 0.96));
    border: 1px solid rgba(184, 155, 255, 0.3);
    border-radius: 18px;
    box-shadow: 0 26px 70px rgba(0, 0, 0, 0.6),
                0 0 100px rgba(184, 155, 255, 0.08);
    overflow-y: auto;
  }
  #tutorial-title {
    font-size: 22px; font-weight: 800; letter-spacing: 1.4px;
    color: var(--text);
    text-align: center;
    margin-bottom: 20px;
  }
  #tutorial-body {
    display: flex; flex-direction: column; gap: 14px;
    margin-bottom: 22px;
  }
  #tutorial .tu-row {
    display: flex; align-items: flex-start; gap: 14px;
    padding: 12px 14px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 12px;
  }
  #tutorial .tu-icon {
    flex: 0 0 auto;
    width: 38px; height: 38px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--lilac);
    background: rgba(184, 155, 255, 0.1);
    border: 1px solid rgba(184, 155, 255, 0.25);
    border-radius: 10px;
  }
  #tutorial .tu-text { flex: 1 1 auto; min-width: 0; }
  #tutorial .tu-name {
    font-size: 13px; font-weight: 700;
    color: var(--text);
    letter-spacing: 0.6px;
    text-transform: uppercase;
    margin-bottom: 4px;
  }
  #tutorial .tu-hint {
    font-size: 13px; line-height: 1.45;
    color: var(--text-dim);
  }
  #tutorial .tu-hint b {
    color: var(--text);
    background: rgba(184, 155, 255, 0.14);
    padding: 1px 6px;
    border-radius: 4px;
    font-weight: 700;
  }
  /* Show the desktop copy by default; body.touch:not(.tablet) flips to the touch copy. */
  #tutorial .tu-touch { display: none; }
  body.touch:not(.tablet) #tutorial .tu-desktop { display: none; }
  body.touch:not(.tablet) #tutorial .tu-touch   { display: block; }

  #tutorial-got-it {
    display: block;
    width: 100%;
    padding: 14px 0;
    font-family: inherit; font-size: 15px; font-weight: 800;
    letter-spacing: 1.4px;
    color: #0b0e17;
    background: linear-gradient(135deg, #7ee2ff 0%, #b89bff 100%);
    border: 1.5px solid rgba(255, 255, 255, 0.35);
    border-radius: 12px;
    cursor: pointer;
    box-shadow: 0 10px 28px rgba(126, 226, 255, 0.45);
    transition: transform 0.15s ease, box-shadow 0.2s ease, filter 0.15s ease;
  }
  #tutorial-got-it:hover,
  #tutorial-got-it:focus-visible {
    transform: translateY(-1px);
    box-shadow: 0 14px 32px rgba(126, 226, 255, 0.6), 0 0 24px rgba(184, 155, 255, 0.6);
    outline: none;
  }
  #tutorial-got-it:active { transform: translateY(0) scale(0.99); }
  #stage-select-grid {
    display: grid; gap: 8px;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    padding: 22px 24px 26px;
    overflow-y: auto;
  }
  /* Hero card — desktop: media (image + lock/CTA) on top, footer (name + diff)
     below. Unlocked images render at full colour; locked images are greyscaled
     so the lock affordance reads clearly without an extra dark gradient.
     Mobile (≤520px): card flips to a compact horizontal row with a small
     thumbnail on the left and text + CTA on the right. */
  .stage-hero-card {
    position: relative;
    display: block;
    border-radius: 14px;
    overflow: hidden;
    cursor: pointer;
    background: #111622;
    border: 1px solid rgba(255, 255, 255, 0.08);
    padding: 0;
    font-family: inherit;
    color: var(--text);
    transition: transform 0.2s cubic-bezier(0.2, 0.8, 0.2, 1),
                border-color 0.2s, box-shadow 0.25s;
    -webkit-tap-highlight-color: transparent;
    /* Staggered entrance — --stage-card-i is set per-card in JS. */
    opacity: 0;
    animation: stageCardIn 0.42s cubic-bezier(0.34, 1.4, 0.64, 1) forwards;
    animation-delay: calc(180ms + var(--stage-card-i, 0) * 55ms);
  }
  @keyframes stageCardIn {
    0%   { opacity: 0; transform: translateY(18px) scale(0.96); }
    100% { opacity: 1; transform: translateY(0)     scale(1);    }
  }
  /* Newest-unlock border — set by renderStageSelect on the highest unlocked
     card. Layered effect: ::before is a blurred conic-gradient aura behind
     the card (big halo), and the card itself paints a sharp conic-gradient
     border via padding-box/border-box compositing. Both gradients share the
     same --stage-card-angle and spin together so the "moving light" reads
     as one continuous sweep with a soft bleed. Browsers without @property
     support still see the same gradient, just static. */
  @property --stage-card-angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
  }
  .stage-hero-card.is-newest {
    --stage-card-angle: 0deg;
    border: 3px solid transparent;
    background:
      linear-gradient(#111622, #111622) padding-box,
      conic-gradient(from var(--stage-card-angle),
        #7ee2ff 0deg,
        #ffd36e 90deg,
        #ff9ec4 180deg,
        #b89bff 270deg,
        #7ee2ff 360deg) border-box;
    /* Layered ambient glow. Can't use a blurred pseudo-element behind the
       card because the card sets overflow:hidden for its image clip — the
       pseudo would be cropped. Stacked box-shadows aren't clipped by
       overflow so they're the cheapest way to make the card feel lit. */
    box-shadow: 0 0 18px rgba(126, 226, 255, 0.55),
                0 0 36px rgba(255, 215, 110, 0.35),
                0 0 64px rgba(184, 155, 255, 0.28);
    animation: stageCardIn 0.42s cubic-bezier(0.34, 1.4, 0.64, 1) forwards,
               stageCardBorderSpin 3.6s linear infinite;
  }
  @keyframes stageCardBorderSpin {
    to { --stage-card-angle: 360deg; }
  }
  .stage-hero-card .stage-hero-badge {
    position: absolute; top: 10px; left: 10px;
    padding: 4px 10px;
    border-radius: var(--radius-pill);
    background: linear-gradient(135deg, #ffd36e, #7ee2ff);
    color: #1a1530;
    font-size: 10px; font-weight: 800; letter-spacing: 1.4px;
    box-shadow: 0 6px 18px rgba(126, 226, 255, 0.35);
    pointer-events: none;
  }
  .stage-hero-card .stage-hero-media {
    position: relative;
    aspect-ratio: 16 / 10;
    overflow: hidden;
  }
  .stage-hero-card .stage-hero-image {
    position: absolute; inset: 0;
    background-size: cover; background-position: center bottom;
    transition: transform 0.45s ease, filter 0.25s;
  }
  /* Name + difficulty overlay over the image — absolute-positioned against the
     card (not the media) so the dark scrim paints over the bottom of the
     thumbnail without any separate body block that could be clipped by the
     grid's row track. */
  .stage-hero-card .stage-hero-body {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 22px 16px 14px;
    display: flex; flex-direction: column; gap: 3px;
    text-align: left;
    background: linear-gradient(180deg,
      rgba(10, 8, 22, 0) 0%,
      rgba(10, 8, 22, 0.55) 45%,
      rgba(6, 4, 14, 0.92) 100%);
    pointer-events: none;
  }
  .stage-hero-card .stage-hero-name {
    font-size: 17px; font-weight: 800; letter-spacing: 0.6px; color: #ffffff;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    text-shadow: 0 2px 8px rgba(0, 0, 0, 0.65);
  }
  .stage-hero-card .stage-hero-diff {
    font-size: 10.5px; letter-spacing: 1px; color: var(--gold);
    font-weight: 700; text-transform: uppercase;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
  }
  .stage-hero-card.locked .stage-hero-diff {
    color: rgba(216, 208, 184, 0.75);
    text-transform: none; letter-spacing: 0.3px; font-weight: 600;
  }
  .stage-hero-card .stage-hero-cta {
    position: absolute; top: 12px; right: 12px;
    padding: 6px 13px;
    border-radius: var(--radius-pill);
    background: rgba(10, 14, 30, 0.78);
    border: 1px solid rgba(255, 215, 110, 0.55);
    color: var(--gold);
    font-size: 11px; font-weight: 800; letter-spacing: 1.2px;
    transition: background 0.15s, transform 0.15s;
  }
  .stage-hero-card:hover:not(.locked) { border-color: rgba(255, 215, 110, 0.55); box-shadow: 0 14px 34px rgba(255, 155, 196, 0.24); }
  /* Newest card keeps its transparent border on hover so the gradient ring
     stays visible; hover layers additional lift onto the ambient glow. */
  .stage-hero-card.is-newest:hover:not(.locked) {
    border-color: transparent;
    box-shadow: 0 14px 34px rgba(255, 155, 196, 0.28),
                0 0 24px rgba(126, 226, 255, 0.7),
                0 0 48px rgba(255, 215, 110, 0.45),
                0 0 72px rgba(184, 155, 255, 0.32);
  }
  .stage-hero-card:hover:not(.locked) .stage-hero-image { transform: scale(1.05); }
  .stage-hero-card:hover:not(.locked) .stage-hero-cta { background: linear-gradient(135deg, #ffd36e, #ff9ec4 55%, #b89bff); color: #1a1530; border-color: transparent; }
  .stage-hero-card.locked { cursor: not-allowed; }
  .stage-hero-card.locked .stage-hero-image { filter: grayscale(0.85) brightness(0.35); }
  .stage-hero-card.locked .stage-hero-cta { display: none; }
  .stage-hero-card .stage-hero-lock {
    position: absolute; inset: 0;
    display: none;
    align-items: center; justify-content: center;
    color: #d8d0b8;
  }
  .stage-hero-card.locked .stage-hero-lock { display: flex; }
  .stage-hero-card .stage-hero-lock svg {
    width: 30px; height: 30px; opacity: 0.85;
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.6));
  }
  /* Daily Run tile — surfaces only when ?dailies=1 is set. Uses the same
     stage-hero-card frame so it sits naturally in the grid, but adds a
     gradient sheen + DAILY badge in the prismatic palette so it reads as
     a separate tier from the regular stages. */
  .stage-hero-card.daily-card .stage-hero-name {
    color: #ffe9aa;
  }
  .stage-hero-card.daily-card .stage-hero-diff {
    color: var(--lilac);
    text-transform: uppercase;
  }
  .stage-hero-card.daily-card .stage-hero-countdown {
    margin-top: 4px;
    font-size: 10px; letter-spacing: 0.8px;
    color: rgba(216, 208, 184, 0.75);
    font-weight: 600;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .stage-hero-card .daily-badge {
    background: linear-gradient(135deg, #b89bff, #ff9ec4 55%, #ffd36e);
    color: #1a1030;
    box-shadow: 0 6px 18px rgba(184, 155, 255, 0.45);
  }
  .stage-hero-card.daily-card .stage-hero-image::after {
    content: '';
    position: absolute; inset: 0;
    background:
      linear-gradient(120deg,
        rgba(184, 155, 255, 0.18) 0%,
        rgba(126, 226, 255, 0.10) 40%,
        rgba(255, 215, 110, 0.18) 100%);
    mix-blend-mode: screen;
    pointer-events: none;
  }

  /* "Coming soon" placeholder cards — same shape as a locked stage card but
     with a subtle animated aurora instead of a stage thumbnail so it reads as
     a teaser rather than a grayed-out stage the player could have unlocked. */
  .stage-hero-card.coming-soon { cursor: not-allowed; }
  .stage-hero-card.coming-soon .stage-hero-image {
    background: linear-gradient(135deg, #2a1a4a 0%, #3a2060 50%, #4a2870 100%);
    filter: none; opacity: 0.95;
  }
  .stage-hero-card.coming-soon .stage-hero-media::after {
    content: '';
    position: absolute; inset: 0;
    background:
      radial-gradient(ellipse 55% 40% at 30% 30%, rgba(126, 226, 255, 0.28), transparent 70%),
      radial-gradient(ellipse 50% 35% at 70% 70%, rgba(255, 158, 196, 0.22), transparent 70%);
    mix-blend-mode: screen;
    animation: comingSoonAurora 6s ease-in-out infinite alternate;
    pointer-events: none;
  }
  @keyframes comingSoonAurora {
    0%   { transform: translate(-4%, -2%) scale(1.02); }
    100% { transform: translate(4%,  2%) scale(1.08); }
  }
  .stage-hero-card.coming-soon .stage-hero-lock { display: none; }
  .stage-hero-card.coming-soon .stage-hero-cta { display: none; }
  .stage-hero-card.coming-soon .stage-hero-name { color: #d6c7ff; }
  .stage-hero-card.coming-soon .stage-hero-diff {
    color: #7ee2ff; font-weight: 700; letter-spacing: 0.6px; text-transform: uppercase;
  }
  .stage-hero-card.coming-soon .stage-hero-badge {
    background: rgba(126, 226, 255, 0.18); color: #7ee2ff;
    border: 1px solid rgba(126, 226, 255, 0.55);
  }
  .stage-hero-card.coming-soon:hover {
    transform: none;
    border-color: rgba(126, 226, 255, 0.35);
    box-shadow: 0 6px 18px rgba(126, 226, 255, 0.12);
  }

  /* Stage-select compact layout for all touch devices, any orientation,
     any viewport width. Originally lived under a @media (max-width: 520px)
     gate, but that missed landscape phones (852-915px wide) whose
     available HEIGHT (~390px) still can't fit the desktop grid. body.touch
     covers every device that needs the row layout — narrow desktop windows
     are an edge case the desktop grid handles fine via auto-fill. */
  /* rc162 — the full-screen sizing that used to live here was promoted
     to the unconditional `#stage-select-panel` rule above. Only the
     touch-only content-layout differences remain in this block. */
  body.touch:not(.tablet) #stage-select-header {
    padding: calc(14px + env(safe-area-inset-top, 0px)) 18px 10px;
  }
  body.touch:not(.tablet) #stage-select-title { font-size: 17px; }
  body.touch:not(.tablet) #stage-select-subtitle { font-size: 11px; }
  body.touch:not(.tablet) #stage-select-grid {
    display: flex; flex-direction: column;
    grid-template-columns: none;
    gap: 8px;
    padding: 10px 14px 18px;
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
  }
  body.touch:not(.tablet) .stage-hero-card {
    display: flex; flex-direction: row; align-items: stretch;
    border-radius: 10px;
    flex-shrink: 0;
  }
  body.touch:not(.tablet) .stage-hero-card .stage-hero-media {
    width: 96px; aspect-ratio: 4 / 3;
    flex-shrink: 0;
  }
  body.touch:not(.tablet) .stage-hero-card .stage-hero-body {
    position: static;
    padding: 10px 64px 10px 14px;
    justify-content: center;
    border-top: none;
    background: linear-gradient(90deg, rgba(28, 24, 52, 0.95), rgba(18, 16, 36, 0.98));
    flex: 1 1 auto; min-width: 0;
  }
  body.touch:not(.tablet) .stage-hero-card .stage-hero-name { font-size: 14.5px; }
  body.touch:not(.tablet) .stage-hero-card .stage-hero-diff { font-size: 10.5px; }
  body.touch:not(.tablet) .stage-hero-card .stage-hero-cta {
    top: 50%; right: 12px; transform: translateY(-50%);
    padding: 6px 13px; font-size: 10.5px;
    background: linear-gradient(135deg, #ffd36e, #ff9ec4 55%, #b89bff);
    color: #1a1530;
    border-color: transparent;
  }
  body.touch:not(.tablet) .stage-hero-card:hover:not(.locked) { transform: none; }
  body.touch:not(.tablet) .stage-hero-card:hover:not(.locked) .stage-hero-image { transform: none; }
  body.touch:not(.tablet) .stage-hero-card .stage-hero-lock svg { width: 22px; height: 22px; }

