  :root {
    --bg: #f6f6f8;
    --bg-t: rgba(246,246,248,0); /* --bg at 0 alpha, for edge-fade gradients */
    --ink: #15151a;
    --muted: #8a8a93;
    --line: #e4e4e9;
    --accent: #ff5247;
    --card: #ffffff;
    /* Stat accent colors — shared by the game-over round summary AND the profile /
       run-summary stat icons (STAT_ICONS). Defined at :root (not under #gameover)
       so the sibling #profile / #game-summary overlays resolve them too. Theme-
       independent (same values in light + dark). */
    --sum-cyan: #28a8ff; --sum-gold: #ffd24a; --sum-org: #ffb020;
    --sum-violet: #9060ff; --sum-green: #2ecf72; --sum-red: #ff5247;
    /* canvas-only colors (read into the JS `C` object) */
    --grid: rgba(0,0,0,0.12);
    --shadow: rgba(0,0,0,0.22);
    --track: rgba(0,0,0,0.08);
    /* score tier colors — light theme (≥4.5:1 on #ffffff card) */
    --t-k:  #4e6647;
    --t-m:  #1a7a43;
    --t-b:  #005eaf;
    --t-t:  #5a2ee8;
    --t-q:  #b8198f;
    --t-qi: #b33d00;
    --t-sx: #cc1f1f;
    --t-sp: #7a5f00;
  }
  [data-theme="dark"] {
    --bg: #0e0e12;
    --bg-t: rgba(14,14,18,0); /* --bg at 0 alpha, for edge-fade gradients */
    --ink: #ececf1;
    --muted: #6f6f7a;
    --line: #2a2a32;
    --accent: #ff5247;
    --card: #1a1a20;
    --grid: rgba(255,61,240,0.07); /* synthwave magenta tint */
    --shadow: rgba(0,0,0,0.40);
    --track: rgba(255,255,255,0.08);
    /* score tier colors — dark theme (vivid, previously approved) */
    --t-k:  #7d9175;
    --t-m:  #2ecf72;
    --t-b:  #28a8ff;
    --t-t:  #9060ff;
    --t-q:  #f040cc;
    --t-qi: #ff7a28;
    --t-sx: #ff3f3f;
    --t-sp: #ffd24a;
  }
  /* ============================================================
     DESIGN SYSTEM — shared scales + roles (theme-independent).
     Color ROLES (--bg/--ink/--accent/etc.) live in the theme blocks
     above; the scales below are the same in light + dark.
     Spacing/radius snap to a 4px grid; type is a fixed ramp.

     USAGE — reach for a token, never a raw value:
       spacing  padding/margin/gap        -> var(--sp-3)        (4px grid)
       radius   corners                   -> var(--r-md), --r-pill (capsule), --r-circle (dot/avatar)
       type     font-size                 -> var(--fs-sm)
       color    text/bg/border roles      -> var(--ink) --muted --bg --card --line
       accent   play / menu / primary     -> var(--accent)      (red)
       accent   garage / profile / summary-> var(--accent-2)    (violet, theme-independent)
       status   signal hues               -> var(--danger) --progress --notice
       size     header button / row icon  -> var(--control) / var(--icon-row)
       depth    floating card             -> var(--shadow-panel)
     Literal values stay only for: brand logo gradient, synthwave
     overlay colors (level-up neon, continue CTA, --cardc glows),
     and one-off geometry (canvas sizes, gauge heights).
     ============================================================ */
  :root {
    /* Canonical coin gold — the ◉ token. Balance chip (#menu-coins) is the
       reference; the reward-claim fly + tournament/win-back reward chips all
       render the same flat gold ◉ from this one value. */
    --coin-gold: #ffd84d;
    /* Spacing — 4px grid. Use for padding / margin / gap. */
    --sp-1: 4px;
    --sp-2: 8px;
    --sp-3: 12px;
    --sp-4: 16px;
    --sp-5: 20px;
    --sp-6: 24px;
    --sp-8: 32px;
    /* Radius. --r-pill for capsules, --r-circle for dots/avatars. */
    --r-xs: 4px;
    --r-sm: 8px;
    --r-md: 12px;
    --r-lg: 16px;
    --r-xl: 20px;   /* the floating .panel card */
    --r-pill: 999px;
    --r-circle: 50%;
    /* Type ramp (px). Anchored on the sizes already in use. */
    --fs-2xs: 9px;
    --fs-xs: 11px;
    --fs-sm: 12px;
    --fs-md: 14px;
    --fs-lg: 16px;
    --fs-xl: 18px;
    --fs-2xl: 21px;
    --fs-3xl: 24px;
    --fs-display: 44px; /* lobby best score */
    --fs-hero: 64px;    /* DRIFT logo lockup */
    /* Elevation — the floating card. (Accent-tinted glows, e.g. the
       active leaderboard tab, stay literal: they're state, not depth.) */
    --shadow-panel: 0 20px 60px -20px rgba(0,0,0,.18);
    /* Second brand accent — the "identity" violet used by the meta
       screens (garage / profile / summary). Theme-independent on
       purpose so the showroom reads the same in light + dark. */
    --accent-2: #a855f7;
    /* Status colors — non-theme signal hues. */
    --danger:   #ff3b30; /* alert dot / unseen-unlock */
    --progress: #ffb020; /* unlock-progress fill */
    --notice:   #d99a2b; /* reassuring amber (offline/queued) */
    /* One color for EVERY "new / unseen / attention" notification dot across the
       app (garage/upgrades/leaderboard/rail/profile + the gated-button unlock dot).
       Purple, matching the meta-screen violet so dots + value badges read as one
       family. Recolor here = recolor everywhere. */
    --notif-dot: var(--accent-2);
    /* Component sizes — recurring fixed dimensions. */
    --control: 36px;   /* circular header buttons: settings / profile / close */
    --icon-row: 22px;  /* mini car + rank icons in list rows */
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  html, body { height: 100%; }
  body {
    background: var(--bg);
    color: var(--ink);
    font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    overflow: hidden;
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none; /* kill iOS long-press callout/tooltip */
    touch-action: none;
  }
  #game {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    cursor: default; /* hidden via JS only while playing */
    -webkit-touch-callout: none; /* suppress iOS long-press tooltip on joystick hold */
    -webkit-user-select: none;
    user-select: none;
  }
  #game.playing { cursor: none; }

  /* --- Overlay shell --- */
  .overlay {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: max(24px, env(safe-area-inset-top)) max(24px, env(safe-area-inset-right)) max(24px, env(safe-area-inset-bottom)) max(24px, env(safe-area-inset-left));
    pointer-events: none;
  }
  .overlay.hidden { display: none; }
  /* First paint shows #menu's static placeholder before main.js populates the
     best score, district label, gate states and tutorial trim — that rewrite
     reflows the panel (a visible blink on lobby open). Keep the lobby invisible
     until the module finishes its first populate: it removes .booting on a
     committed frame (boot.js has a timeout fallback so it can never stay hidden).
     opacity (not display:none) keeps fitBigScore's width measurement valid. */
  #menu { transition: opacity .18s ease; }
  html.booting #menu { opacity: 0; }
  .panel {
    position: relative;
    pointer-events: auto;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r-xl);
    box-shadow: var(--shadow-panel);
    width: 100%;
    max-width: 360px;
    padding: 28px;
    text-align: center;
    animation: pop .25s ease;
  }
  @keyframes pop { from { transform: scale(.96); opacity: 0; } to { transform: scale(1); opacity: 1; } }

  .title {
    /* Clear the absolutely-positioned top corner buttons (~50px tall), matching
       #final-score on the game-over panel, so the title can't overlap them. */
    margin-top: var(--sp-8);
    margin-bottom: var(--sp-6);
    text-align: center;
  }
  /* "Drift Survivors" duotone-depth lockup. Three stacked plates per word —
     cyan + magenta ghosts offset behind a warm front plate — give anaglyph
     depth. Fixed brand colors (theme-independent) so the logo reads the same
     in light and dark. */
  .title { --red: #ff5247; --mag: #f040cc; --cyan: #28a8ff; }
  .title .lock { position: relative; width: 100%; text-align: center; padding-top: 6px; }

  .title .hero { position: relative; display: inline-block; line-height: .9; }
  .title .hero .word {
    font-size: 64px; font-weight: 800; letter-spacing: .04em; display: block;
  }
  .title .hero .l { position: absolute; inset: 0; mix-blend-mode: screen; }
  .title .hero .l1 { color: var(--cyan); transform: translate(-3px, 2px); opacity: .85; }
  .title .hero .l2 { color: var(--mag); transform: translate(3px, -2px); opacity: .9; }
  .title .hero .lfront {
    position: relative;
    background: linear-gradient(180deg, #fff 0%, #ffe9e6 42%, var(--red) 100%);
    -webkit-background-clip: text; background-clip: text;
    color: transparent; -webkit-text-fill-color: transparent;
  }

  .title .sub {
    position: relative; margin-top: 10px;
    display: flex; align-items: center; justify-content: center; gap: 12px;
  }
  .title .sub .rule {
    height: 1px; width: 46px;
    background: linear-gradient(90deg, transparent, rgba(40, 168, 255, .55));
  }
  .title .sub .rule.r {
    background: linear-gradient(90deg, rgba(40, 168, 255, .55), transparent);
  }
  .title .sub .stxt { position: relative; display: inline-block; }
  .title .sub .stxt span {
    font-size: 15px; font-weight: 700; letter-spacing: .46em;
    padding-left: .46em; display: block;
  }
  .title .sub .sl { position: absolute; inset: 0; mix-blend-mode: screen; opacity: .7; }
  .title .sub .sl.c { color: var(--cyan); transform: translateX(-1.5px); }
  .title .sub .sl.m { color: var(--mag); transform: translateX(1.5px); }
  .title .sub .sfront {
    position: relative;
    background: linear-gradient(90deg, var(--cyan), #cde2ff, var(--mag));
    -webkit-background-clip: text; background-clip: text;
    color: transparent; -webkit-text-fill-color: transparent;
  }

  .big-score { font-size: 54px; font-weight: 800; line-height: 1; white-space: nowrap; }
  #menu-best { font-size: var(--fs-display); } /* lobby best-score sits below the larger title, kept smaller */
  /* The game-over score is the panel's first element. Drop it below the top
     corner buttons (help left, theme right, ~50px tall) so long numbers like
     "10,259,314" can't run under them. */
  #final-score { margin-top: var(--sp-8); }
  .score-label { color: var(--muted); font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 2px; margin-top: var(--sp-2); }
  /* Lobby world-rank line under the best score. Hidden until the player has a
     rank (set from the last submitted run). */
  .rank-line { color: var(--muted); font-size: var(--fs-sm); letter-spacing: 1px; margin-top: var(--sp-1); }
  .rank-line[hidden] { display: none; }
  .rank-line .accent { color: var(--accent); font-weight: 700; }
  .rank-msg { margin-top: var(--sp-4); font-size: var(--fs-md); font-weight: 600; min-height: 20px; }
  .rank-msg .accent { color: var(--accent); }

  /* Staged game-over reveal: rows start invisible + slightly dropped; .revealed
     fades/rises them in while gameover.js runReveal counts the values up. */
  #gameover .ov-reveal { opacity: 0; transform: translateY(8px); transition: opacity .2s ease-out, transform .2s ease-out; }
  #gameover .ov-reveal.revealed { opacity: 1; transform: none; }
  @media (prefers-reduced-motion: reduce) { #gameover .ov-reveal { transition: none; } }
  /* Round-summary card: time + coins + the next-unlock progress share one frame
     (label left, big value right, hairline dividers between rows). */
  .over-card {
    width: 100%; max-width: 380px; margin: 0 auto; text-align: left;
    border: 1px solid var(--line); border-radius: var(--r-lg); padding: 0 var(--sp-4);
  }
  .over-card > * + * { border-top: 1px solid color-mix(in srgb, var(--line) 60%, transparent); }
  .oc-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
            padding: var(--sp-4) 0; }
  .oc-row[hidden] { display: none; }
  .oc-label { display: inline-flex; align-items: center; gap: var(--sp-2);
              color: var(--muted); font-size: var(--fs-sm); }
  .oc-label svg { width: 1.25em; height: 1.25em; }
  .oc-coin { color: #ffd84d; }
  .oc-val { font-size: var(--fs-3xl); font-weight: 800; font-variant-numeric: tabular-nums; }
  .oc-gold { color: #ffd84d; }
  /* Coin haul + the double-your-coins pill sit INLINE on the right of the Coins row
     (pill just left of the value, which stays rightmost so the number column still
     aligns with the other rows). Reads as an action ON the value, not a bolted-in box. */
  /* Pill sits inline left of the haul (value stays rightmost, column aligned). */
  .oc-coin-end { display: flex; flex-direction: row; align-items: center; justify-content: flex-end; gap: var(--sp-3); }
  .oc-coin-end .oc-val { line-height: 1; }
  /* Double pill — compact gold capsule (rewarded-ad CTA; noticeable via colour, not
     size). ×2 badge sits in a darkened inset. Goes flat/muted once claimed.
     margin:0 resets the global `.panel button` margin-top, which otherwise offsets
     the pill ~8px below the number when the flex row centre-aligns the margin box. */
  .over-coins-double { display: inline-flex; align-items: center; gap: var(--sp-1); margin: 0;
    padding: var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-3); border: 0; border-radius: var(--r-pill);
    background: #ffd84d; color: #1a1206; font-weight: 800; font-size: var(--fs-xs);
    line-height: 1; cursor: pointer; -webkit-tap-highlight-color: transparent; }
  .over-coins-double[hidden] { display: none; } /* inline-flex above would beat the UA [hidden] rule */
  .over-coins-double .ocd-x { background: rgba(0,0,0,0.16); border-radius: var(--r-pill);
    padding: 0 var(--sp-2); font-weight: 900; }
  .over-coins-double:disabled { cursor: default; }
  .over-coins-double.done { background: transparent; color: var(--muted); } /* after the double lands */
  .over-coins-double.done .ocd-x { display: none; }
  .oc-end { display: inline-flex; align-items: baseline; gap: var(--sp-2); }
  .oc-sub { color: var(--muted); font-size: var(--fs-xs); }

  /* Rank badge (per-location crowns / cups) shown beside a player's name. Sized to
     the surrounding text via 1em so it scales in titles and list rows alike. */
  .rank-badge {
    display: inline-flex;
    align-items: center;
    width: 1.05em; height: 1.05em;
    margin-left: 0.28em;
    vertical-align: -0.16em;
    flex: 0 0 auto;
    filter: drop-shadow(0 1px 1px rgba(0,0,0,0.45));
  }
  .rank-badge svg { width: 100%; height: 100%; display: block; }
  /* Multiple per-location badges tuck under their predecessor into a tight stack;
     the first keeps its margin-left gap from the name. Drop-shadow separates them. */
  .rank-badge + .rank-badge { margin-left: -0.35em; }

  button {
    font: inherit;
    font-weight: 700;
    cursor: pointer;
    border: none;
    border-radius: var(--r-md);
    padding: var(--sp-4) var(--sp-5);
    width: 100%;
    background: var(--ink);
    color: var(--bg); /* inverts with --ink so the label stays readable in both themes */
    margin-top: var(--sp-4);
    transition: transform .08s ease, opacity .2s;
  }
  button:hover { transform: translateY(-1px); }
  button:active { transform: translateY(1px); }
  button.ghost { background: transparent; color: var(--muted); border: 1px solid var(--line); }
  /* In-run "Finish run" — a ghost button tinted with the brand red so ending a
     live run reads as a deliberate, weightier action than the neutral rows. */
  button.ghost.finish-run { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
  /* Honor the .hidden toggle (openSettings only shows it for the in-game gear).
     Without this rule the bare .hidden class is inert on a non-overlay button, so
     the button leaked into the lobby/game-over settings popups. */
  #settings-finish-run.hidden { display: none; }

  /* Settings family floats ON TOP of the live UI instead of replacing it — the
     opener overlay (lobby / game-over) stays mounted underneath. Translucent scrim
     (like #update-popup) dims the UI behind and owns pointer events so a tap that
     misses the panel can't leak to the lobby/canvas below; z-index clears the HUD
     (#ingame-settings is z-index 6) so the in-run gear no longer needs hiding.
     Only one of the three is ever visible at once (advanced/feedback stash settings). */
  #settings, #settings-advanced, #feedback-popup {
    z-index: 30; pointer-events: auto;
    background: rgba(8, 8, 14, 0.62);
    backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
  }

  /* Unified dev cheat deck: one panel, category-grouped, 2-up button grids.
     The panel flexes to the overlay's padded height and .cheats-body owns the
     scroll, so the title and Close stay pinned while the deck scrolls. Sits
     above every product overlay (profile, tournament, celebrate @1100) so the
     two-finger toggle is visible from anywhere; toast (1200) still wins. */
  #cheats-panel { z-index: 1150; }
  #cheats-panel .panel {
    max-width: 440px;
    max-height: 100%;
    display: flex; flex-direction: column;
    overflow: hidden; /* the body scrolls, not the panel */
  }
  #cheats-panel .set-title { margin-bottom: var(--sp-2); }
  #cheats-panel .hint { margin-top: var(--sp-3); }
  #cheats-panel #cheats-close { flex: 0 0 auto; }
  .cheats-body {
    flex: 1 1 auto; min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 2px; /* room for button focus/hover lift at the grid edges */
    /* soft fade at both scroll edges so clipped rows read as "more below" */
    -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
            mask-image: linear-gradient(180deg, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
  }
  .cheat-cat {
    display: flex; align-items: center; gap: var(--sp-3);
    margin: var(--sp-5) 0 var(--sp-2);
    color: var(--muted); font-size: var(--fs-sm); font-weight: 800;
    text-transform: uppercase; letter-spacing: 2px; white-space: nowrap;
  }
  .cheat-cat::before, .cheat-cat::after { content: ""; height: 1px; background: var(--line); flex: 1; }
  .cheats-body .cheat-cat:first-child { margin-top: var(--sp-2); }
  .cheat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
  /* Dense dev buttons: ~150px columns on phones — the default button padding/size
     wraps labels to 4 lines, so drop both a notch for the deck only. */
  .cheat-grid button { margin-top: 0; padding: var(--sp-3) var(--sp-2); font-size: var(--fs-sm); font-weight: 600; }
  .cheat-grid .cheat-wide { grid-column: 1 / -1; }

  /* Lobby Play CTA — two-color (brand red + lockup cyan): a static red/cyan
     chromatic-aberration split plus a sheen sweep. The sheen is a real child
     element (.play-sheen) driven by the Web Animations API in enterLobby() —
     WKWebView won't restart a CSS animation on an element that was display:none
     during a run, but a freshly-created WAAPI animation always plays. */
  #play-btn {
    position: relative;
    overflow: hidden;
    color: #fff;
    font-weight: 800;
    letter-spacing: 3px;
    text-transform: uppercase;
    border: 1px solid rgba(255, 255, 255, .14);
    background: linear-gradient(#17141f, #120f18);
    text-shadow: -1.6px 0 0 rgba(255, 82, 71, .55), 1.6px 0 0 rgba(40, 168, 255, .55);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .25), 0 8px 22px rgba(0, 0, 0, .45);
    transition: transform .12s cubic-bezier(.34, 1.56, .64, 1), border-color .2s ease;
  }
  /* sheen — bright diagonal highlight; parked off-screen until JS animates it */
  #play-btn .play-sheen {
    position: absolute; top: 0; bottom: 0; left: -75%;
    width: 55%;
    background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .30) 45%, rgba(255, 255, 255, .45) 50%, rgba(255, 255, 255, .30) 55%, transparent);
    transform: skewX(-18deg);
    pointer-events: none;
  }
  #play-btn:hover {
    transform: translateY(-2px) scale(1.025);
    border-color: rgba(255, 255, 255, .26);
  }
  #play-btn:active { transform: translateY(1px) scale(.97); }

  /* Small icon button anchored in the panel's top-right corner (the settings gear) */
  .settings-toggle, .recent-toggle, .friends-toggle {
    position: absolute;
    top: 14px;
    right: 14px;
    width: var(--control);
    height: var(--control);
    margin: 0;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-lg);
    line-height: 1;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: var(--r-circle);
  }

  /* In-run settings gear: floats over the canvas, top-right safe area. Same look as
     the menu .settings-toggle but position:fixed. Hidden unless a run is live (the
     game loop toggles .hidden off the `live` flag). z-index clears the HUD; the
     #settings popup (z-index 30) now layers its scrim over this button instead of
     hiding it, so it reads as dimmed-behind rather than gone. */
  #ingame-settings {
    position: fixed;
    top: max(14px, env(safe-area-inset-top));
    right: max(14px, env(safe-area-inset-right));
    width: var(--control);
    height: var(--control);
    margin: 0;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-lg);
    line-height: 1;
    background: var(--card);
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: var(--r-circle);
    z-index: 6;
  }
  #ingame-settings.hidden { display: none; }

  /* Profile / your-stats — mirror of .settings-toggle, sits just left of the gear. */
  .profile-toggle {
    position: absolute;
    top: 14px;
    right: 58px;
    width: var(--control);
    height: var(--control);
    margin: 0;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-lg);
    line-height: 1;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: var(--r-circle);
  }

  /* --- Full-screen main menu (scoped to #menu; game-over keeps the card) --- */
  /* Background lives on the overlay (inset:0) so it fills edge-to-edge behind the
     safe-area insets; the overlay's padding insets the panel's CONTENT only.
     #menu stays TRANSPARENT so the canvas behind it (same C.bg color) shows the
     idle-drifting lobby car through the panel's empty center. #gameover stays
     opaque — it covers the dead-car scene. */
  #gameover { background: var(--bg); }
  #menu { background: transparent; }
  /* Top + bottom edge-fade to --bg (plus a 20% black deepen) so the idle game
     drifting behind the lobby settles behind the title and the action buttons
     instead of competing. Fades out with the panel on Play. */
  #menu::before {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    background:
      linear-gradient(to bottom, rgba(0,0,0,.2) 0%, rgba(0,0,0,0) 26%),
      linear-gradient(to top,    rgba(0,0,0,.2) 0%, rgba(0,0,0,0) 30%),
      linear-gradient(to bottom, var(--bg) 0%, var(--bg-t) 26%),
      linear-gradient(to top,    var(--bg) 0%, var(--bg-t) 30%);
    transition: opacity .3s ease;
  }
  #menu .panel { position: relative; z-index: 1; }
  #menu .panel, #gameover .panel {
    width: 100%;
    height: 100%;
    max-width: none;
    padding: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
    display: flex;
    flex-direction: column;
    animation: fade .2s ease;
  }
  @keyframes fade { from { opacity: 0; } to { opacity: 1; } }
  @media (prefers-reduced-motion: reduce) { #menu .panel { animation: none; } }
  /* Lobby Play: fade the menu panel out to reveal the drifting car/world. The
     overlay is pointer-through; killing panel pointer-events lets the canvas take
     input immediately while the fade plays. JS adds .hidden after the transition. */
  #menu.lifting .panel { opacity: 0; pointer-events: none; transition: opacity .3s ease; }
  #menu.lifting::before { opacity: 0; } /* fade the edge-vignette out with the panel */

  /* Top bar: the three controls flow inline instead of pinning to corners. */
  .menu-topbar {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex: 0 0 auto;
    margin-bottom: var(--sp-2);
  }
  .menu-topbar .settings-toggle,
  .menu-topbar .recent-toggle,
  .menu-topbar .friends-toggle,
  .menu-topbar .profile-toggle {
    position: static;
    top: auto;
    right: auto;
    left: auto;
  }
  /* Friends: round icon toggle in the social cluster; relative for its dot. */
  .menu-topbar .friends-toggle { position: relative; }
  .friends-toggle svg { width: 1em; height: 1em; }
  .friends-toggle .notif-dot { position: absolute; top: -3px; right: -3px; margin: 0; }
  /* Stroke-SVG glyphs (clock/gear) size like the emoji they replaced. */
  .recent-toggle svg, .settings-toggle svg, #ingame-settings svg { width: 1em; height: 1em; display: block; }
  .menu-topbar .settings-toggle { margin-left: auto; } /* bare-gear fallback; #menu/#gameover right-groups override below; profile stays left */
  /* Coin wallet chip — content-width gold pill pushed right, just left of the
     clock/gear cluster (the social cluster owns the left edge); taps to garage. */
  .menu-coins { width: auto; margin: 0 0 0 auto; height: var(--control); display: flex; align-items: center;
                white-space: nowrap; border: 1px solid var(--line); border-radius: var(--r-lg);
                background: color-mix(in srgb, var(--ink) 4%, transparent);
                color: var(--coin-gold); font-size: var(--fs-xs); font-weight: 800; font-variant-numeric: tabular-nums;
                padding: 0 var(--sp-3); cursor: pointer; -webkit-tap-highlight-color: transparent; }
  /* Lobby wallet: leading "+" top-up + balance fused into ONE subtle pill. The
     wrapper owns the pill border/bg + the push-right; the inner chip drops its own so
     the two read as a single control. The "+" is a muted-gold glyph (subtle, no fill)
     — a top-up hint, not a shout. It's a sibling, never a child of #menu-coins
     (paintCoinChip overwrites the chip's textContent each repaint). width:auto on the
     "+" beats the global button{width:100%}. */
  .coin-wallet { margin-left: auto; display: inline-flex; align-items: center; height: var(--control);
                 border: 1px solid var(--line); border-radius: var(--r-lg);
                 background: color-mix(in srgb, var(--ink) 4%, transparent); }
  .coin-wallet .menu-coins-plus { width: auto; flex: 0 0 auto; height: 100%; margin: 0;
                 padding: 0 0 0 var(--sp-2); display: flex; align-items: center; justify-content: center;
                 border: 0; background: transparent; border-radius: 0;
                 color: var(--coin-gold); opacity: .7; font-size: var(--fs-lg); font-weight: 800; line-height: 1;
                 cursor: pointer; -webkit-tap-highlight-color: transparent; }
  .coin-wallet .menu-coins-plus svg { display: block; }
  .coin-wallet .menu-coins-plus:active { opacity: 1; }
  .coin-wallet .menu-coins { margin: 0; border: 0; background: transparent; border-radius: 0; height: 100%;
                 padding: 0 var(--sp-3) 0 var(--sp-1); }
  /* Lobby right-group: clock (Recent) + gear; the coins chip's auto margin owns
     the push-right, the group just flows after it. */
  #menu .menu-topbar .topbar-right { margin-left: 0; display: flex; align-items: center; gap: var(--sp-2); }
  #menu .menu-topbar .topbar-right .settings-toggle { margin-left: 0; }
  /* Game-over right-group: balance chip + clock (Recent) + gear. The group itself
     owns the push-right; the chip drops its baked-in auto margin so it sits inline
     with the clock/gear instead of splitting the row. */
  #gameover .menu-topbar .topbar-right { margin-left: auto; display: flex; align-items: center; gap: var(--sp-2); }
  #gameover .menu-topbar .topbar-right .settings-toggle { margin-left: 0; }
  #gameover .menu-topbar .topbar-right .menu-coins { margin: 0; }

  /* Profile is an icon-only round toggle in the menu + game-over top bars (same
     family as friends/clock/gear — the name lives in the profile screen). The
     medal chip-dot rides the corner like the friends notif dot. */
  .menu-topbar .profile-toggle { position: relative; }
  .profile-toggle svg { width: 1em; height: 1em; }
  .menu-topbar .profile-toggle .chip-dot { position: absolute; top: -3px; right: -3px; margin: 0; }

  /* The body fills the space between top bar and Play. It does NOT scroll itself —
     only the leaderboard list inside it does. Children span the full width so text
     wraps and the big score can measure its available width (fitBigScore). */
  .menu-body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    overflow: hidden;
    text-align: center;
  }
  #menu .menu-body > *, #gameover .menu-body > * { width: 100%; flex: 0 0 auto; }
  /* No corner buttons to dodge here, but the title's drop-shadow glow needs
     headroom or menu-body's overflow:hidden clips it flat across the top. */
  #menu .menu-body .title { margin-top: 0; padding-top: var(--sp-8); }
  #gameover #final-score { margin-top: 0; font-size: var(--fs-display); } /* fullscreen body has a real topbar above it */
  /* Leaderboard absorbs the leftover height; its <ol> is the only scroll region. */
  #menu #menu-lbwrap { flex: 1 1 auto; min-height: 120px; display: flex; flex-direction: column; }
  #menu #menu-lbwrap ol { height: auto; flex: 1 1 auto; min-height: 0; }
  /* game-over: the round summary owns the body. It scrolls as one column (the
     groups + perk card + lifetime), absorbing all leftover height so the panel
     buttons stay pinned below. */
  /* Round summary: topbar pins to the top and the action buttons to the bottom
     (both inset by the overlay's safe-area padding). The column fills the body
     and spreads its clusters (title / stat rows / widget / perks) evenly across
     the freed height via auto margins — which resolve to 0 when the column
     overflows, falling back to the plain top-stacked scroll on short screens. */
  #gameover .menu-body { justify-content: flex-start; }
  #gameover #over-summary-scroll {
    flex: 1 1 auto; min-height: 0; width: 100%;
    overflow-y: auto; overflow-x: hidden; /* scroll if it still overflows — never clip the perks row off-screen */
    scrollbar-width: none;
    display: flex; flex-direction: column; align-items: stretch; text-align: center;
  }
  #gameover #over-summary-scroll::-webkit-scrollbar { display: none; }
  #gameover #over-summary-scroll > * { flex: 0 0 auto; }
  #gameover .over-eyebrow { margin-top: auto; }
  #gameover #over-card { margin-top: auto; margin-bottom: auto; }
  #gameover #over-perks { margin-bottom: auto; }
  /* No perk recap this run (renderOverPerks hid #over-perks): the perks row is the
     column's trailing cluster, so its absence drops the even-spread from four auto
     margins to three — which marooned the card in dead-center under a sunk-down
     title. Top-anchor the header + card instead; the freed height pools below the
     card (where the perks row would sit) so a perk-less run reads like an ordinary
     top-loaded results screen. margin-bottom:auto still resolves to 0 on overflow,
     keeping the short-screen top-stacked scroll. */
  #gameover #over-summary-scroll.no-perks .over-eyebrow { margin-top: 0; }
  #gameover #over-summary-scroll.no-perks #over-card { margin-top: var(--sp-8); margin-bottom: auto; }
  #menu #play-btn, #gameover #again-btn, #gameover #main-btn { flex: 0 0 auto; margin-top: var(--sp-4); }
  #gameover #again-btn { margin-top: var(--sp-6); } /* same rhythm as the stat sections */
  #gameover #main-btn { margin-top: var(--sp-3); }  /* tight pair with Play again */

  /* Lobby secondary actions: one compact horizontal row of icon tiles below Play.
     Frees the vertical space the three stacked ghost buttons used to eat. */
  /* During the tutorial the lobby is stripped to name/tagline/best/Play. */
  #menu.tutorial .menu-topbar,
  #menu.tutorial .menu-actions,
  #menu.tutorial #tips-feed { display: none; }
  #menu .menu-actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-3); flex: 0 0 auto; }
  #menu .menu-actions .icon-action {
    flex: 1 1 0; width: auto; min-width: 0; margin-top: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-1);
    background: transparent; color: var(--muted); border: 1px solid var(--line);
    border-radius: var(--r-lg); padding: var(--sp-3) var(--sp-1); position: relative;
  }
  /* [hidden] must beat the display:flex above (tournament tile off-season). */
  #menu .menu-actions .icon-action[hidden] { display: none; }
  #menu .menu-actions .icon-action:hover { color: var(--ink); border-color: var(--ink); }
  #menu .menu-actions .ia-icon { font-size: var(--fs-2xl); line-height: 1; }
  #menu .menu-actions .ia-icon svg { display: block; }
  #menu .menu-actions .ia-label {
    font-size: var(--fs-2xs); font-weight: 600; letter-spacing: .02em; text-transform: uppercase; opacity: .9;
    line-height: 1.05; text-align: center; max-width: 100%;
  }
  /* badge floats over the tile's top-right corner instead of sitting after a text label */
  #menu .menu-actions .garage-badge,
  #menu .menu-actions .lb-drop-badge,
  #menu .menu-actions .notif-dot {
    position: absolute; top: var(--sp-2); right: var(--sp-2); margin: 0;
  }

  /* Leaderboard + Recent + Friends screens: full-screen like #menu, body fills the panel. */
  #leaderboard, #recent, #friends { background: var(--bg); }
  #leaderboard .panel, #recent .panel, #friends .panel {
    width: 100%; max-width: none; height: 100%; padding: 0;
    background: var(--bg); border: none; box-shadow: none; border-radius: 0;
    display: flex; flex-direction: column;
  }
  /* No header divider on the leaderboard — the podium sits flush under the title. */
  #leaderboard .screen-head { border-bottom: none; padding-bottom: 0; margin-bottom: var(--sp-3); }
  /* stretch (not center) so the board fills the full width; menu-body + lbwrap +
     ol each flex to fill the height edge-to-edge. */
  #leaderboard .menu-body { flex: 1 1 auto; display: flex; flex-direction: column; align-items: stretch; min-height: 0; }
  #leaderboard #menu-lbwrap { width: 100%; flex: 1 1 auto; min-height: 0; margin-top: 0; display: flex; flex-direction: column; }
  #leaderboard #menu-lbwrap ol { height: auto; flex: 1 1 auto; min-height: 0; overflow-y: auto; margin-top: 0; }
  /* Time window pinned to the bottom (just above Done), centered as a footer control. */
  #leaderboard #menu-lbwrap .lb-window { flex: 0 0 auto; justify-content: center; margin-top: var(--sp-3); }
  #leaderboard #leaderboard-done { flex: 0 0 auto; margin-top: var(--sp-4); }

  /* Recent screen: same full-height flex column as the leaderboard board, minus the
     podium/window — just the scrolling feed (rows are styled by the block below). */
  #recent .menu-body { flex: 1 1 auto; display: flex; flex-direction: column; align-items: stretch; min-height: 0; }
  #recent #recent-wrap { width: 100%; flex: 1 1 auto; min-height: 0; margin-top: var(--sp-3); display: flex; flex-direction: column; }
  #recent #recent-wrap ol { height: auto; flex: 1 1 auto; min-height: 0; overflow-y: auto; margin-top: 0; }
  #recent #recent-done { flex: 0 0 auto; margin-top: var(--sp-4); }

  /* ---- Recent screen redesign -------------------------------------------------
     Recent is the leaderboard's sibling, so it adopts the same violet meta
     identity. The feed gets its own row styling (the .lb base + leaderboard rules
     bleed in via #recent-wrap.lb, so #recent-wrap overrides win on id specificity),
     grouped under sticky relative-time headers painted by paintRecent. */
  #recent { --accent: var(--accent-2); }

  /* Full-bleed feed: drop the overlay's side gutter + the header divider so the
     feed runs edge-to-edge; re-inset the header / rows / Done so text still breathes. */
  #recent.overlay { padding-left: 0; padding-right: 0; }
  #recent .screen-head { border-bottom: none; padding-bottom: 0; margin-bottom: var(--sp-3);
    padding-left: var(--sp-4); padding-right: var(--sp-4); }
  /* Inset via width (not margin): the base button is width:100%, so side margins
     would overflow the viewport. calc + center keeps it on-screen. */
  #recent #recent-done { width: calc(100% - var(--sp-4) * 2); align-self: center; }

  /* Live-feed signal: a soft violet pulse before the title. */
  #recent .screen-head .set-title { display: inline-flex; align-items: center; gap: var(--sp-2); }
  #recent .set-title::before {
    content: ""; flex: 0 0 auto; width: 8px; height: 8px; border-radius: var(--r-circle);
    background: var(--accent-2); animation: recent-pulse 2.2s ease-out infinite;
  }
  @keyframes recent-pulse {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-2) 55%, transparent); }
    70%  { box-shadow: 0 0 0 7px color-mix(in srgb, var(--accent-2) 0%, transparent); }
    100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-2) 0%, transparent); }
  }
  @media (prefers-reduced-motion: reduce) { #recent .set-title::before { animation: none; } }

  /* Feed rows — own the layout so .lb li / leaderboard rules don't leak in. */
  #recent-wrap ol { list-style: none; }
  #recent-wrap li {
    display: flex; align-items: center; gap: var(--sp-3);
    height: auto; padding: var(--sp-2) var(--sp-4);
    border-bottom: 1px solid var(--line); font-size: var(--fs-md);
  }
  #recent-wrap li:last-child { border-bottom: none; }
  #recent-wrap li.clickable { cursor: pointer; }
  #recent-wrap li.clickable:active { opacity: .65; }

  /* Car → a violet-tinted avatar chip, bigger than the inline list icon. */
  #recent-wrap .r-car {
    width: 34px; height: 34px; flex: 0 0 auto; box-sizing: border-box; padding: 4px;
    border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--accent-2) 9%, var(--card));
    border: 1px solid color-mix(in srgb, var(--accent-2) 16%, transparent);
  }
  /* Name + location chip stacked in a column; the column grows (flex:1) so the
     trailing score cluster stays right-aligned exactly as before. */
  #recent-wrap .r-who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
  #recent-wrap .who {
    max-width: 100%; min-width: 0; font-weight: 600; font-size: var(--fs-md);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    display: inline-flex; align-items: center; gap: 4px;
  }
  /* Location pill — identity colour piped in via --loc (set per row in JS). */
  .r-loc {
    align-self: flex-start; display: inline-flex; align-items: center;
    font-size: 0.6rem; font-weight: 800; letter-spacing: .06em; line-height: 1;
    text-transform: uppercase; padding: 2px 6px; border-radius: 999px;
    color: var(--loc, var(--muted));
    background: color-mix(in srgb, var(--loc, var(--muted)) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--loc, var(--muted)) 30%, transparent);
  }
  /* Score = row hero (keeps scoreSpan's rarity tint); time-ago tucked beneath it. */
  #recent-wrap .r-end {
    flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 1px;
  }
  #recent-wrap .pts {
    font-weight: 800; font-size: var(--fs-lg); line-height: 1.05; font-variant-numeric: tabular-nums;
  }
  #recent-wrap .when {
    color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; white-space: nowrap;
  }

  /* Own runs — violet wash + accent text (the "you" signal). */
  #recent-wrap li.me {
    color: var(--accent-2);
    background: color-mix(in srgb, var(--accent-2) 8%, transparent);
  }
  /* ...but the score stays white — the violet wash + accent name already mark the
     row as yours. High-tier scores keep their rarity tint (it lives on the inner
     .score-t-* / .score-letter span, which sets its own color). */
  #recent-wrap li.me .pts { color: var(--ink); }

  /* Relative-time section headers — sticky as the feed scrolls. */
  #recent-wrap li.recent-sec {
    display: block; height: auto; border: none; cursor: default;
    position: sticky; top: 0; z-index: 1; background: var(--bg);
    padding: var(--sp-4) var(--sp-4) var(--sp-1);
    font-size: var(--fs-xs); font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
    color: var(--accent-2);
  }
  #recent-wrap li.recent-sec:first-child { padding-top: var(--sp-1); }

  /* Empty / offline / loading states — centered, with breathing room. */
  #recent-wrap li.empty, #recent-wrap li.loading {
    justify-content: center; color: var(--muted); font-size: var(--fs-sm);
    border: none; padding: var(--sp-8) var(--sp-1);
  }

  /* ---- Leaderboard showroom: adopt the garage/profile violet identity. Scoping
     --accent here flips the tabs, the active window pill and the own-row glow to
     the same theme-independent violet the meta screens use — no per-rule edits. */
  /* Per-location tint: JS sets --lb-tint to the current district's titleColor;
     unset falls back to the meta violet, so the board looks identical until a
     location is active. Driving --accent from it flips tabs/active-pill/own-row. */
  #leaderboard { --accent: var(--lb-tint, var(--accent-2)); }
  /* Top-3 podium "bay" — mirrors the profile-hero key-light: a violet top light
     over a dark sweep-floor, the leaders parked under it on tinted pedestals. */
  .lb-podium { position: relative; flex: 0 0 auto;
    margin: 0 0 var(--sp-3); padding: var(--sp-4) var(--sp-3) var(--sp-3);
    border-radius: var(--r-lg); overflow: hidden;
    background:
      radial-gradient(120% 70% at 50% 0%, color-mix(in srgb, var(--lb-tint, var(--accent-2)) 24%, transparent), transparent 62%),
      radial-gradient(80% 80% at 50% 120%, color-mix(in srgb, var(--lb-tint, var(--accent-2)) 8%, transparent), transparent 55%),
      linear-gradient(180deg, var(--shadow), color-mix(in srgb, var(--ink) 6%, var(--shadow))); }
  .lb-podium[hidden] { display: none; }
  /* align-items:flex-end so the three pedestals share one baseline (the floor). */
  .pod-row { display: flex; align-items: flex-end; justify-content: center; gap: var(--sp-2); }
  .pod { flex: 1 1 0; max-width: 116px; min-width: 0; position: relative;
    display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); }
  /* gold sits center regardless of DOM order (painted 1·2·3). */
  .pod-1 { order: 2; } .pod-2 { order: 1; } .pod-3 { order: 3; }
  .pod.clickable { cursor: pointer; } .pod.clickable:active { opacity: .8; }
  .pod-crown { width: 22px; height: 22px; display: block; margin-bottom: -1px; }
  .pod-crown svg { width: 22px; height: 22px; display: block; }
  .pod-who { font-size: var(--fs-sm); font-weight: 700; max-width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    display: inline-flex; align-items: center; gap: 4px; }
  .pod-pts { font-size: var(--fs-md); font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; }
  .pod-base { width: 100%; margin-top: var(--sp-1); border-radius: var(--r-sm) var(--r-sm) 0 0;
    display: flex; align-items: flex-start; justify-content: center; padding-top: var(--sp-1);
    font-weight: 800; font-variant-numeric: tabular-nums; font-size: var(--fs-sm);
    /* thin top edge catches the bay's key-light so each pedestal reads as a block. */
    box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 14%, transparent); }
  /* Pedestal heights rank the medals; fills carry the metal, numbers stay
     theme-legible (medal hues that read on both the light + dark bay floor). */
  .pod-1 .pod-base { height: 46px; background: color-mix(in srgb, var(--sum-gold) 30%, transparent); color: #b5820a; }
  .pod-2 .pod-base { height: 32px; background: color-mix(in srgb, #9aa3b2 30%, transparent); color: #6f7787; }
  .pod-3 .pod-base { height: 22px; background: color-mix(in srgb, #c77b3a 30%, transparent); color: #a85c24; }
  [data-theme="dark"] .pod-1 .pod-base { color: var(--sum-gold); }
  [data-theme="dark"] .pod-2 .pod-base { color: #d3d9e6; }
  [data-theme="dark"] .pod-3 .pod-base { color: #e3a06a; }
  .pod.me .pod-who, .pod.me .pod-pts { color: var(--lb-tint, var(--accent-2)); }
  /* tasteful-wink leader line under the podium. */
  .pod-cap { text-align: center; font-size: var(--fs-xs); color: var(--muted); margin-top: var(--sp-3); }

  /* Locked/coming-soon board teaser — replaces podium+list when #menu-lbwrap.locked. */
  #lb-locked {
    display: none; flex: 1 1 auto; min-height: 0;
    flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-2);
    color: var(--muted); font-size: var(--fs-md); font-weight: 700; text-align: center;
  }
  #lb-locked .lb-lock-ico {
    font-size: 2rem; line-height: 1;
    filter: drop-shadow(0 0 10px color-mix(in srgb, var(--lb-tint, var(--accent-2)) 55%, transparent));
  }
  #menu-lbwrap.locked #menu-lb,
  #menu-lbwrap.locked .lb-podium { display: none; }
  #menu-lbwrap.locked #lb-locked { display: flex; }

  /* Board carousel arrows (#lb-prev/#lb-next): reuse the lobby .district-arrow look.
     Scope the end-of-carousel fade under #leaderboard so it beats the board's own
     accent cascade (the bare .district-arrow.is-end opacity:0 gets out-specified
     here otherwise, leaving a dead arrow visible at the first/last location). */
  #leaderboard .district-arrow.is-end { opacity: 0; }

  /* One-shot directional slide when the board switches location (JS adds/removes
     the class). Content nudges in from the swipe direction + fades — a lightweight
     stand-in for a full carousel pan (the board content is async-fetched, so a
     true two-panel slide isn't worth it). */
  @keyframes lb-in-right { from { opacity: .25; transform: translateX(46px); } to { opacity: 1; transform: none; } }
  @keyframes lb-in-left  { from { opacity: .25; transform: translateX(-46px); } to { opacity: 1; transform: none; } }
  #menu-lbwrap.lb-slide-next { animation: lb-in-right .34s cubic-bezier(.22,.61,.36,1); }
  #menu-lbwrap.lb-slide-prev { animation: lb-in-left  .34s cubic-bezier(.22,.61,.36,1); }
  @media (prefers-reduced-motion: reduce) {
    #menu-lbwrap.lb-slide-next, #menu-lbwrap.lb-slide-prev { animation: none; }
  }

  /* Frozen-window hint: centered, non-interactive, fades with .show. */
  .drive-nudge {
    position: fixed; left: 50%; bottom: 18%; transform: translateX(-50%);
    z-index: 5; pointer-events: none;
    font: 700 var(--fs-md) ui-sans-serif, system-ui, sans-serif;
    letter-spacing: .04em; text-transform: uppercase;
    color: var(--ink); opacity: 0; transition: opacity .35s ease;
    text-shadow: 0 1px 6px var(--bg);
  }
  .drive-nudge.show { opacity: .85; }

  /* --- Full-screen sheet (reusable: profile, etc.) --- */
  /* Background on the overlay (inset:0) fills edge-to-edge behind the safe area;
     the overlay's padding insets the content. Mirrors the #menu treatment. */
  /* Profile is a full-screen DOCUMENT scroll, not a fixed card with an inner
     scroll region: the overlay itself scrolls and page length = content length. */
  #profile {
    background: var(--bg);
    align-items: flex-start;          /* scroll from the top, not vertically centered */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    pointer-events: auto;             /* overlay is normally pointer-through; this screen captures */
  }
  #profile .panel {
    width: 100%;
    min-height: 100%;                 /* fill the viewport, grow past it with content */
    max-width: none;
    padding: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
    overflow: visible;                /* no inner scroll — the overlay scrolls */
    display: flex;
    flex-direction: column;
    animation: none;                  /* no open fade/pop — content paints instantly, a ramp reads as a blink */
  }
  .sheet-topbar {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex: 0 0 auto;
    margin-bottom: var(--sp-2);
    text-align: left;
  }
  .sheet-topbar .set-title { margin: 0; margin-right: auto; } /* title left, close pushed right */
  .sheet-close {
    flex: 0 0 auto;
    width: var(--control);
    height: var(--control);
    padding: 0;
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-xl);
    line-height: 1;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: var(--r-circle);
  }
  /* Natural flow — grows with content; the overlay (not this) is the scroll region. */
  .sheet-body {
    flex: 1 0 auto;
    min-height: 0;
    overflow: visible;
    display: flex;
    flex-direction: column;
  }
  #profile .profile-history { max-height: none; } /* no nested scroll — the page scrolls */
  /* Done trails content; margin-top:auto drops it to the viewport bottom when the
     content is shorter than one screen. padding-bottom is the gap above it. */
  #profile #profile-done { flex: 0 0 auto; margin-top: auto; }
  #profile .sheet-body { padding-bottom: var(--sp-6); }

  button:disabled { opacity: .4; cursor: default; transform: none; }

  input {
    font: inherit;
    width: 100%;
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    margin-top: var(--sp-4);
    text-align: center;
    outline: none;
  }
  input:focus { border-color: var(--accent); }

  /* --- Leaderboard --- */
  .lb { margin-top: var(--sp-6); text-align: left; }
  /* Fixed 7-row (245px) window: the panel height stays constant regardless of
     row count or tab, and lists longer than 7 (up to 100) scroll inside it. */
  .lb ol { list-style: none; height: 245px; overflow-y: auto; margin-top: var(--sp-4);
    scrollbar-width: none; -webkit-overflow-scrolling: touch; }  /* hide the scroll indicator (Firefox) */
  .lb ol::-webkit-scrollbar { display: none; }                   /* hide it in WebKit / iOS WKWebView */
  .lb li {
    display: flex; align-items: center; gap: var(--sp-3);
    /* Fixed row height (box-sizing: border-box is global) keeps a row the same
       height in every browser/font, so 7 rows is exactly the 245px ol window
       above — the panel can't differ between tabs by a few stray pixels. */
    height: 35px;
    padding: var(--sp-2) var(--sp-1); border-bottom: 1px solid var(--line);
    font-size: var(--fs-md);
  }
  .lb li:last-child { border-bottom: none; }
  .lb .pos { width: var(--icon-row); color: var(--muted); font-variant-numeric: tabular-nums; font-weight: 700; }
  .lb .who { flex: 1; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .lb .r-car, .recent-list .r-car { width: var(--icon-row); height: var(--icon-row); flex: 0 0 auto; }
  /* Featured stats: 2 prominent cards (Games / Played) centered under the hero. */
  .profile-featured { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); width: 100%; margin: var(--sp-3) 0 var(--sp-4); }
  .profile-featured .feat { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-2); text-align: center;
    background: linear-gradient(160deg, color-mix(in srgb, var(--accent-2) 9%, var(--card)), var(--card)); }
  .profile-featured .feat .v { font-size: var(--fs-lg); font-weight: 800; line-height: 1.1; }
  .profile-featured .feat .v.vi { color: var(--accent-2); }
  .profile-featured .feat .k { font-size: var(--fs-2xs); opacity: .6; text-transform: uppercase; letter-spacing: .5px; margin-top: var(--sp-1); }
  #profile-lifetime { display: flex; flex-direction: column; gap: var(--sp-2); }
  /* Lifetime/records stats — one framed "panel": cells tile flush, separated by
     hairline dividers drawn by the 1px grid gap showing the container's --line bg.
     overflow:hidden clips the cell corners to the panel radius. */
  .profile-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; width: 100%; margin: var(--sp-3) 0;
    background: var(--line); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden;
    box-shadow: 0 1px 0 color-mix(in srgb, var(--ink) 4%, transparent); }
  .profile-grid.cols3 { grid-template-columns: 1fr 1fr 1fr; }
  #profile-lifetime .profile-grid { margin: 0; }
  .profile-grid .stat { border-radius: 0; padding: var(--sp-3); text-align: left;
    background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 3%, var(--card)), var(--card)); }
  .profile-grid .stat.wide { grid-column: 1 / -1; }
  .profile-grid .stat .v { font-size: var(--fs-xl); font-weight: 700; }
  .profile-grid .stat .k { font-size: var(--fs-xs); opacity: 0.7; }
  /* SVG stat icon (STAT_ICONS) inside a profile stat cell — colored per stat via
     an inline color on .stat-ic, picked up by currentColor in the SVG. Only the
     icon cells flex; plain (emoji) statCell cells in the same grid stay block. */
  .profile-grid .stat:has(.stat-ic) { display: flex; align-items: center; gap: var(--sp-2); }
  .profile-grid .stat .stat-ic { width: 18px; height: 18px; flex: 0 0 18px; }
  .profile-grid .stat .stat-ic svg { width: 18px; height: 18px; display: block; }
  .profile-grid .stat .stat-txt { min-width: 0; }
  .profile-section-label { align-self: flex-start; font-size: var(--fs-xs); opacity: 0.55; margin: var(--sp-3) 0 var(--sp-1); text-transform: uppercase; letter-spacing: .6px; font-weight: 600; }
  /* Top-level section header: a clear delimiter (full-width hairline + accent bar). */
  .profile-section-head { align-self: stretch; display: flex; align-items: center; gap: var(--sp-3);
    font-size: var(--fs-sm); font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase;
    color: var(--ink); margin: var(--sp-6) 0 var(--sp-3); padding-bottom: var(--sp-2); border-bottom: 1px solid var(--line); }
  .profile-section-head::before { content: ""; width: var(--sp-1); height: 15px; border-radius: var(--r-xs); background: var(--accent-2); flex: 0 0 auto; }
  .profile-name { width: 100%; margin: var(--sp-1) 0 var(--sp-3); text-align: center; }
  .profile-history { list-style: none; padding: 0; margin: 0; width: 100%; }
  /* Keep the tall summary popup inside the safe-inset box the .overlay pads to,
     scrolling internally on short screens. #profile is a full document scroll
     (see #profile rules), so it's deliberately excluded here. */
  #game-summary .panel { max-height: 100%; overflow-y: auto; }
  .profile-history li { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) var(--sp-1); border-bottom: 1px solid var(--line); font-size: var(--fs-sm); }
  .profile-history li .h-score { font-weight: 700; min-width: 4.5em; }
  .profile-history li .h-when { opacity: 0.6; font-size: var(--fs-sm); margin-left: auto; }
  .profile-history li.empty { justify-content: center; opacity: 0.6; border: 0; }
  .profile-history li .h-car { width: var(--icon-row); height: var(--icon-row); flex: 0 0 auto; vertical-align: middle; }
  /* history is a centred row (not a name/chip column) — keep the pill vertically centred */
  .profile-history li .r-loc { align-self: center; font-size: 0.55rem; padding: 1px 5px; }

  /* Hero "main ride" bay — mirrors the garage Neon Violet showroom: accent scoped
     here, a top key-light over a dark sweep-floor, the car parked under it.
     Canvas car bodies stay red (painted in JS), exactly like the garage. */
  .profile-hero { --accent: var(--accent-2);
    position: relative; display: flex; flex-direction: column; align-items: center;
    gap: var(--sp-3); width: 100%; padding: var(--sp-5) var(--sp-3) var(--sp-4); margin: var(--sp-1) 0 var(--sp-3); border-radius: var(--r-lg); overflow: hidden;
    background:
      radial-gradient(120% 62% at 50% 6%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 60%),
      radial-gradient(80% 80% at 50% 120%, color-mix(in srgb, var(--accent) 7%, transparent), transparent 55%),
      linear-gradient(180deg, var(--shadow), color-mix(in srgb, var(--ink) 6%, var(--shadow))); }
  .profile-hero img.hero-car { width: 92px; height: 92px; display: block;
    filter: drop-shadow(0 6px 10px color-mix(in srgb, var(--accent) 30%, transparent)); }
  .profile-hero .hero-name { display: flex; align-items: center; justify-content: center; gap: var(--sp-2); font-size: var(--fs-xl); font-weight: 800; }
  .profile-hero .hero-name[hidden] { display: none; }
  .profile-hero #hero-name-text.empty { opacity: .5; font-weight: 600; font-style: italic; }
  .profile-hero .hero-pen { background: transparent; border: 1px solid var(--line); border-radius: var(--r-sm);
    width: auto; height: auto; margin: 0; padding: var(--sp-1) var(--sp-2); font-size: var(--fs-xs); line-height: 1; opacity: .6; cursor: pointer; }
  /* "Set your name" nudge: pulsing accent dot on the rename pencil + accent hint,
     shown once after 10 games to still-default ("anon") players (see screens.js). */
  .profile-hero .hero-pen.nudge { opacity: 1; border-color: var(--accent); color: var(--accent); position: relative; }
  .profile-hero .hero-pen.nudge::after { content: ""; position: absolute; top: -4px; right: -4px;
    width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
    box-shadow: 0 0 6px color-mix(in srgb, var(--accent) 80%, transparent);
    animation: nameNudgePulse 1.4s ease-in-out infinite; }
  @keyframes nameNudgePulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.5); opacity: .55; } }
  .profile-hero .hero-name-input { width: auto; max-width: 190px; margin: 0; padding: var(--sp-2) var(--sp-3); font-size: var(--fs-md); font-weight: 700; text-align: center; }
  .profile-hero .hero-name-input[hidden] { display: none; }
  .profile-hero .ride-pill { display: inline-flex; align-items: center; gap: var(--sp-2);
    padding: var(--sp-1) var(--sp-3); border-radius: var(--r-pill); font-size: var(--fs-xs); font-weight: 700; letter-spacing: .6px;
    color: var(--accent); border: 1px solid var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
  .profile-hero .ride-pill .lbl { opacity: .65; text-transform: uppercase; font-size: var(--fs-2xs); letter-spacing: .8px; }
  .profile-hero .ride-pill b { color: var(--accent); font-weight: 800; }
  .profile-hero .ride-pill .runs { color: inherit; opacity: .65; font-weight: 600; }

  /* Loading skeletons: full layout paints immediately, each slot shimmers (car
     preview shows a spinner) until the fetch resolves. */
  .skel { position: relative; overflow: hidden; border-radius: var(--r-sm); background: color-mix(in srgb, var(--line) 65%, transparent); }
  .skel::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ink) 9%, transparent), transparent);
    animation: skelShimmer 1.2s ease-in-out infinite; }
  @keyframes skelShimmer { 100% { transform: translateX(100%); } }
  .skel-pill { display: inline-block; width: 150px; height: 22px; border-radius: var(--r-pill); }
  .profile-hero .hero-car.skel-wrap { display: flex; align-items: center; justify-content: center; filter: none; }
  .spinner { width: 30px; height: 30px; border-radius: var(--r-circle);
    border: 3px solid color-mix(in srgb, var(--ink) 16%, transparent); border-top-color: var(--accent-2); animation: spin .8s linear infinite; }
  @keyframes spin { to { transform: rotate(360deg); } }
  @media (prefers-reduced-motion: reduce) { .skel::after, .spinner { animation: none; } }
  .profile-history li.skel-row { gap: 8px; }
  .profile-grid .stat.clickable, .profile-history li.clickable, .lb li.clickable { cursor: pointer; }
  .profile-grid .stat.clickable:active, .profile-history li.clickable:active, .lb li.clickable:active { opacity: .7; }
  .profile-grid .stat.clickable .k::after { content: " ›"; opacity: .6; }
  #profile-title { display: inline-flex; align-items: center; gap: 8px; }
  .set-title .plat { display: inline-flex; width: 18px; vertical-align: middle; margin-left: 8px; color: var(--muted); }
  .set-title .plat svg { width: 15px; height: 15px; }
  /* Score rarity tiers — the whole number is tinted; top tiers read as mythical. */
  .score-t-k  { color: var(--t-k); }
  .score-t-m  { color: var(--t-m); }
  .score-t-b  { color: var(--t-b); }
  .score-t-t  { color: var(--t-t); }
  .score-t-q  { color: var(--t-q); }
  .score-t-qi { color: var(--t-qi); }
  .score-t-sx { color: var(--t-sx); }
  .score-t-sp { color: var(--t-sp); }
  [data-theme="dark"] .score-t-sp { text-shadow: 0 0 6px rgba(255,210,74,.7); }
  /* Unbounded letter range (past Sp): hue ramps with magnitude via inline
     --score-hue. Brighter + glowing in dark theme, like the Sp gold cap. */
  .score-letter { color: hsl(var(--score-hue, 64) 85% 38%); }
  [data-theme="dark"] .score-letter {
    color: hsl(var(--score-hue, 64) 90% 62%);
    text-shadow: 0 0 6px hsl(var(--score-hue, 64) 90% 62% / .6);
  }
  .lb .pts { font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 700; }
  .lb li.me { color: var(--accent); }
  .lb li.me .pos, .lb li.me .pts, .lb li.me .when { color: var(--accent); }
  /* Rank-move spotlight: the caller's row slides old→new slot (transform set
     inline by flipMeRow). Lift it above neighbours + a fading accent glow so the
     travel reads. position:relative makes z-index apply; transform is inline. */
  .lb li.me.lb-moving {
    position: relative; z-index: 3;
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent),
                0 6px 18px color-mix(in srgb, var(--accent) 35%, transparent);
    border-radius: var(--r-sm, 6px);
  }
  .lb .plat {
    width: 16px; flex: none; color: var(--muted);
    display: inline-flex; align-items: center; justify-content: center;
  }
  .lb .plat svg { width: 14px; height: 14px; display: block; }
  .lb li.me .plat { color: var(--accent); }
  .lb .empty { color: var(--muted); font-size: var(--fs-md); text-align: center; padding: var(--sp-2); }
  .lb ol.loading { display: flex; align-items: center; justify-content: center; } /* center the spinner in the full list height */
  .lb-spin {
    width: 15px; height: 15px; border-radius: var(--r-circle);
    border: 2px solid var(--line); border-top-color: var(--ink);
    animation: spin .7s linear infinite;
  }
  @keyframes spin { to { transform: rotate(360deg); } }
  @media (prefers-reduced-motion: reduce) { .lb-spin { animation-duration: 1.6s; } }
  /* Segmented control — full-width, rounded-rect to match the app's button
     language (12px radius) and the recessed --bg surface used by stat cards. */
  .lb-tabs {
    display: flex; width: 100%; margin: 0;
    padding: var(--sp-1); gap: var(--sp-1);
    background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-lg);
  }
  .lb-tab {
    flex: 1; text-align: center; margin: 0; /* override global button margin-top */
    background: none; border: none; cursor: pointer;
    font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 2px; font-weight: 700;
    color: var(--muted); padding: var(--sp-2) 0; border-radius: var(--r-md);
    -webkit-tap-highlight-color: transparent;
    transition: color .2s ease, background-color .2s ease, box-shadow .2s ease;
  }
  .lb-tab:hover { color: var(--ink); }
  .lb-tab.active, .lb-tab.active:hover {
    color: #fff; background: var(--accent);
    box-shadow: 0 2px 8px -2px var(--accent);
  }
  /* Time-window pill under the board tabs. Drives Top + Lowest; hidden on Recent. */
  .lb-window { display: flex; gap: var(--sp-1); margin: 0; }
  .lb-win {
    flex: 1; padding: var(--sp-1) var(--sp-1); font-size: var(--fs-xs); font-weight: 700;
    border: 1px solid var(--line);
    background: var(--bg); color: var(--muted);
    border-radius: var(--r-pill); cursor: pointer;
  }
  .lb-win.active { color: var(--accent); border-color: var(--accent); }
  .lb-window.disabled { display: none; }
  .lb ol.off { display: none; }
  .lb .when { color: var(--muted); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; white-space: nowrap; }

  .hint { color: var(--muted); font-size: var(--fs-sm); margin-top: var(--sp-5); }
  .hint.nudge { color: var(--accent); font-weight: 700; }  /* 10-games "set your name" nudge */
  /* Score queued for retry — reassuring amber, not an alarming error red. */
  #save-hint.offline { color: var(--notice); }
  #save-hint.offline::before { content: "🟡 "; }
  /* Save line is silent on success — collapse it so it claims no header space. */
  #save-hint:empty { display: none; }
  /* Tips feed: tappable rotating hint above Play */
  .tips-feed {
    display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
    cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent;
    transition: color .18s ease;
  }
  /* tips sit just above Play (outside menu-body) so the body's empty middle
     lets the idle car show centered */
  #menu > .panel > .tips-feed { flex: 0 0 auto; margin-top: var(--sp-3); }
  .tips-feed:hover, .tips-feed:focus-visible { color: var(--ink); outline: none; }
  .tips-feed .tip-bulb { font-size: var(--fs-xs); opacity: .85; flex: 0 0 auto; }
  .tips-feed .tip-text { min-width: 0; }
  .tips-feed .tip-next {
    font-size: var(--fs-md); line-height: 1; opacity: .4; flex: 0 0 auto;
    transition: transform .18s ease, opacity .18s ease;
  }
  .tips-feed:hover .tip-next, .tips-feed:focus-visible .tip-next { opacity: .9; transform: translateX(2px); }
  @keyframes tipSwap { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
  .tips-feed.swap .tip-text { animation: tipSwap .28s ease; }
  @media (prefers-reduced-motion: reduce) { .tips-feed.swap .tip-text { animation: none; } }
  /* Lobby tips feed hidden for now (JS still cycles it; just not shown). */
  #tips-feed { display: none; }
  /* --- Level-up choice cards --- */
  #levelup { pointer-events: auto; background: rgba(8,8,14,0.62); backdrop-filter: blur(2px); }
  #levelup.hidden { display: none; }
  .lvl-wrap { margin: auto; display: flex; flex-direction: column; align-items: center; gap: var(--sp-5);
    padding: 0 var(--sp-4); max-width: 720px; width: 100%; }
  .lvl-title { font: 800 var(--fs-2xl) ui-sans-serif, system-ui, sans-serif; letter-spacing: 0.12em;
    color: var(--xp, #9dff3d); text-shadow: 0 0 9px rgba(157,255,61,0.3); }
  /* Vertical stack: one card per row, icon on the left, text block on the right. */
  .lvl-cards { display: flex; flex-direction: column; gap: var(--sp-3); width: 100%; align-items: stretch; }
  .lvl-card { width: 100%; max-width: 420px; min-height: 78px; margin: 0 auto; cursor: pointer; user-select: none;
    background: rgba(20,20,30,0.92); border: 2px solid var(--cardc, #888); border-radius: var(--r-lg);
    padding: var(--sp-4) var(--sp-4); display: grid; grid-template-columns: 48px 1fr auto;
    grid-template-areas: "ico name level" "ico desc level"; column-gap: var(--sp-4); row-gap: var(--sp-1);
    align-items: center; align-content: center;
    box-shadow: 0 0 11px -10px var(--cardc, #888); transition: transform .08s ease, box-shadow .12s ease; }
  .lvl-card > :nth-child(1) { grid-area: ico; align-self: center; }
  .lvl-card > :nth-child(2) { grid-area: name; }
  .lvl-card > :nth-child(3) { grid-area: level; align-self: center; }
  .lvl-card > :nth-child(4) { grid-area: desc; }
  .lvl-card:active { transform: scale(0.96); }
  .lvl-ico { font-size: 34px; line-height: 1; color: var(--cardc, #fff);
    text-shadow: 0 0 8px color-mix(in srgb, var(--cardc, #fff) 55%, transparent); display: flex; align-items: center; justify-content: center; }
  .lvl-name { font: 800 var(--fs-lg) ui-sans-serif, system-ui, sans-serif; color: #fff; text-align: left;
    justify-self: start; line-height: 1.2; }
  .lvl-level { font: 800 var(--fs-sm) ui-sans-serif, system-ui, sans-serif; color: var(--cardc, #fff);
    letter-spacing: 0.04em; padding: var(--sp-1) var(--sp-3); border-radius: var(--r-pill); justify-self: end;
    background: color-mix(in srgb, var(--cardc, #888) 18%, transparent);
    border: 1px solid color-mix(in srgb, var(--cardc, #888) 45%, transparent); white-space: nowrap; }
  .lvl-desc { font: 600 var(--fs-sm) ui-sans-serif, system-ui, sans-serif; color: rgba(255,255,255,0.7);
    text-align: left; justify-self: start; line-height: 1.3; }
  @media (max-width: 420px) { .lvl-cards { gap: var(--sp-2); } }

  /* --- Rewarded continue popup — synthwave magenta on the native dark card --- */
  /* Native DRIFT panel (var(--card)/--line/--ink), the game's button system + system
     font; magenta→purple synthwave accent on the CTA, matching the grid/tier palette.
     Scrim owns pointer events (base .overlay is none) so a tap that misses the panel
     can't leak to canvas steering and re-anchor the joystick. */
  /* Translucent scrim (not opaque var(--bg)) so the frozen crash frame + HUD stay
     visible behind the popup — player reads their run/score to decide on the ad. */
  #continue { pointer-events: auto; background: rgba(8,8,14,0.62); backdrop-filter: blur(2px); }
  /* Flex column so `gap` applies (base .panel is block). Otherwise inherits .panel
     (var(--card) bg, var(--line) border, radius 20). */
  .continue-panel { display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); max-width: 340px; }
  .continue-panel button { margin-top: 0; } /* gap owns the spacing, not button's default margin */
  .continue-eyebrow { font-size: var(--fs-sm); font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: #f040cc; }
  .continue-title { font-size: var(--fs-3xl); font-weight: 800; line-height: 1.18; color: var(--ink); margin-bottom: var(--sp-1); }
  /* CTA: purple→magenta synthwave gradient, white label. Overrides the base primary button. */
  .continue-watch { display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
    background: linear-gradient(92deg, #9060ff, #f040cc); color: #fff; font-weight: 800;
    box-shadow: 0 8px 24px -10px rgba(240,64,204,0.65); }
  .continue-watch .cw-label { line-height: 1; white-space: nowrap; }
  /* Reward chip: the bundled deal — dark tag, reads against the bright gradient. */
  .continue-watch .cw-reward { display: inline-flex; align-items: center; gap: var(--sp-1);
    background: rgba(8,6,16,0.32); color: #fff; font-weight: 800; font-size: var(--fs-sm);
    padding: var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-2); border-radius: var(--r-pill); }
  .continue-watch .cw-reward svg { width: 15px; height: 15px; }
  /* Balance chip on the revive offer — mirrors the lobby coin pill so the player reads
     their wallet before buying. Pinned top-right over the scrim (same inset as the
     overlay padding); informational only — no garage tap, no pointer events. */
  #continue .continue-coins-chip { position: absolute; margin: 0; cursor: pointer; pointer-events: auto;
    top: max(24px, env(safe-area-inset-top)); right: max(24px, env(safe-area-inset-right)); }
  /* Tap the balance to draw the eye to it — a quick shake nudge when you're short. */
  #continue .continue-coins-chip.shake { animation: continue-coin-shake .42s cubic-bezier(.36,.07,.19,.97); }
  @keyframes continue-coin-shake {
    10%, 90% { transform: translateX(-1px); }
    20%, 80% { transform: translateX(2px); }
    30%, 50%, 70% { transform: translateX(-4px); }
    40%, 60% { transform: translateX(4px); }
  }
  /* Can't-afford state: strip the bright gradient, mute the label + reward chip so
     the disabled button reads as unavailable. */
  .continue-watch.broke { background: var(--card); color: var(--muted);
    box-shadow: none; border: 1px solid var(--line); cursor: pointer; }
  .continue-watch.broke .cw-reward { opacity: .5; }
  /* Can't-afford: the price NUMBER goes danger-red, but the ◉ glyph stays gold (.cw-coin
     overrides the red inherited from .cw-cost). Affordable state keeps its white-on-CTA look. */
  .continue-watch.broke .cw-cost { color: #ff5d5d; font-weight: 800; }
  .continue-watch.broke .cw-coin { color: #ffd84d; }
  /* Decline uses the .ghost button (transparent / muted / line border). */

  /* --- Settings popup --- */
  .set-title { font-size: var(--fs-3xl); font-weight: 800; letter-spacing: 2px; margin-bottom: var(--sp-2); }
  .set-title.clickable { cursor: pointer; }
  .set-title .title-chev { opacity: .5; margin-left: var(--sp-2); font-weight: 400; }
  .lb-loc { font-size: .62em; opacity: .7; font-weight: 600; letter-spacing: .02em; }
  .joy-preview { width: 140px; height: 140px; margin: var(--sp-2) auto var(--sp-4); display: block; }
  .set-row { text-align: left; margin-top: var(--sp-5); }
  .set-row-head { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-3); }
  .set-label { font-weight: 700; font-size: var(--fs-md); }
  .set-readout { margin-left: auto; color: var(--muted); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
  .set-info {
    width: 18px; height: 18px; flex: 0 0 18px; margin: 0; padding: 0;
    font-size: var(--fs-xs); font-weight: 700; line-height: 1;
    background: transparent; color: var(--muted);
    border: 1px solid var(--line); border-radius: var(--r-circle);
  }
  .set-info:hover { transform: none; }
  .set-tip { color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; margin-top: var(--sp-3); }
  /* On/Off switch button: sits in the readout slot, pressed = accent fill. */
  .set-switch {
    margin: 0 0 0 auto; padding: var(--sp-1) var(--sp-4); min-width: 56px;
    font-size: var(--fs-sm); font-weight: 700; line-height: 1;
    background: transparent; color: var(--muted);
    border: 1px solid var(--line); border-radius: var(--r-pill);
  }
  .set-switch:hover { transform: none; }
  .set-switch[aria-pressed="true"] {
    background: var(--accent); color: #fff; border-color: var(--accent);
  }
  /* Range slider: override the global input{} rule (padding/border/margin/center). */
  .set-range {
    -webkit-appearance: none; appearance: none;
    width: 100%; height: 4px; margin: 0; padding: 0;
    background: var(--line); border: none; border-radius: var(--r-pill); outline: none;
  }
  .set-range:focus { border: none; }
  .set-range::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: 20px; height: 20px; border-radius: var(--r-circle);
    background: var(--accent); cursor: pointer; border: none;
  }
  .set-range::-moz-range-thumb {
    width: 20px; height: 20px; border-radius: var(--r-circle);
    background: var(--accent); cursor: pointer; border: none;
  }

  /* Feedback panel — chip row, textarea, error, actions. */
  .fb-sub { color: var(--muted); font-size: var(--fs-sm); margin: var(--sp-3) 0; }
  #feedback-cats { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: var(--sp-3); }
  .fb-chip { display: inline-block; padding: 7px 13px; border-radius: 999px; border: 1px solid var(--line); background: transparent; color: var(--ink); cursor: pointer; font-size: var(--fs-sm); }
  .fb-chip.sel { background: var(--accent); border-color: var(--accent); color: #fff; }
  #feedback-text { width: 100%; box-sizing: border-box; margin-top: var(--sp-2); padding: var(--sp-3); border-radius: var(--r-md); border: 1px solid var(--line); background: var(--card); color: var(--ink); font-size: var(--fs-sm); resize: none; }
  .fb-err { color: #ff8a8a; min-height: 1em; font-size: var(--fs-sm); margin-top: var(--sp-2); }
  .fb-actions { display: flex; gap: var(--sp-3); margin-top: var(--sp-4); justify-content: flex-end; }
  .fb-actions button { min-width: 80px; }

  /* Runtime debug HUD — fixed top-left overlay, drift + buzz live state. Toggled
     from Settings (drift_debug=on). Pointer-transparent so it can't eat touches. */
  #debug-hud {
    position: fixed;
    top: max(env(safe-area-inset-top, 0px), 8px);
    left: max(env(safe-area-inset-left, 0px), 8px);
    z-index: 1000;
    pointer-events: none;
    font: 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
    color: #ececf1;
    background: rgba(14, 14, 18, 0.78);
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 8px;
    padding: 7px 9px;
    min-width: 220px;
    letter-spacing: 0.02em;
    text-shadow: 0 1px 1px rgba(0, 0, 0, 0.6);
  }
  #debug-hud[hidden] { display: none; }
  #debug-hud .dbg-section {
    font-weight: 700;
    font-size: 9px;
    letter-spacing: 0.14em;
    color: #8a8a93;
    margin-top: 6px;
  }
  #debug-hud .dbg-section:first-child { margin-top: 0; }
  #debug-hud .dbg-row {
    display: grid;
    grid-template-columns: 52px 1fr 64px;
    align-items: center;
    gap: 6px;
    margin-top: 3px;
  }
  #debug-hud .dbg-text { color: #aab; margin-top: 3px; font-size: 10px; }
  #debug-hud .dbg-k { color: #8a8a93; }
  #debug-hud .dbg-v { text-align: right; font-variant-numeric: tabular-nums; }
  #debug-hud .dbg-bar {
    position: relative;
    height: 8px;
    background: rgba(255, 255, 255, 0.08);
    border-radius: 2px;
    overflow: hidden;
  }
  #debug-hud .dbg-fill {
    position: absolute; inset: 0 auto 0 0;
    width: 0%;
    background: var(--accent);
  }
  #debug-hud .dbg-bar.deep .dbg-fill { background: #2ecf72; }
  /* small tick at the deep-angle threshold so you can see when slip crosses it */
  #debug-hud .dbg-mark {
    position: absolute; top: -1px; bottom: -1px;
    width: 1px;
    background: rgba(255, 255, 255, 0.55);
  }
  /* Screenshot-mode helper: hide non-essential HUD chrome for clean marketing
     captures. Toggled by window.hideHUDExceptScore(). Score stays visible. */
  body.screenshot-clean #debug-hud,
  body.screenshot-clean .hud-secondary { display: none !important; }

  /* ---- Garage ---- */
  /* Modal popup over the (still-visible) main menu: dim scrim + tap-outside to
     close. The overlay is normally transparent/pointer-through; #garage opts into
     a backdrop so it floats on top of #menu instead of replacing it. */
  /* Full screen, like #menu / #profile: an opaque screen (not a card over a
     scrim) that fully covers the menu. The panel is a transparent column filling
     the safe-area box; the slot grid takes the leftover height and scrolls. */
  /* Garage-scoped accent: the showroom runs a Neon Violet accent independent of the
     app's theme red. Overriding --accent here cascades to every var(--accent) in the
     garage UI (glow, pills, gauges, rail highlight, ✓ badge, progress). Canvas car
     bodies are painted in cars.js and don't read this. */
  #garage { background: var(--bg); pointer-events: auto; --accent: var(--accent-2); }
  #garage .panel { width: 100%; height: 100%; max-width: none; padding: 0;
                   border: 0; border-radius: 0; box-shadow: none;
                   background: transparent; display: flex; flex-direction: column; }
  /* Shared screen header — garage, leaderboard, upgrades. Title left, optional meta
     + close ✕ grouped right, all on one vertically-centered row above a hairline
     divider. The base .set-title (26px, bottom margin) is too tall/loose for a top
     bar, so it's tuned down here. */
  .screen-head { display: flex; align-items: center; gap: var(--sp-3); width: 100%; flex: 0 0 auto;
                 padding-bottom: var(--sp-3); margin-bottom: var(--sp-4); border-bottom: 1px solid var(--line); }
  .screen-head .set-title { margin: 0; margin-right: auto; font-size: var(--fs-2xl); font-weight: 800;
                            letter-spacing: 1.2px; line-height: 1; }
  /* kill any inherited button margin (e.g. the stacked-button margin-top) so the ✕
     sits centered on the row instead of being shoved down by its own margin box. */
  .screen-head .garage-x { margin: 0; }
  .garage-x { background: transparent; border: 1px solid var(--line); border-radius: var(--r-sm);
              width: 30px; height: 30px; color: inherit; opacity: .6; cursor: pointer;
              display: flex; align-items: center; justify-content: center;
              padding: 0; line-height: 1; font-size: var(--fs-md); }
  /* Scroll column: everything between the fixed header and Done scrolls as one
     unit (banner, blurb, paint strip, category sections). */
  .garage-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; width: 100%;
                   display: flex; flex-direction: column;
                   -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .garage-scroll::-webkit-scrollbar { display: none; }
  /* Banner: a slim showcase strip — small car preview left under the studio
     wash, identity + spec bars + one action right. A one-shot light-sweep plays
     when the displayed car changes (.swept toggled by garage.js). */
  /* height is FIXED so the banner never jumps between states (locked progress /
     Buy pill / Customize, spec bars vs paint strip). */
  .garage-stage { display: flex; align-items: center; gap: var(--sp-4); flex: 0 0 auto;
                  position: relative; overflow: hidden; height: 152px;
                  background:
                    radial-gradient(120% 90% at 18% 0%, color-mix(in srgb, var(--accent) 20%, transparent), transparent 60%),
                    linear-gradient(180deg, var(--shadow), color-mix(in srgb, var(--ink) 6%, var(--shadow)));
                  border-radius: var(--r-lg); padding: var(--sp-3); margin: var(--sp-3) 0 0; width: 100%; }
  /* light-sweep: a soft diagonal band that crosses the bay once on car change */
  .garage-stage::after { content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2;
                  background: linear-gradient(115deg, transparent 38%,
                    color-mix(in srgb, var(--accent) 26%, transparent) 50%, transparent 62%);
                  transform: translateX(-120%); opacity: 0; }
  .garage-stage.swept::after { animation: garageSweep .55s ease-out; }
  @keyframes garageSweep { 0% { transform: translateX(-120%); opacity: .9; }
                           100% { transform: translateX(120%); opacity: 0; } }
  .car-preview { width: 132px; height: 64px; flex: 0 0 auto; position: relative; z-index: 1; }
  .garage-stage.swept .car-preview { animation: garageDrive .45s cubic-bezier(.2,.8,.3,1); }
  @keyframes garageDrive { 0% { transform: translateX(28px); opacity: 0; }
                           100% { transform: translateX(0); opacity: 1; } }
  @media (prefers-reduced-motion: reduce) {
    .garage-stage.swept::after, .garage-stage.swept .car-preview { animation: none; }
  }
  .gb-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
  .gb-title { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
  .garage-name { font-size: var(--fs-lg); font-weight: 800; letter-spacing: .2px;
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .garage-class { display: inline-block; flex: 0 0 auto; padding: 1px var(--sp-2); border-radius: var(--r-pill);
                  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .6px; text-transform: uppercase;
                  color: var(--accent); border: 1px solid var(--accent);
                  background: color-mix(in srgb, var(--accent) 12%, transparent); }
  .garage-blurb { font-size: var(--fs-xs); opacity: .6; font-style: italic; margin-top: var(--sp-2);
                  text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* Spec bars — 4 slim label+gauge rows from real CARS.handling. Shares its
     banner slot with the paint strip (Customize swaps them); fixed height so
     the swap is jump-free. */
  .garage-specs { display: flex; flex-direction: column; gap: 3px; width: 100%; height: 58px;
                  justify-content: center; }
  .garage-specs[hidden] { display: none; } /* explicit display above defeats the bare [hidden] UA rule */
  .garage-specs.dim { opacity: .35; }
  .spec { display: flex; align-items: center; gap: var(--sp-2); }
  .spec-l { flex: 0 0 40px; font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .8px; text-transform: uppercase; opacity: .55; }
  .spec-bar { flex: 1 1 auto; height: 4px; border-radius: var(--r-xs); background: color-mix(in srgb, var(--line) 70%, transparent);
              overflow: hidden; }
  .spec-bar i { display: block; height: 100%; border-radius: var(--r-xs); background: var(--accent);
                transition: width .35s cubic-bezier(.2,.8,.3,1); }
  /* Paint booth — coin-shop body colors. A snap-scrolling row of circular
     swatches with a studio-light sheen, collapsed behind the banner's Paint
     button. Locked swatches carry a small gold price tag; the equipped one gets
     the accent ring + ✓ (slot-check kin). The gold wallet chip lives in the
     screen header. */
  .gp-wallet { font-size: var(--fs-xs); font-weight: 800; color: #ffd84d;
               font-variant-numeric: tabular-nums; transition: transform .12s ease; }
  .gp-wallet.denied { animation: gp-deny .3s ease 2; }
  @keyframes gp-deny { 50% { transform: translateX(3px); } }
  /* Balance-change pulse: paintCoinChip (shared.js) holds .coin-tick on a wallet
     chip for the whole count-up/down tween — a soft scale pop + brighter gold so
     the eye lands on the moving number. Applies to both chips. */
  .gp-wallet.coin-tick, .menu-coins.coin-tick { animation: coin-tick .65s ease; }
  @keyframes coin-tick { 25% { transform: scale(1.12); filter: brightness(1.35); } }
  .garage-paints { display: flex; gap: var(--sp-3); width: 100%; margin: var(--sp-2) 0 0; flex: 0 0 auto;
                   height: 64px; align-items: center;
                   overflow-x: auto; overflow-y: hidden; padding: 0 2px;
                   /* horizontal-only: lock touch panning to x and keep the strip's
                      scroll from chaining into the vertical page scroll */
                   touch-action: pan-x; overscroll-behavior-x: contain;
                   -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  /* Locked-car state of the open Customize strip — same height as the swatch
     row so browsing locked <-> owned never shifts the layout. */
  .gp-na { flex: 1 1 auto; align-self: stretch; margin: var(--sp-1) 0; display: flex;
           align-items: center; justify-content: center;
           border: 1px dashed var(--line); border-radius: var(--r-md);
           color: var(--muted); font-size: var(--fs-xs); font-weight: 600; }
  .garage-paints::-webkit-scrollbar { display: none; }
  .paint-swatch { position: relative; flex: 0 0 auto; width: 40px; display: flex;
                  flex-direction: column; align-items: center; gap: var(--sp-1);
                  background: none; border: 0; padding: 0; cursor: pointer;
                  -webkit-tap-highlight-color: transparent; }
  .paint-swatch i { display: block; width: 32px; height: 32px; border-radius: var(--r-circle);
                    background: var(--pc, #888);
                    /* studio sheen: a soft top-left highlight over the paint color */
                    background-image: radial-gradient(circle at 32% 28%,
                      rgba(255,255,255,.5), rgba(255,255,255,.08) 45%, rgba(0,0,0,.18) 100%);
                    border: 1px solid var(--line);
                    box-shadow: inset 0 0 0 1px rgba(0,0,0,.12);
                    transition: transform .15s ease, box-shadow .15s ease; }
  .paint-swatch:active i { transform: scale(.92); }
  .paint-swatch.equipped i { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent); }
  .paint-swatch .gp-check { position: absolute; top: -3px; right: 0; width: 15px; height: 15px;
                            border-radius: var(--r-circle); background: var(--accent); color: var(--bg);
                            display: flex; align-items: center; justify-content: center;
                            font-size: 10px; font-weight: 900; line-height: 1;
                            box-shadow: 0 0 0 2px var(--bg); }
  .paint-price { font-size: var(--fs-2xs); font-weight: 800; color: #ffd84d;
                 font-variant-numeric: tabular-nums; white-space: nowrap; }
  .paint-price.cant { color: var(--muted); }
  .paint-swatch.locked i { opacity: .8; }
  /* Freshly-WON paint: a notification dot on the swatch corner (same family as the
     Garage/Customize dots). Cleared on the same open the strip auto-scrolls to it. */
  .paint-swatch .gp-new { position: absolute; top: -3px; right: 0; width: 9px; height: 9px;
                          border-radius: var(--r-circle); background: var(--notif-dot);
                          box-shadow: 0 0 0 2px var(--bg), 0 0 6px color-mix(in srgb, var(--notif-dot) 75%, transparent); }
  /* The strip's display:flex would defeat the `hidden` attribute (author rules
     beat the UA's [hidden]{display:none}) — garage.js hides the booth while the
     browsed car is locked, so pin it explicitly. */
  .garage-paints[hidden] { display: none; }
  /* Double-tap purchase: first tap arms the swatch (gold pulsing ring, 1s
     confirm window), second tap buys. Timing lives in garage.js. */
  .paint-swatch.arm i { animation: paintArm .45s ease-in-out infinite alternate;
                        box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px #ffd84d; }
  @keyframes paintArm { from { transform: scale(1); } to { transform: scale(1.14); } }
  /* Centered quick hint shown when an armed swatch times out unconfirmed. */
  .paint-hint { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
                padding: var(--sp-2) var(--sp-4); border-radius: var(--r-pill);
                background: color-mix(in srgb, var(--bg) 88%, transparent);
                border: 1px solid var(--line); color: var(--ink);
                font-size: var(--fs-sm); font-weight: 900; white-space: nowrap;
                pointer-events: none; z-index: 5; opacity: 0;
                animation: paintHint 1.4s ease forwards; }
  @keyframes paintHint { 0% { opacity: 0; } 12% { opacity: 1; } 72% { opacity: 1; } 100% { opacity: 0; } }
  /* Rainbow swatch disc: the studio sheen layered over the paint's gradient
     (--pc can't hold a gradient — the sheen background-image would override it). */
  .paint-swatch.rainbow i { background-image: radial-gradient(circle at 32% 28%,
                      rgba(255,255,255,.5), rgba(255,255,255,.08) 45%, rgba(0,0,0,.18) 100%),
                    linear-gradient(135deg, #ff3b3b, #ff9f1c, #ffe23b, #2ecc71, #3b8dff, #a55eff); }
  .gp-name { font-size: var(--fs-2xs); font-weight: 700; opacity: .6; max-width: 44px;
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* Category sections — vertical lists of compact car rows, one bordered list
     per category (CAREER / COIN CARS), section labels with owned counts. */
  .garage-sections { width: 100%; flex: 0 0 auto; padding-bottom: var(--sp-3); }
  .gl-head { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .8px; text-transform: uppercase;
             opacity: .8; margin: var(--sp-4) 0 var(--sp-2); }
  .gl-head b { color: var(--accent); font-variant-numeric: tabular-nums; }
  .gl-sub { opacity: .55; text-transform: none; letter-spacing: .2px; font-weight: 600; }
  .gl-list { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden;
             background: color-mix(in srgb, var(--ink) 3%, transparent); }
  .gl-row { position: relative; display: flex; align-items: center; gap: var(--sp-3);
            padding: var(--sp-2) var(--sp-3); cursor: pointer;
            transition: background .18s ease; }
  .gl-row + .gl-row { border-top: 1px solid var(--line); }
  .gl-row canvas { width: 56px; height: 26px; flex: 0 0 auto; }
  .gl-row .gl-name { flex: 1 1 auto; min-width: 0; font-size: var(--fs-sm); font-weight: 700;
                     overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* .browse = currently shown in the banner; .sel = the actually-equipped car */
  .gl-row.browse { background: color-mix(in srgb, var(--accent) 10%, transparent); }
  .gl-row.sel .gl-name { color: var(--accent); }
  .gl-row.locked .gl-name { opacity: .5; }
  .gl-owned { font-size: var(--fs-2xs); font-weight: 700; opacity: .5; letter-spacing: .4px;
              text-transform: uppercase; }
  .gl-price { font-size: var(--fs-xs); font-weight: 800; color: #ffd84d;
              font-variant-numeric: tabular-nums; white-space: nowrap; }
  .gl-price.cant { color: var(--muted); }
  /* Location-gated career car: the district that opens it (colour set inline). */
  .gl-locreq { font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .3px;
               text-transform: uppercase; white-space: nowrap; opacity: .85; }
  /* "More coming soon" placeholder row closing the coin section. */
  .gl-soon { cursor: default; color: var(--muted); }
  .gl-soon .gl-name { font-size: var(--fs-xs); font-weight: 600; opacity: .6; }
  .gl-soon-i { width: 56px; text-align: center; flex: 0 0 auto; opacity: .5; }
  .slot-new { position: absolute; top: var(--sp-2); left: var(--sp-2); width: 8px; height: 8px; border-radius: var(--r-circle);
              background: var(--notif-dot); box-shadow: 0 0 0 2px var(--bg); }
  .rail-prog { width: 56px; flex: 0 0 auto; height: 3px; border-radius: var(--r-xs);
               background: color-mix(in srgb, var(--line) 70%, transparent); overflow: hidden; }
  .rail-prog i { display: block; height: 100%; background: var(--accent); border-radius: var(--r-xs); }
  /* Equipped marker: a ✓ badge on the equipped car's row. */
  .slot-check { flex: 0 0 auto; width: 18px; height: 18px;
                border-radius: var(--r-circle); background: var(--accent); color: var(--bg);
                display: flex; align-items: center; justify-content: center;
                font-size: var(--fs-xs); font-weight: 900; line-height: 1;
                box-shadow: 0 0 0 2px var(--bg); }
  /* Banner action row — Customize toggle, gold Buy pill, or an unlock-progress
     gauge. Fixed height: part of the banner's no-jump contract. */
  .garage-action { width: 100%; height: 28px; display: flex; align-items: center; }
  .ga-paint { height: 26px; padding: 0 var(--sp-4); border-radius: var(--r-pill); cursor: pointer;
              font-size: var(--fs-xs); font-weight: 700; letter-spacing: .6px; text-transform: uppercase;
              color: var(--accent); border: 1px solid var(--accent);
              background: color-mix(in srgb, var(--accent) 12%, transparent); }
  .ga-paint.on { color: var(--bg); background: var(--accent); }
  .ga-buy { height: 26px; padding: 0 var(--sp-4); border-radius: var(--r-pill); cursor: pointer;
            font-size: var(--fs-xs); font-weight: 800; letter-spacing: .4px;
            font-variant-numeric: tabular-nums; white-space: nowrap;
            color: #2b2200; border: 1px solid #ffd84d; background: #ffd84d; }
  .ga-buy.cant { color: var(--muted); background: transparent; border-color: var(--line); }
  /* Double-tap purchase: first tap arms the pill (gold pulse, 1s confirm window),
     second tap buys. Timing lives in garage.js (shared with paint swatches). */
  .ga-buy.arm { animation: paintArm .45s ease-in-out infinite alternate;
                box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px #ffd84d; }
  .ga-locked { width: 100%; }
  .ga-locked-top { display: flex; align-items: center; justify-content: space-between;
                   font-size: var(--fs-2xs); margin-bottom: var(--sp-1); }
  .ga-locked-top b { color: var(--accent); font-variant-numeric: tabular-nums; }
  .ga-locked-bar { height: 6px; border-radius: var(--r-xs); background: var(--shadow);
                   border: 1px solid var(--line); overflow: hidden; }
  .ga-locked-bar i { display: block; height: 100%; background: var(--accent); border-radius: var(--r-xs); }

  /* ---- Store ---- */
  /* Coin-pack shop. Floats over the (still-visible) menu like the Garage, full
     screen with the dim scrim. Its own identity: --accent scoped to coin GOLD (the
     ◉ token is gold), so every accent in here — pack glow, price pill, discs — runs
     warm instead of the app red. Cars + paints stay in the Garage; v1 sells coins. */
  #store { background: var(--bg); pointer-events: auto; --accent: #ffd84d; }
  #store .panel { width: 100%; height: 100%; max-width: none; padding: 0;
                  border: 0; border-radius: 0; box-shadow: none;
                  background: transparent; display: flex; flex-direction: column; }
  .store-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; width: 100%;
                  display: flex; flex-direction: column; gap: var(--sp-3);
                  -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .store-scroll::-webkit-scrollbar { display: none; }
  .store-tagline { text-align: center; font-size: var(--fs-sm); color: var(--muted);
                   font-weight: 600; margin-top: var(--sp-2); }
  .st-token, .sp-token { color: #ffd84d; }
  .store-list { display: flex; flex-direction: column; gap: var(--sp-3); }
  .store-fineprint { text-align: center; font-size: var(--fs-2xs); color: var(--muted);
                     opacity: .7; margin: var(--sp-1) 0 var(--sp-2); }

  /* Pack card — art (coin pile) left, name + amount middle, gold price pill right.
     A warm radial wash + hairline; the two flagged tiers glow brighter. */
  .store-pack { position: relative; display: flex; align-items: center; gap: var(--sp-3);
                width: 100%; text-align: left; cursor: pointer; overflow: hidden;
                padding: var(--sp-3); border-radius: var(--r-lg);
                border: 1px solid var(--line); color: inherit; margin: 0;
                background:
                  radial-gradient(120% 120% at 12% 0%, color-mix(in srgb, #ffd84d 14%, transparent), transparent 62%),
                  linear-gradient(180deg, var(--shadow), color-mix(in srgb, var(--ink) 6%, var(--shadow)));
                -webkit-tap-highlight-color: transparent;
                transition: transform .12s ease, border-color .18s ease, box-shadow .18s ease;
                animation: storeIn .38s cubic-bezier(.2,.8,.3,1) both;
                animation-delay: calc(var(--i, 0) * 55ms); }
  .store-pack:active { transform: scale(.985); }
  .store-pack:hover { border-color: color-mix(in srgb, #ffd84d 55%, var(--line)); }
  .store-pack.tag-popular { border-color: color-mix(in srgb, #ffd84d 45%, var(--line)); }
  .store-pack.tag-best { border-color: #ffd84d;
                         box-shadow: 0 0 0 1px color-mix(in srgb, #ffd84d 55%, transparent),
                                     0 6px 22px -8px color-mix(in srgb, #ffd84d 45%, transparent); }
  @keyframes storeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

  /* Coin-stack art (inline SVG from store.js) — a side-view gold stack that grows
     taller with the tier. Fixed column so every card's name/amount line up. */
  .sp-art { flex: 0 0 auto; width: 58px; height: 50px; display: flex;
            align-items: center; justify-content: center; }
  .sp-art svg { display: block; filter: drop-shadow(0 2px 3px rgba(0,0,0,.4)); }

  .sp-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  /* Name + optional tag chip on one row — no absolute overlap with the price. */
  .sp-head { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
  .sp-name { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 1px;
             text-transform: uppercase; color: var(--muted); }
  .sp-tag { flex: 0 0 auto; padding: 1px var(--sp-2); border-radius: var(--r-pill);
            font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .4px;
            text-transform: uppercase; line-height: 1.5;
            color: #ffd84d; background: color-mix(in srgb, #ffd84d 14%, transparent);
            border: 1px solid color-mix(in srgb, #ffd84d 55%, transparent); }
  .store-pack.tag-best .sp-tag { color: #1a1205; background: #ffd84d; border-color: #ffd84d; }
  .sp-coins { font-size: var(--fs-xl); font-weight: 800; letter-spacing: .2px;
              font-variant-numeric: tabular-nums; line-height: 1.05; }
  .sp-bonus { align-self: flex-start; margin-top: 2px; padding: 1px var(--sp-2);
              border-radius: var(--r-pill); font-size: var(--fs-2xs); font-weight: 800;
              letter-spacing: .3px; color: #7cf29a;
              background: color-mix(in srgb, #7cf29a 14%, transparent);
              border: 1px solid color-mix(in srgb, #7cf29a 45%, transparent); }
  .sp-buy { flex: 0 0 auto; align-self: center; min-width: 66px; text-align: center;
            padding: var(--sp-2) var(--sp-3); border-radius: var(--r-pill);
            background: #ffd84d; color: #1a1205; font-weight: 800;
            font-size: var(--fs-sm); font-variant-numeric: tabular-nums; line-height: 1; }
  .store-pack.buying { opacity: .7; }
  .store-pack.buying .sp-buy { background: color-mix(in srgb, #ffd84d 40%, var(--line)); color: var(--ink); }

  .store-pack.shake { animation: sp-shake .34s ease; }
  @keyframes sp-shake { 10%,90% { transform: translateX(-2px); } 30%,70% { transform: translateX(4px); }
                        50% { transform: translateX(-5px); } }
  @media (prefers-reduced-motion: reduce) {
    .store-pack { animation: none; }
    .store-pack.shake { animation: none; }
  }

  /* Purchase-in-progress veil — covers the whole store, blocks pointer events, and
     shows a gold coin-ring spinner while a buy()/server-validate is in flight. */
  .store-busy { position: absolute; inset: 0; z-index: 6; display: none;
    align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--bg) 66%, transparent);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    animation: store-busy-in .18s ease both; }
  .store-busy.show { display: flex; }
  @keyframes store-busy-in { from { opacity: 0; } to { opacity: 1; } }
  .store-busy-card { display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
    padding: var(--sp-5) var(--sp-6); border-radius: var(--r-xl); background: var(--card);
    border: 1px solid color-mix(in srgb, #ffd84d 32%, var(--line));
    box-shadow: 0 14px 38px rgba(0,0,0,.5); }
  .store-spinner { width: 36px; height: 36px; border-radius: var(--r-circle);
    border: 3px solid color-mix(in srgb, #ffd84d 18%, transparent);
    border-top-color: #ffd84d; animation: spin .7s linear infinite; }
  .store-busy-label { font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 1px;
    text-transform: uppercase; color: var(--ink); }
  @media (prefers-reduced-motion: reduce) {
    .store-busy { animation: none; }
    .store-spinner { animation: none; opacity: .9; }
  }

  /* Perks page — milestone progression. Floats over the menu with a dim scrim
     (own pointer-events; base .overlay is none). Default centered .panel. */
  #upgrades { background: var(--bg); pointer-events: auto; }
  #upgrades .panel { width: 100%; height: 100%; max-width: none; padding: 0;
                  border: 0; border-radius: 0; box-shadow: none;
                  background: transparent; display: flex; flex-direction: column; }
  .perk-ico { display: inline-flex; color: var(--accent); }
  .perk-ico svg { display: block; }
  .perks-life { font-size: 15px; font-weight: 800; color: var(--accent); margin: 8px 0 4px; text-align: center; }
  /* progress bar tracks ONLY the next reward; its icon is drawn beneath the bar */
  .perks-bar { position: relative; height: 12px; border-radius: 8px; background: var(--shadow);
               border: 1px solid var(--ink); margin: 12px 4px 14px; overflow: hidden; }
  .perks-bar-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 8px; background: var(--accent); overflow: hidden; }
  /* game-over perk-progress widget — single advancing "next reward" bar. This is
     the dopamine hero of the results screen, so it's sized up vs the in-list bars. */
  .over-perk { width: 100%; max-width: 380px; margin: 22px auto 0; position: relative; cursor: pointer; }
  /* tap the widget → a hued speech bubble explaining the current/next perk. Sits
     above the card (its .over-perk parent is overflow:visible) with a down-caret. */
  .perk-tip {
    position: absolute; left: 50%; bottom: calc(100% + 11px);
    transform: translateX(-50%) translateY(6px) scale(.96); transform-origin: bottom center;
    width: max-content; max-width: 264px; padding: 11px 14px; border-radius: 14px;
    text-align: left; z-index: 6; opacity: 0; pointer-events: none;
    background: color-mix(in srgb, var(--perk-hue, var(--accent)) 13%, var(--bg));
    border: 1px solid color-mix(in srgb, var(--perk-hue, var(--accent)) 42%, transparent);
    box-shadow: 0 16px 36px -16px var(--perk-hue, var(--accent));
    transition: opacity .18s ease, transform .2s cubic-bezier(.2,.8,.3,1);
  }
  .over-perk.tip-open .perk-tip {
    opacity: 1; transform: translateX(-50%) translateY(0) scale(1); pointer-events: auto;
  }
  .perk-tip-name { display: block; font-size: 13.5px; font-weight: 800; letter-spacing: .2px;
    color: var(--perk-hue, var(--accent)); margin-bottom: 3px; }
  .perk-tip-desc { display: block; font-size: 12px; line-height: 1.42; color: var(--ink); opacity: .82; }
  .perk-tip::after {
    content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
    border: 7px solid transparent;
    border-top-color: color-mix(in srgb, var(--perk-hue, var(--accent)) 13%, var(--bg));
  }
  .over-perk-top { display: flex; align-items: center; justify-content: center; gap: 8px;
                   font-size: 12.5px; font-weight: 600; color: var(--muted); min-height: 20px; }
  .over-perk-top .perk-ico { color: var(--muted); }
  .over-perk-top .perk-ico svg { width: 16px; height: 16px; }
  .over-perk-bar { position: relative; height: 8px; border-radius: 8px; background: var(--shadow);
                   border: 1px solid var(--line); margin: 7px 0 5px; overflow: hidden; }
  .over-perk-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; border-radius: 8px;
                    background: var(--muted); overflow: hidden; }
  .over-perk-num { font-size: 11.5px; opacity: .65; text-align: center; }
  /* Tournament-points recap (game-over) — fills the next-upgrade slot during a
     live tournament. Azure total echoes the in-run tournament orbs. Reuses the
     .over-perk frame; not tappable, so drop the inherited pointer cursor. */
  .over-tourn { cursor: default; }
  /* Trophy header + metric icons are inline line-SVGs (game/gameover.js), tinted
     the widget's tournament azure so they echo the azure total below. */
  .over-tourn .ott-trophy { display: inline-flex; align-items: center; color: #2f9bff; }
  .over-tourn .ott-trophy svg { width: 15px; height: 15px; display: block; }
  .over-tourn-rows { display: flex; flex-direction: column; gap: 6px; margin: 11px 0 4px; }
  .over-tourn-row { display: flex; align-items: center; gap: 9px; font-size: 13px; }
  .over-tourn-row .ott-ico { width: 18px; flex: 0 0 auto; display: inline-flex;
                             justify-content: center; color: #2f9bff; }
  .over-tourn-row .ott-ico svg { width: 16px; height: 16px; display: block; }
  .over-tourn-row .ott-lbl { flex: 1 1 auto; color: var(--ink); font-weight: 600; }
  .over-tourn-row .ott-mul { color: var(--muted); font-size: 11.5px; font-variant-numeric: tabular-nums; }
  .over-tourn-row .ott-pts { flex: 0 0 auto; min-width: 50px; text-align: right; font-weight: 800;
                             color: var(--ink); font-variant-numeric: tabular-nums; }
  .over-tourn-total { display: flex; align-items: center; justify-content: space-between;
                      margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--line); }
  .over-tourn-total .ott-total-lbl { font-size: 12px; font-weight: 700; color: var(--muted);
                                     text-transform: uppercase; letter-spacing: .5px; }
  .over-tourn-total .ott-total-val { display: inline-flex; align-items: center; gap: 6px;
                                     font-size: 19px; font-weight: 900; color: #2f9bff;
                                     font-variant-numeric: tabular-nums; }
  /* neon (dark theme only) — next perk's family hue + glow. No moving shimmer
     here: the game-over bar width-animates as it fills, which a fill-anchored
     flare can't track cleanly (the Upgrades-screen bar is static, so it keeps its
     shimmer). Hue set on #over-upgrade in animateOverUpgrade. */
  /* steady glow lives on the BAR (its parent .over-perk is overflow:visible), not
     the fill — the fill sits inside the bar's overflow:hidden, which would clip
     its shadow into a hard rectangle. */
  /* frame the widget as the hero card — faint hued tint + border lifts it above
     the (now-smaller) leaderboard as the primary progression point. */
  /* monochrome variant: drop the hued hero-card frame — the widget sits flush as a
     quiet "next reward" line under the score, not a second focal point. */
  [data-theme="dark"] .over-perk {
    padding: 0; border-radius: 0;
    background: none; border: none;
  }
  [data-theme="dark"] .over-perk-bar { background: #16161d; border-color: #2a2a34; }
  [data-theme="dark"] .over-perk-top,
  [data-theme="dark"] .over-perk-top .perk-ico { color: var(--muted); }
  [data-theme="dark"] .over-perk-fill {
    background: color-mix(in srgb, var(--muted) 78%, transparent);
  }
  .over-perk-top { transition: transform 180ms ease-out, color 180ms ease-out; }
  /* Inside the round-summary card the widget is a ROW, not a floating block:
     label left, count right, bar spanning underneath (flex-wrap). ID-scoped so
     these outrank the [data-theme] variants above. */
  #gameover .over-card .over-perk { max-width: none; margin: 0; padding: var(--sp-4) 0;
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    border-top: 1px solid color-mix(in srgb, var(--line) 60%, transparent); } /* the dark-theme `border: none` above would erase the card divider */
  #gameover .over-card .over-perk[hidden] { display: none; } /* display:flex above would beat the UA [hidden] rule */
  #gameover .over-card .over-perk-top { justify-content: flex-start; }
  #gameover .over-card .over-perk-num { font-size: var(--fs-sm); opacity: .8; text-align: right;
    font-variant-numeric: tabular-nums; }
  #gameover .over-card .over-perk-bar { flex: 1 0 100%; margin: var(--sp-3) 0 0; }
  /* Tournament recap speaks the same row grammar as the upgrade widget: trophy
     label left, azure total right, breakdown rows spanning underneath — in the
     slot where the upgrade row puts its bar (flex order, no DOM change). */
  #gameover .over-card .over-tourn .over-tourn-rows { flex: 1 0 100%; order: 1; margin: var(--sp-3) 0 0; }
  #gameover .over-card .over-tourn .over-tourn-total { margin: 0; padding: 0; border: 0; }
  #gameover .over-card .over-tourn .ott-total-lbl { display: none; } /* the header label already names the row */
  .over-perk-bar { transition: transform 200ms ease-out; }
  /* unlock beat: a quiet accent flash on the fill + label — no hued glow, no big scale. */
  .over-perk.pop .over-perk-top { transform: scale(1.04); color: var(--accent); }
  .over-perk.pop .over-perk-fill { background: var(--accent); }
  /* monochrome variant: the upgrade tip drops the hued speech bubble for the neutral
     dark info-tooltip look (matches the perks-recap tip). Scoped to .over-perk so it
     never touches the perks-recap tip or any other .perk-tip user. */
  .over-perk .perk-tip { background: var(--shadow); border-color: var(--ink);
    box-shadow: 0 6px 20px rgba(0,0,0,.35); }
  .over-perk .perk-tip-name { color: var(--ink); }
  .over-perk .perk-tip::after { border-top-color: var(--ink); }
  [data-theme="dark"] .over-perk .perk-tip { background: #15151c; border-color: #2c2c38; }
  [data-theme="dark"] .over-perk .perk-tip::after { border-top-color: #2c2c38; }

  /* --- ROUND SUMMARY (game-over) — style A labeled tiles. The accent colors
     (--sum-*) live at :root so both the summary and the profile stat icons can
     use them; the section icon + tile icons are colored per group, values stay
     --ink. --- */
  .over-eyebrow { font-size: 12px; letter-spacing: .32em; text-transform: uppercase;
    color: var(--muted); margin-bottom: 4px; }
  .sum-groups { width: 100%; max-width: 380px; margin: 22px auto 0; text-align: left; }
  .sum-sec { margin-top: 11px; }
  .sum-sec:first-child { margin-top: 0; }
  .sum-sec-h { display: flex; align-items: center; gap: 8px;
    font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase;
    color: var(--muted); font-weight: 700; margin: 0 0 7px; }
  .sum-sec-h .sum-sec-ic { display: flex; }
  .sum-sec-h .sum-sec-ic svg { width: 13px; height: 13px; display: block; }
  .sum-sec-h .sum-hl { flex: 1; height: 1px; background: var(--line); }
  .sum-row { display: grid; grid-template-columns: 1fr 1fr; gap: 7px 14px; }
  .sum-tile { display: flex; align-items: center; gap: 9px; font-size: 13.5px; color: var(--ink); }
  /* An odd-one-out tile stays in a single column (label + value kept tight) rather
     than spanning the full row, so the value never floats to the far screen edge. */
  .sum-tile .sum-ic { width: 17px; height: 17px; flex: 0 0 17px; }
  .sum-tile .sum-ic svg { width: 17px; height: 17px; display: block; }
  .sum-tile .sum-lbl { color: var(--muted); font-size: 12px; }
  .sum-tile .sum-val { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; }
  /* Personal-best tile: a stat that beat its lifetime max this run. Gold accent on
     the value (and a tinted label) signals the record — no badge needed. */
  .sum-tile.record .sum-val { color: var(--sum-gold); text-shadow: 0 0 9px color-mix(in srgb, var(--sum-gold) 65%, transparent); }
  .sum-tile.record .sum-lbl { color: color-mix(in srgb, var(--sum-gold) 80%, var(--muted)); }
  /* Compact headline (3 stats) — shown when collapsed; one stat per main group. */
  .sum-headline { width: 100%; max-width: 380px; margin: 22px auto 0;
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px 18px; text-align: left; }
  .sum-headline .sum-tile { font-size: 15px; gap: 6px; }
  .sum-headline .sum-tile .sum-lbl { font-size: 13px; }
  .sum-headline .sum-tile .sum-val { margin-left: auto; font-size: 16px; }
  .sum-headline .sum-tile .sum-ic { width: 20px; height: 20px; flex: 0 0 20px; }
  .sum-headline .sum-tile .sum-ic svg { width: 20px; height: 20px; }
  /* Solo Survival row — single Level stat, centered and a touch bigger. */
  .sum-sec.sum-solo .sum-row { display: flex; justify-content: center; }
  .sum-sec.sum-solo .sum-tile { font-size: 16px; gap: 8px; }
  .sum-sec.sum-solo .sum-tile .sum-lbl { font-size: 14px; }
  .sum-sec.sum-solo .sum-tile .sum-val { margin-left: 8px; font-size: 17px; }
  .sum-sec.sum-solo .sum-tile .sum-ic { width: 21px; height: 21px; flex: 0 0 21px; }
  .sum-sec.sum-solo .sum-tile .sum-ic svg { width: 21px; height: 21px; }
  /* Show more / Hide — a quiet text link, not a button. No border or fill. */
  .sum-toggle { display: block; margin: 13px auto 2px; padding: 4px 8px;
    font: inherit; font-size: 12px; letter-spacing: .03em; color: var(--muted);
    background: none; border: 0; cursor: pointer; opacity: .8; transition: opacity .15s, color .15s; }
  .sum-toggle:hover { color: var(--ink); opacity: 1; }
  /* The hero first line is permanent; the detail grid only unfolds below it. */
  #over-summary-scroll .sum-groups { display: none; }
  #over-summary-scroll.stats-expanded .sum-groups { display: block; }
  /* Collapsed: toggle sits right under the hero line ("Show more"). Expanded: it
     drops to the bottom under the unfolded grid ("Hide") — reads as one accordion.
     flex order pushes it past the groups (default order 0) without moving the DOM. */
  #over-summary-scroll.stats-expanded .sum-toggle { order: 1; margin-top: 4px; }
  /* Next-upgrade widget spacing is owned by the spread layout (auto margins on
     #over-upgrade/#over-tourn near #over-summary-scroll) — no fixed top margin. */

  /* --- Game-over: short recent feed (the inline grid lives in the game-summary
     popup; its first row is this run, replacing the old Stats button). --- */
  .over-recent-h { font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase;
    color: var(--muted); font-weight: 700; margin: 16px 0 6px; text-align: left;
    max-width: 380px; margin-left: auto; margin-right: auto; }
  /* Compact list (sizes to ~6 rows; no fixed-height scroller like the menu board). */
  .recent-list { list-style: none; width: 100%; max-width: 380px; margin: 0 auto;
    text-align: left; }
  .recent-list li { display: flex; align-items: center; gap: 10px; padding: 8px 2px;
    border-bottom: 1px solid var(--line); font-size: 13.5px; }
  .recent-list li:last-child { border-bottom: none; }
  /* score+time stay inline on the compact game-over feed (the Recent screen stacks them). */
  .recent-list .r-end { display: contents; }
  .recent-list li.clickable { cursor: pointer; }
  .recent-list li.clickable:active { opacity: .7; }
  .recent-list .r-who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .recent-list .who { min-width: 0; max-width: 100%; font-weight: 600; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }
  /* compact feed: slightly tighter pill so the row height barely grows */
  .recent-list .r-loc { font-size: 0.55rem; padding: 1px 5px; }
  .recent-list .pts { font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 700; }
  .recent-list .when { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums;
    white-space: nowrap; }
  .recent-list li.me { color: var(--accent); }
  .recent-list li.me .pts, .recent-list li.me .when, .recent-list li.me .plat { color: var(--accent); }
  .recent-list li.empty, .recent-list li.loading { color: var(--muted); justify-content: center;
    font-size: 12.5px; }
  /* Skeleton placeholder rows for the game-over feed — same height as real rows so
     the perks recap below doesn't jump while the feed loads (see reserveRecent). */
  .recent-list li.skeleton { pointer-events: none; }
  .recent-list li.skeleton .r-car, .recent-list li.skeleton .who,
  .recent-list li.skeleton .pts, .recent-list li.skeleton .when {
    background: var(--line); border-radius: 6px; color: transparent;
    animation: lb-skel 1.2s ease-in-out infinite; }
  .recent-list li.skeleton .r-car { border-radius: 50%; }
  .recent-list li.skeleton .who { height: 12px; max-width: 130px; }
  .recent-list li.skeleton .pts { height: 12px; width: 46px; }
  .recent-list li.skeleton .when { height: 10px; width: 34px; }
  @keyframes lb-skel { 0%, 100% { opacity: .3; } 50% { opacity: .65; } }
  @media (prefers-reduced-motion: reduce) { .recent-list li.skeleton * { animation: none; } }

  /* --- Game-over: perks-this-run recap. Icon chips in pick order; tap → a hued
     bubble (reuses .perk-tip) above the block with the perk's name + effect. --- */
  /* bottom margin keeps the chip glow clear of the Play-again button below. */
  .over-perks { width: 100%; max-width: 380px; margin: 14px auto 10px; position: relative; }
  /* single horizontal row, left-aligned; scrolls when it overflows. Generous
     vertical padding so the .active chip's glow renders inside the overflow clip
     (overflow-y must be hidden to pair with overflow-x:auto) instead of being cut. */
  .over-perks-row { display: flex; flex-wrap: nowrap; justify-content: flex-start; gap: 8px;
    overflow-x: auto; overflow-y: hidden; padding: 11px 4px 18px; scroll-snap-type: x proximity;
    scrollbar-width: none; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain;
    touch-action: pan-x; } /* claim horizontal pans so the vertical summary scroller doesn't eat them */
  .over-perks-row::-webkit-scrollbar { display: none; }
  .over-perk-chip { width: 42px; height: 42px; border-radius: 12px; padding: 0; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
    font-size: 20px; line-height: 1; color: var(--cchue, var(--accent)); scroll-snap-align: center;
    /* crisp-outline recipe: neutral raised tile (no muddy hue-into-bg tint) + a clean
       vivid hue ring; the pure perk hue lives in the icon + border, not a dark sludge fill. */
    background: color-mix(in srgb, var(--ink) 6%, var(--bg));
    border: 1px solid color-mix(in srgb, var(--cchue, var(--accent)) 68%, transparent);
    transition: transform .12s ease, box-shadow .12s ease; }
  .over-perk-chip svg { width: 24px; height: 24px; display: block; }
  /* no .active glow/lift: tapping a chip just opens its tooltip — the chip itself
     stays put. (.active class still toggles in JS to track which tip is open.) */
  /* bubble + caret are positioned per-chip in JS (left/bottom inline); the caret
     tracks the tapped chip via --cx so it still points at edge chips. */
  .over-perks .perk-tip::after { left: calc(50% + var(--cx, 0px)); transform: translateX(-50%); }
  .over-perks.tip-open .perk-tip { opacity: 1; pointer-events: auto;
    transform: translateX(-50%) translateY(0) scale(1); }
  /* Perks recap tip uses the neutral dark info-tooltip look (matches .perk-info-tip
     on the Upgrades page), not the hued speech bubble of the upgrade-milestone tip. */
  .over-perks .perk-tip { border-radius: 10px; background: var(--shadow);
    border: 1px solid var(--ink); box-shadow: 0 6px 20px rgba(0,0,0,.35); }
  .over-perks .perk-tip-name { color: var(--ink); }
  .over-perks .perk-tip::after { border-top-color: var(--ink); }
  [data-theme="dark"] .over-perks .perk-tip { background: #15151c; border-color: #2c2c38; }
  [data-theme="dark"] .over-perks .perk-tip-name { color: var(--ink); }
  [data-theme="dark"] .over-perks .perk-tip::after { border-top-color: #2c2c38; }

  /* --- Game-summary popup: car tile + identity + hero score + grouped grid.
     The --sum-* group hues are scoped to #gameover; the popup renders the same
     grid, so mirror them here. --- */
  /* Layered popup: sits ON TOP of whatever opened it (game-over / recent feed /
     profile) instead of replacing it. Dim scrim over the opener, own pointer
     events (base .overlay is none) so a tap on the scrim closes it and can't leak
     to anything behind. z-index clears the normal overlays (menu/game-over panels). */
  #game-summary {
    --sum-cyan: #28a8ff; --sum-gold: #ffd24a; --sum-org: #ffb020;
    --sum-violet: #9060ff; --sum-green: #2ecf72; --sum-red: #ff5247;
    background: rgba(0, 0, 0, .55);
    pointer-events: auto;
    z-index: 50;
  }
  .summary-head { display: flex; align-items: center; gap: 12px; text-align: left; }
  .summary-car { width: 44px; height: 44px; flex: 0 0 44px; border-radius: 12px;
    background: var(--bg); border: 1px solid var(--line); }
  .summary-car[hidden] { display: none; }
  .summary-id { min-width: 0; }
  .summary-id .set-title { margin: 0; }
  .summary-sub { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
  #summary-score { font-size: 44px; margin-top: 14px; }
  #game-summary .sum-groups { margin-top: 16px; }
  .over-life { font-size: 11.5px; color: var(--muted); margin-top: 8px; letter-spacing: .04em; text-align: center; }
  .over-life b { color: var(--ink); }
  .perks-next { display: flex; flex-direction: column; align-items: center; gap: 5px; margin: 0 0 18px; }
  .perks-next .perk-ico { color: var(--accent); }
  .perks-next .perk-ico svg { width: 30px; height: 30px; }
  .perks-next-name { font-size: 13px; font-weight: 700; color: var(--accent); }
  .perks-next-num { font-size: 12px; opacity: .7; }
  .perks-list { display: flex; flex-direction: column; gap: 8px; width: 100%;
                margin-top: 16px; /* breathing room below the panel header */
                align-content: start; flex: 1; min-height: 0; overflow-y: auto;
                overflow-x: hidden; touch-action: pan-y;
                -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .perks-list::-webkit-scrollbar { display: none; } /* hide scrollbar while keeping scroll */
  .perks-group-head { font-size: 11px; font-weight: 800; letter-spacing: .08em;
                text-transform: uppercase; opacity: .55; margin: 6px 2px 0;
                padding-top: 10px; border-top: 1px solid var(--shadow); }
  .perks-group-head:first-child { border-top: none; padding-top: 0; margin-top: 0; }
  .perks-row.ach .perk-desc { font-size: 9px; opacity: .6; }
  /* fixed icon box so the car canvas, glyphs, and booster SVGs share one footprint */
  .perks-row.ach .perk-ico { width: 20px; height: 20px; align-items: center;
                justify-content: center; font-size: 16px; }
  .perks-row.ach .perk-ico svg { width: 20px; height: 20px; }
  .perks-row { display: flex; align-items: center; gap: 12px; width: 100%;
               background: var(--shadow); border: 1px solid var(--ink); border-radius: 14px;
               padding: 10px 12px; }
  .perks-row.locked { opacity: .5; }
  /* hairline divider between perk groups (the 8px flex gap stays between rows within
     a group). Tinted with the next group's hue so it previews the block below. */
  .perks-divider { height: 1px; width: 100%; margin: 6px 0;
    background: linear-gradient(90deg, transparent,
      color-mix(in srgb, var(--perk-hue, var(--ink)) 55%, transparent), transparent); }
  .perks-row .perk-ico { color: var(--accent); flex: 0 0 auto; }
  .perks-row.locked .perk-ico { color: var(--ink); }
  .perks-row .perk-text { display: flex; flex-direction: column; gap: 2px; flex: 1; text-align: left; }
  .perks-row .perk-name { font-size: 13px; font-weight: 700; color: var(--accent); }
  /* small "i" badge sitting by the progress; opens a text tooltip above it */
  .perk-info { flex: 0 0 auto; width: 14px; height: 14px; padding: 0; margin: 0 -2px 0 0;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 999px; border: 1px solid currentColor; background: transparent;
    color: var(--perk-hue, var(--accent)); font: italic 700 9px/1 serif;
    cursor: pointer; opacity: .65; -webkit-appearance: none; appearance: none; }
  .perk-info:hover { opacity: 1; }
  /* floating description bubble (fixed → never clipped); arrow points down to the badge */
  .perk-info-tip { position: fixed; z-index: 9999; max-width: 230px; box-sizing: border-box;
    padding: 8px 10px; border-radius: 10px; background: var(--shadow); border: 1px solid var(--ink);
    box-shadow: 0 6px 20px rgba(0,0,0,.35); font-size: 11.5px; line-height: 1.4; color: var(--ink);
    pointer-events: none; }
  .perk-info-tip::after { content: ""; position: absolute; left: var(--tip-cx, 50%); top: 100%;
    transform: translateX(-50%); border: 5px solid transparent; border-top-color: var(--ink); }
  .perk-info-tip.below::after { top: auto; bottom: 100%; border-top-color: transparent; border-bottom-color: var(--ink); }
  [data-theme="dark"] .perk-info-tip { background: #15151c; border-color: #2c2c38; color: var(--ink); }
  [data-theme="dark"] .perk-info-tip::after { border-top-color: #2c2c38; }
  [data-theme="dark"] .perk-info-tip.below::after { border-bottom-color: #2c2c38; }
  .perks-row.locked .perk-name { color: inherit; }
  .perks-row .perk-desc { font-size: 11px; opacity: .7; line-height: 1.3; }
  /* "immunity" keyword — the grace windows Adrenaline doubles. One color
     everywhere the word appears (perk rows + game-over tooltip). */
  .imm { font-style: normal; font-weight: 700; color: #f5f7ff; text-shadow: 0 0 6px rgba(245,247,255,.45); }
  .perks-row .perk-status { font-size: 11px; font-weight: 700; flex: 0 0 auto; opacity: .85; white-space: nowrap; }
  /* locked rows: a hued progress bar (fraction filled) + small count caption,
     replacing the old "47 / 150 kills" text. Fixed width so rows line up. */
  .perks-row .perk-status.prog { display: flex; flex-direction: column; align-items: stretch;
                gap: 4px; width: 74px; opacity: 1; }
  .perk-prog { width: 100%; height: 6px; border-radius: 999px; overflow: hidden;
               background: var(--shadow); border: 1px solid var(--ink); }
  .perk-prog-fill { display: block; height: 100%; border-radius: inherit;
               background: var(--perk-hue, var(--accent)); transition: width .3s ease; }
  .perk-prog-num { font-size: 9px; font-weight: 700; opacity: .7; text-align: right; display: flex; flex-direction: column; line-height: 1.25; }
  .perk-prog-count { white-space: nowrap; }
  .perk-prog-unit { white-space: nowrap; }
  .perks-row.masked .perk-ico { font-weight: 800; font-size: 16px; width: 22px; justify-content: center; color: var(--ink); }
  .perks-row.masked .perk-name { color: inherit; }
  /* ===== Neon rainbow ladder — DARK THEME ONLY. Light theme keeps the flat
     look above. All hues come from --perk-hue set per-row in renderUpgradesList. */
  [data-theme="dark"] .perks-life {
    background: linear-gradient(90deg,#ffb020,#5ce06a,#22a7ff,#b23bff,#ff6ec7);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
  }
  /* progress bar — filled with the NEXT perk's family hue */
  [data-theme="dark"] .perks-bar { background: #16161d; border-color: #2a2a34; }
  [data-theme="dark"] .perks-bar-fill {
    background: linear-gradient(90deg,
      color-mix(in srgb, var(--perk-hue,#ff5247) 55%, #000), var(--perk-hue,#ff5247));
    box-shadow: 0 0 7px -2px var(--perk-hue,#ff5247);
  }
  [data-theme="dark"] .perks-next .perk-ico,
  [data-theme="dark"] .perks-next-name { color: var(--perk-hue,#ff5247); }
  /* rows */
  [data-theme="dark"] .perks-row { background: #15151c; border-color: #23232d; }
  [data-theme="dark"] .perks-row:not(.locked) {
    border-color: color-mix(in srgb, var(--perk-hue) 30%, #23232d);
    box-shadow: 0 3px 12px -10px var(--perk-hue);
  }
  /* unlocked: family-hued glossy badge behind the icon */
  [data-theme="dark"] .perks-row:not(.locked) .perk-ico {
    position: relative; width: 34px; height: 34px; border-radius: 10px;
    align-items: center; justify-content: center; color: #0b0b0f;
    background: linear-gradient(135deg,
      color-mix(in srgb, var(--perk-hue) 78%, #fff), var(--perk-hue));
  }
  [data-theme="dark"] .perks-row:not(.locked)[data-level="2"] .perk-ico {
    box-shadow: 0 0 6px -3px var(--perk-hue);
  }
  [data-theme="dark"] .perks-row:not(.locked)[data-level="3"] .perk-ico {
    box-shadow: 0 0 10px -3px var(--perk-hue);
  }
  [data-theme="dark"] .perks-row:not(.locked)[data-level="1"] .perk-ico { filter: saturate(.7); }
  /* roman level pip, bottom-right of the badge */
  .perk-pip {
    position: absolute; right: -4px; bottom: -4px; font-size: 8.5px; font-weight: 800;
    line-height: 1; padding: 2px 3px; border-radius: 5px; color: #0b0b0f;
    background: #fff; box-shadow: 0 0 0 1px rgba(0,0,0,.35);
  }
  [data-theme="dark"] .perks-row:not(.locked) .perk-name { color: var(--perk-hue); }
  /* glowing ✓ pill */
  [data-theme="dark"] .perks-row:not(.locked) .perk-status {
    color: #0b0b0f; background: var(--perk-hue); padding: 3px 9px; border-radius: 999px;
    box-shadow: 0 0 6px -3px var(--perk-hue); opacity: 1;
  }
  /* locked-but-revealed: faint hue hint on the border only */
  [data-theme="dark"] .perks-row.locked:not(.masked) {
    border-color: color-mix(in srgb, var(--perk-hue) 22%, #23232d); opacity: .72;
  }
  [data-theme="dark"] .perks-row.locked .perk-ico { color: var(--muted); }
  /* dark-theme locked progress bar — neon fill in the perk family hue */
  [data-theme="dark"] .perk-prog { background: #0e0e14; border-color: #2a2a34; }
  [data-theme="dark"] .perk-prog-fill {
    background: linear-gradient(90deg,
      color-mix(in srgb, var(--perk-hue,#ff5247) 55%, #000), var(--perk-hue,#ff5247));
    box-shadow: 0 0 5px -3px var(--perk-hue,#ff5247);
  }
  [data-theme="dark"] .perk-prog-num { color: var(--perk-hue,#ff5247); opacity: .85; }
  /* ===== Motion (dark theme). Honors prefers-reduced-motion below. ===== */
  [data-theme="dark"] .perks-bar-fill::after {
    content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 60%;
    border-radius: inherit;
    background: linear-gradient(100deg, transparent, rgba(255,255,255,.55) 50%, transparent);
    transform: translateX(-180%); animation: perkShimmer 1.8s linear infinite;
  }
  /* single band translated fully off both ends → reset happens off-screen, no
     visible loop seam. translateX % is relative to the band's own width. */
  @keyframes perkShimmer { from { transform: translateX(-180%); } to { transform: translateX(280%); } }
  /* subtle badge "breathe" — low amplitude so it never churns/distracts */
  [data-theme="dark"] .perks-row:not(.locked) .perk-ico { animation: perkBreathe 3.2s ease-in-out infinite; }
  @keyframes perkBreathe { 0%,100% { filter: brightness(1); } 50% { filter: brightness(1.18); } }
  [data-theme="dark"] .perks-row:not(.locked)[data-level="1"] .perk-ico {
    animation: perkBreathe 3.2s ease-in-out infinite; filter: saturate(.7);
  }
  /* unlock pop — applied to .new rows on open */
  .perks-row.new { animation: perkPop 480ms cubic-bezier(.2,1.3,.4,1) both; }
  @keyframes perkPop {
    0% { transform: scale(.88); opacity: 0; }
    60% { transform: scale(1.05); opacity: 1; }
    100% { transform: scale(1); }
  }
  /* pause all upgrades animation while the overlay is hidden (no offscreen churn) */
  #upgrades.hidden .perks-bar-fill::after,
  #upgrades.hidden .perks-row .perk-ico { animation: none; }
  @media (prefers-reduced-motion: reduce) {
    [data-theme="dark"] .perks-bar-fill::after,
    [data-theme="dark"] .perks-row .perk-ico,
    .perks-row.new { animation: none !important; }
  }
  .garage-badge { display: inline-block; width: 8px; height: 8px; border-radius: var(--r-circle);
                  background: var(--notif-dot); margin-left: var(--sp-2); vertical-align: middle; }
  .garage-badge[hidden] { display: none; } /* explicit display above defeats the bare [hidden] UA rule */
  /* Notification dot on the Leaderboard button: lit when the player was passed on
     today's board since last look. Amber notice (not alarm-red) — being passed is
     news, not an error. A plain dot, not a count: "you slipped today", no number. */
  .lb-drop-badge { display: inline-block; width: 8px; height: 8px; border-radius: var(--r-circle);
                   background: var(--notif-dot); }
  .lb-drop-badge[hidden] { display: none; }

  /* ---- Generic notification dot ---- */
  /* Shared "new / unseen" dot. Reused for the gated-button unlock dot. The legacy
     dots above (.garage-badge/.lb-drop-badge/.slot-new/.chip-dot) keep their own
     selectors but all now draw with var(--notif-dot), so the whole family matches. */
  .notif-dot { display: inline-block; width: 8px; height: 8px; border-radius: var(--r-circle);
               background: var(--notif-dot); box-shadow: 0 0 6px color-mix(in srgb, var(--notif-dot) 75%, transparent); }
  .notif-dot[hidden] { display: none; }
  /* Tournament tile: unlock dot rides the top-LEFT corner (the rank value badge
     owns top-right). The icon-action tile already has position:relative. */
  #menu .menu-actions #menu-tournament .notif-dot { top: var(--sp-2); left: var(--sp-2); right: auto; }
  #menu .menu-actions .tourn-rank-badge { position: absolute; top: var(--sp-2); right: var(--sp-2); }

  /* ---- Gated lobby button (locked behind a progress requirement) ----
     Unlike #play-btn.coming-soon (which kills pointer-events), a gated button STAYS
     clickable — the tap is what surfaces the progress popup. We gray only the
     icon/label and pin a small lock so the dim state reads without nuking the tap. */
  .gate-locked { position: relative; }
  .gate-locked .ia-icon, .gate-locked .ia-label { filter: grayscale(1); opacity: .4; }
  .gate-locked::after {
    content: "🔒"; position: absolute; top: 1px; right: 2px;
    font-size: 10px; line-height: 1; opacity: .85; pointer-events: none;
  }
  /* a gated button never shows its unlock dot (only fires once unlocked) */
  .gate-locked .notif-dot { display: none; }

  /* ---- Gate progress popup: center, floats up, fades out ---- */
  #gate-hint {
    position: fixed; left: 50%; top: 50%; z-index: 1200; pointer-events: none;
    display: flex; flex-direction: column; align-items: center; gap: var(--sp-1);
    padding: var(--sp-3) var(--sp-4); text-align: center; max-width: min(80vw, 320px);
    border-radius: var(--r-md);
    background: color-mix(in srgb, var(--card) 92%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-2) 30%, transparent);
    box-shadow: 0 12px 40px -12px rgba(0,0,0,.5);
    opacity: 0; transform: translate(-50%, -50%);
  }
  #gate-hint .gh-title { font-size: var(--fs-2xl); font-weight: 800; color: var(--accent-2);
                         font-variant-numeric: tabular-nums; line-height: 1.05; }
  #gate-hint .gh-sub { font-size: var(--fs-sm); font-weight: 600; color: var(--ink); opacity: .82; }
  #gate-hint.show { animation: gateHintRise 1.3s ease-out forwards; }
  @keyframes gateHintRise {
    0%   { opacity: 0; transform: translate(-50%, calc(-50% + 8px)) scale(.92); }
    14%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    68%  { opacity: 1; transform: translate(-50%, calc(-50% - 26px)) scale(1); }
    100% { opacity: 0; transform: translate(-50%, calc(-50% - 48px)) scale(1); }
  }
  @media (prefers-reduced-motion: reduce) {
    #gate-hint.show { animation: gateHintFade 1.3s ease forwards; }
    @keyframes gateHintFade { 0% { opacity: 0; } 14% { opacity: 1; } 80% { opacity: 1; } 100% { opacity: 0; } }
  }

  /* ---- Generic toast ---- */
  .toast { position: fixed; top: calc(env(safe-area-inset-top, 0px) + 16px); left: 50%;
           transform: translate(-50%, -12px); z-index: 1000;
           max-width: min(86vw, 360px); padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md);
           background: var(--ink); color: var(--bg, #fff);
           font-size: var(--fs-sm); font-weight: 600; text-align: center;
           box-shadow: 0 10px 30px -8px rgba(0,0,0,.45);
           opacity: 0; pointer-events: none; transition: opacity .22s ease, transform .22s ease; }
  .toast.show { opacity: 1; transform: translate(-50%, 0); }

  /* ===== Aurora rainbow shimmer (special car icon) =====
     Hue-cycling the baked rainbow body (CARS.aurora.render) makes the colours
     flow. The .car-rainbow class is toggled by applyRainbow() only for the
     unlocked Aurora, on both <img> (profile/history/summary/feed) and <canvas>
     (garage banner + section rows) elements. */
  @keyframes carRainbow { to { filter: hue-rotate(360deg); } }
  .car-rainbow { animation: carRainbow 4s linear infinite; }
  /* Garage banner canvas keeps its drive-in slide AND the shimmer (the drive-in
     rule is more specific, so re-declare both here together). */
  .garage-stage.swept .car-preview.car-rainbow {
    animation: garageDrive .45s cubic-bezier(.2,.8,.3,1), carRainbow 4s linear infinite; }
  /* Reduced motion: keep the static rainbow (baked gradient), drop the cycling. */
  @media (prefers-reduced-motion: reduce) {
    .car-rainbow,
    .garage-stage.swept .car-preview.car-rainbow { animation: none; }
  }

/* --- Tournament button + board (2026-06-19) --------------------------- */
/* Lobby surface is the #menu-tournament icon-action tile, first in the
   .menu-actions row (icon + label + rank badge). */
#menu-tournament[hidden] { display: none; } /* also covered by .menu-actions .icon-action[hidden]; kept for non-row contexts */
/* Rank badge: a small pink pill clipped to the toggle's top-right corner. */
.tourn-rank-badge {
  position: absolute; top: -5px; right: -5px; margin: 0;
  min-width: 16px; height: 16px; padding: 0 4px; box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-2xs); font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums;
  color: #fff; background: var(--accent-2); border-radius: var(--r-pill);
}
.tourn-rank-badge[hidden] { display: none; }

/* --- Tournament screen (app design system: tokens, system font, theming) --- */
#tournament { background: var(--bg); }
#tournament.overlay { padding: 0; } /* full-bleed: panel owns its safe-area padding */
#tournament .panel {
  width: 100%; height: 100%; max-width: none;
  padding: max(20px, env(safe-area-inset-top)) var(--sp-5) var(--sp-5);
  border: 0; border-radius: 0; box-shadow: none; background: transparent;
  display: flex; flex-direction: column; text-align: left; animation: fade .2s ease;
}
/* Top corner controls: History pinned top-left, close top-right, same row. */
.trn-topctl { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); margin-bottom: var(--sp-1); }
.trn-pastbtn {
  flex: 0 0 auto; width: auto; margin: 0; height: 36px; padding: 0 var(--sp-4);
  border: 1px solid var(--line); border-radius: var(--r-sm); background: transparent;
  color: var(--muted); font-size: var(--fs-sm); font-weight: 700; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
}
.trn-pastbtn:hover { color: var(--ink); border-color: var(--ink); }

/* "TOURNAMENT" hero lockup — the lobby DRIFT technique: two screen-blended
   anaglyph ghosts (cyan + magenta) behind a gradient front plate. Brand hues are
   fixed (theme-independent) so the logo reads the same in light + dark. */
#tournament { --mag: #f040cc; --cyan: #28a8ff; }
.trn-hero { text-align: center; margin: var(--sp-2) 0 var(--sp-4); }
.trn-lock { position: relative; display: inline-block; line-height: .9; }
.trn-word { font-size: clamp(30px, 10.5vw, 42px); font-weight: 800; letter-spacing: .02em; display: block; }
/* Calm anaglyph: faint 1px chromatic ghosts (just a hint of depth, not a glitch). */
.trn-word.l { position: absolute; inset: 0; mix-blend-mode: screen; }
.trn-word.l1 { color: var(--cyan); transform: translate(-1px, 1px); opacity: .26; }
.trn-word.l2 { color: var(--mag); transform: translate(1px, -1px); opacity: .28; }
.trn-word.lfront {
  position: relative;
  background: linear-gradient(180deg, #fff 0%, var(--sum-gold) 52%, var(--accent-2) 100%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
.trn-namewrap { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 12px; }
.trn-namerule { height: 1px; width: 42px; background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent-2) 60%, transparent)); }
.trn-namerule.r { background: linear-gradient(90deg, color-mix(in srgb, var(--accent-2) 60%, transparent), transparent); }
.trn-now {
  font-size: 13px; font-weight: 800; letter-spacing: .22em; padding-left: .22em; white-space: nowrap;
  background: linear-gradient(90deg, var(--cyan), #dfe7ff, var(--mag));
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
.trn-now:empty { display: none; }
.trn-eyebrow { margin-top: 10px; font-size: var(--fs-sm); color: var(--muted); }
.trn-eyebrow b { color: var(--sum-gold); font-weight: 800; font-variant-numeric: tabular-nums; }
/* Done — pinned at the panel bottom (list flexes above it), mirrors leaderboard. */
#tournament #tournament-done { flex: 0 0 auto; margin-top: var(--sp-4); }

/* Scored-action chips: two equal-width cells per row, sorted most-valuable-first
   by the caller. Flex-wrap + centre so a lone chip (odd count) sits in the middle
   of its row instead of hugging the left. Label left, "×N ◉" right; per-metric
   identity rides the value colour. */
.trn-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2); min-width: 0; }
.trn-chip { flex: 0 1 calc(50% - var(--sp-2) / 2); min-width: 0;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm); border: 1px solid var(--line);
  background: color-mix(in srgb, var(--ink) 4%, transparent);
  font-size: var(--fs-xs); font-weight: 700; color: var(--muted); }
.trn-chip .lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trn-chip .val { display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto; font-weight: 800; color: var(--ink); }
.trn-chip.cc .val { color: var(--accent-2); }
.trn-chip.dg .val { color: var(--sum-cyan); }
.trn-chip.wk .val { color: var(--sum-org); }

/* ===== Tournament board — integrated leaderboard (each row carries its prize) =====
   Board bar: name + racer count, a live countdown pill, and the ⓘ that toggles
   the scoring breakdown. Replaces the old wordmark hero + podium + prizes panel. */
.trn-bar { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-1); }
/* Single-line title — pill + ⓘ share its row and stay vertically centred with it
   (the old two-line name+count block left the pill floating mid-column). */
.trn-bar-title { flex: 1 1 auto; min-width: 0; font-size: var(--fs-xl); font-weight: 800;
  letter-spacing: -.01em; line-height: 1.15; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: linear-gradient(180deg, var(--ink), color-mix(in srgb, var(--sum-gold) 62%, var(--ink)));
  -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent; }
.trn-bar-title:empty { display: none; }
/* Past-popup variant: no name (it's in the head), so the count fills the slot. */
.trn-bar-title.muted { background: none; color: var(--muted); -webkit-text-fill-color: var(--muted);
  font-size: var(--fs-sm); font-weight: 700; }
/* Racer count — its own quiet line under the bar, out of the alignment. */
.trn-barmeta { font-size: var(--fs-2xs); color: var(--muted); font-weight: 600; letter-spacing: .02em;
  margin: 0 0 var(--sp-3) 2px; }
.trn-barmeta:empty { display: none; }

/* Pill + ⓘ share one fixed height so they sit as a matched pair on the title row;
   the ⓘ is a perfect square of that same height with its glyph dead-centre. */
.trn-bar { --trn-ctl-h: 30px; }
.trn-pill { flex: 0 0 auto; height: var(--trn-ctl-h); display: inline-flex; align-items: center; gap: 6px; padding: 0 12px;
  white-space: nowrap; border-radius: 999px; font-size: var(--fs-xs); font-weight: 800; letter-spacing: .02em; color: var(--sum-cyan);
  border: 1px solid color-mix(in srgb, var(--sum-cyan) 36%, transparent);
  background: color-mix(in srgb, var(--sum-cyan) 12%, transparent); }
.trn-pill b { font-weight: 800; font-variant-numeric: tabular-nums; }
.trn-pill .live { width: 6px; height: 6px; border-radius: 50%; background: var(--sum-cyan); box-shadow: 0 0 8px var(--sum-cyan); }
.trn-pill.final { color: var(--muted); border-color: var(--line); background: color-mix(in srgb, var(--ink) 5%, transparent); }
@media (prefers-reduced-motion: no-preference) { .trn-pill .live { animation: trnLive 1.8s ease-in-out infinite; } }
@keyframes trnLive { 50% { opacity: .35; } }

.trn-info { position: relative; flex: 0 0 auto; width: var(--trn-ctl-h); height: var(--trn-ctl-h); margin: 0; padding: 0; border-radius: var(--r-sm);
  display: grid; place-items: center; cursor: pointer; line-height: 1;
  border: 1px solid var(--line); background: color-mix(in srgb, var(--ink) 4%, transparent);
  color: var(--muted); font-weight: 800; font-size: var(--fs-sm); font-family: inherit; }
.trn-info:active { opacity: .7; }
.trn-info.on { color: var(--sum-cyan); border-color: color-mix(in srgb, var(--sum-cyan) 42%, transparent);
  background: color-mix(in srgb, var(--sum-cyan) 12%, transparent); }
/* "New tournament" nudge dot — cleared once the player opens the scoring. */
.trn-info-dot { position: absolute; top: -3px; right: -3px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--card); box-shadow: 0 0 6px color-mix(in srgb, var(--accent) 60%, transparent); }
.trn-info-dot[hidden] { display: none; }

/* Profile nav button — e.g. "Tournament History" (opens the past-tournaments list). */
.profile-navbtn { display: flex; align-items: center; gap: var(--sp-3); width: 100%; margin-top: var(--sp-2);
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md); border: 1px solid var(--line);
  background: var(--card); color: var(--ink); font: inherit; font-size: var(--fs-md); font-weight: 700;
  cursor: pointer; text-align: left; }
.profile-navbtn[hidden] { display: none; }
.profile-navbtn:active { opacity: .72; }
.profile-navbtn .pnav-ic { flex: 0 0 auto; font-size: 18px; line-height: 1; }
.profile-navbtn .pnav-tx { flex: 1 1 auto; min-width: 0; }
.profile-navbtn .pnav-ch { flex: 0 0 auto; color: var(--muted); font-size: var(--fs-lg); font-weight: 800; }

/* Friend control on another player's profile (Add / Requested / Accept / Friends).
   NOTE: setting display here overrides the `hidden` attribute, so guard it. */
.profile-friend { display: flex; justify-content: center; margin: var(--sp-3) 0 0; }
.profile-friend[hidden] { display: none; }
.pf-btn, .pf-tag { display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4); border-radius: 999px; font: inherit;
  font-size: var(--fs-sm); font-weight: 700; line-height: 1; }
.pf-btn svg, .pf-tag svg { width: 16px; height: 16px; flex: 0 0 auto; }
.pf-btn { border: 1px solid var(--accent-2); background: var(--accent-2); color: #fff; cursor: pointer; }
.pf-btn:active { opacity: .72; }
.pf-btn:disabled { opacity: .5; cursor: default; }
.pf-tag { border: 1px solid var(--line); background: var(--card); color: var(--accent-2); }
.pf-tag.pf-pending { color: var(--muted); }

/* Scoring breakdown — collapsed by default, revealed by the ⓘ. */
.trn-scoring { margin-bottom: var(--sp-2); padding: var(--sp-3); border: 1px solid var(--line);
  border-radius: var(--r-md); background: color-mix(in srgb, var(--ink) 4%, transparent); }
.trn-scoring[hidden] { display: none; }
.trn-scoring-h { font-size: var(--fs-2xs); letter-spacing: .14em; text-transform: uppercase;
  font-weight: 800; color: var(--muted); margin-bottom: var(--sp-2); }

/* Leaderboard rows */
.trn-lrow { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2); margin-bottom: 6px;
  border-radius: var(--r-md); border: 1px solid var(--line); background: var(--card); }
.trn-lrow:last-child { margin-bottom: 0; }
.trn-lrow.clickable { cursor: pointer; }
.trn-lrow.clickable:active { opacity: .72; }
.trn-medal { flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center;
  font-size: var(--fs-xs); font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--muted); background: color-mix(in srgb, var(--ink) 7%, transparent); border: 1px solid var(--line); }
.trn-lrow.t-g .trn-medal { color: #5b4300; background: linear-gradient(160deg, #ffe27a, var(--sum-gold)); border: 0;
  box-shadow: 0 0 10px color-mix(in srgb, var(--sum-gold) 45%, transparent); }
.trn-lrow.t-s .trn-medal { color: #3a4152; background: linear-gradient(160deg, #eef1f8, #c7cede); border: 0; }
.trn-lrow.t-b .trn-medal { color: #5a2f12; background: linear-gradient(160deg, #f2b98a, #e3a06a); border: 0; }

.trn-lcar { flex: 0 0 auto; width: 30px; height: 20px; object-fit: contain; }
.trn-who { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.trn-who .nm { font-size: var(--fs-md); font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  display: flex; align-items: center; }
.trn-who .sub { font-size: var(--fs-2xs); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trn-prize { display: inline-flex; align-items: center; gap: 5px; color: var(--muted); font-weight: 600; }
.trn-prize .p-coin { display: inline-flex; align-items: center; gap: 3px; color: var(--coin-gold); font-weight: 800; }
.trn-prize .trn-rcoin { font-style: normal; }
.trn-prize .p-paint { display: inline-flex; align-items: center; gap: 4px; color: color-mix(in srgb, var(--ink) 76%, var(--muted)); }
.trn-prize .trn-rswatch { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; background: var(--pc, #888);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--pc, #888) 55%, transparent); }
.trn-prize .p-plus { opacity: .4; }

.trn-pp { flex: 0 0 auto; font-size: var(--fs-md); font-weight: 800; font-variant-numeric: tabular-nums;
  display: inline-flex; align-items: center; gap: 5px; }

/* Gold row gets a faint champion wash; the viewer's own row wins the accent tint. */
.trn-lrow.t-g { border-color: color-mix(in srgb, var(--sum-gold) 32%, transparent);
  background: linear-gradient(180deg, color-mix(in srgb, var(--sum-gold) 10%, var(--card)), var(--card)); }
.trn-lrow.me { border-color: color-mix(in srgb, var(--accent-2) 55%, transparent);
  background: color-mix(in srgb, var(--accent-2) 12%, transparent); }
.trn-lrow.me.t-n .trn-medal { color: var(--accent-2); border-color: color-mix(in srgb, var(--accent-2) 40%, transparent); }

/* Podium — mirrors the leaderboard podium (violet radial card + medal pedestals) */
.trn-podium { flex: 0 0 auto; display: flex; align-items: flex-end; justify-content: center;
  gap: var(--sp-2); padding: var(--sp-4) var(--sp-3) 0; margin-bottom: var(--sp-3);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background:
    radial-gradient(120% 70% at 50% 0%, color-mix(in srgb, var(--accent-2) 22%, transparent), transparent 62%),
    linear-gradient(180deg, color-mix(in srgb, var(--ink) 5%, transparent), transparent); }
.trn-spot { flex: 1 1 0; max-width: 116px; display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); }
.trn-spot.center { max-width: 124px; }
.trn-crown { font-size: var(--fs-md); line-height: 1; }
.trn-spot .nm { font-size: var(--fs-sm); font-weight: 700; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trn-spot .pts { font-size: var(--fs-md); font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
.trn-car { width: 46px; height: 46px; object-fit: contain; }
/* Tournament-points orb — the same azure token players collect in-game (TOURN
   glow #2f9bff), used here as the unit of score so the board reads as "orbs
   banked". Decorative; sizes in em so it tracks the surrounding score text. */
.trn-orb { display: inline-block; width: .64em; height: .64em; border-radius: 50%; flex: 0 0 auto; vertical-align: -.02em;
  background: radial-gradient(circle at 38% 32%, #eaf3ff 0%, #2f9bff 46%, #1f6fd0 100%);
  box-shadow: 0 0 5px rgba(47,155,255,.8), inset 0 0 2px rgba(234,243,255,.9); }
.trn-spot.center .trn-car { width: 56px; height: 56px; }
.trn-ped { width: 100%; margin-top: var(--sp-1); border-radius: var(--r-sm) var(--r-sm) 0 0; position: relative;
  display: flex; flex-direction: column; align-items: center; padding-top: var(--sp-2);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 14%, transparent); }
.trn-ped .rk { font-size: var(--fs-xl); font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; }
.trn-ped .medal { font-size: var(--fs-2xs); font-weight: 800; text-transform: uppercase; letter-spacing: .4px; margin-top: 2px; }
.trn-ped.gold { height: 64px; background: color-mix(in srgb, var(--sum-gold) 28%, transparent); }
.trn-ped.gold .rk, .trn-ped.gold .medal { color: #b5820a; }
.trn-ped.silver { height: 50px; background: color-mix(in srgb, #9aa3b2 28%, transparent); }
.trn-ped.silver .rk, .trn-ped.silver .medal { color: #6f7787; }
.trn-ped.bronze { height: 38px; background: color-mix(in srgb, #c77b3a 28%, transparent); }
.trn-ped.bronze .rk, .trn-ped.bronze .medal { color: #a85c24; }
[data-theme="dark"] .trn-ped.gold .rk, [data-theme="dark"] .trn-ped.gold .medal { color: var(--sum-gold); }
[data-theme="dark"] .trn-ped.silver .rk, [data-theme="dark"] .trn-ped.silver .medal { color: #d3d9e6; }
[data-theme="dark"] .trn-ped.bronze .rk, [data-theme="dark"] .trn-ped.bronze .medal { color: #e3a06a; }
.trn-spot.me .nm, .trn-spot.me .pts { color: var(--accent-2); }
.trn-spot.me .trn-ped { background: color-mix(in srgb, var(--accent-2) 22%, transparent); }
.trn-spot.me .trn-ped .rk, .trn-spot.me .trn-ped .medal { color: var(--accent-2); }
.trn-youtag { position: absolute; top: -9px; left: 50%; transform: translateX(-50%);
  font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .5px; padding: 2px var(--sp-2);
  border-radius: var(--r-pill); background: var(--accent-2); color: #fff; }

/* Standings list — leaderboard row styling */
.trn-listwrap { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.trn-listhdr { display: flex; align-items: center; justify-content: space-between; padding: 0 var(--sp-1) var(--sp-2); }
.trn-listhdr .h { font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); }
.trn-listhdr .sum { font-size: var(--fs-xs); color: var(--muted); }
.trn-list { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.trn-list::-webkit-scrollbar { display: none; }   /* hide the scroll indicator (WebKit / iOS WKWebView) */
.trn-row { display: flex; align-items: center; gap: var(--sp-3); height: 40px; padding: var(--sp-2) var(--sp-1);
  border-bottom: 1px solid var(--line); font-size: var(--fs-md); }
.trn-row .rank { width: var(--icon-row); flex: 0 0 auto; text-align: center; color: var(--muted); font-weight: 700; font-variant-numeric: tabular-nums; }
.trn-row .glyph { width: var(--icon-row); height: var(--icon-row); flex: 0 0 auto; object-fit: contain; }
.trn-row .nm { flex: 1 1 auto; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trn-row .pp { flex: 0 0 auto; font-weight: 800; font-variant-numeric: tabular-nums; display: inline-flex; align-items: center; gap: 5px; }
.trn-row .pp small { font-size: var(--fs-2xs); color: var(--muted); margin-left: 3px; font-weight: 700; }
.trn-row.me { color: var(--accent-2); background: color-mix(in srgb, var(--accent-2) 8%, transparent); border-radius: var(--r-sm); border-bottom-color: transparent; }
.trn-row.me .rank { color: var(--accent-2); }
.trn-row.me .pp { color: var(--ink); }
.trn-youpill { font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .4px; padding: 1px var(--sp-1);
  border-radius: var(--r-xs); background: color-mix(in srgb, var(--accent-2) 20%, transparent); color: var(--accent-2); margin-left: var(--sp-2); vertical-align: middle; }
/* Ranks 4-10 are award places too (bronze "Tournament Top 10" medal). Fixed-
   width place slot keeps the name column aligned whether or not a row carries a
   trophy; a bronze trophy + tinted rank flag the medal zone. */
.trn-place { width: 14px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; }
/* Muted copper — deliberately dimmer than the vivid podium bronze (#a85c24 /
   #e3a06a) so a top-10 finalist place reads as a tier BELOW the rank-3 podium
   spot, not equal to it. */
.trn-place-medal { color: #8a6240; display: block; filter: drop-shadow(0 1px 1px rgba(0,0,0,.3)); }
[data-theme="dark"] .trn-place-medal { color: #c0926a; }
.trn-row.medal-place .rank { color: #8a6240; }
[data-theme="dark"] .trn-row.medal-place .rank { color: #c0926a; }
.trn-row.medal-place { box-shadow: inset 2px 0 0 color-mix(in srgb, #8a6240 40%, transparent); }
.trn-row.me.medal-place .rank { color: var(--accent-2); }   /* "me" identity wins over the copper tint */
/* Cutline marking the end of the medal places (after rank 10) */
.trn-cutline { display: flex; align-items: center; gap: var(--sp-2); margin: var(--sp-2) 0; color: var(--muted);
  font-size: var(--fs-2xs); font-weight: 800; text-transform: uppercase; letter-spacing: .6px; }
.trn-cutline::before, .trn-cutline::after { content: ""; flex: 1 1 auto; height: 1px;
  background: linear-gradient(to right, transparent, color-mix(in srgb, #8a6240 40%, transparent), transparent); }
.trn-cutline span { flex: 0 0 auto; color: color-mix(in srgb, #8a6240 75%, var(--muted)); }
[data-theme="dark"] .trn-cutline span { color: #c0926a; }
/* Tap a player row/spot to open their profile (game/tournament.js) */
.trn-row.clickable, .trn-spot.clickable { cursor: pointer; }
.trn-row.clickable:active, .trn-spot.clickable:active { opacity: .7; }
.trn-empty { padding: var(--sp-6) var(--sp-2); text-align: center; color: var(--muted); font-size: var(--fs-sm); }

/* History list rows (inside Past popup) */
.trn-hrow { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-2);
  border-bottom: 1px solid var(--line); cursor: pointer; }
.trn-hrow:hover { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.trn-hrow .hmain { flex: 1 1 auto; min-width: 0; }
.trn-hrow .hname { font-size: var(--fs-md); font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trn-hrow .hdate { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .4px; color: var(--muted); margin-top: 2px; }
.trn-hrow .hwin { font-size: var(--fs-xs); color: var(--accent-2); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trn-hrow .hme { flex: 0 0 auto; text-align: right; }
.trn-hrow .hme .rk { font-size: var(--fs-lg); font-weight: 800; color: var(--ink); line-height: 1; }
.trn-hrow .hme .lb { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .4px; color: var(--muted); }
.trn-hrow .hme .none { color: var(--muted); }
.trn-hrow .chev { flex: 0 0 auto; color: var(--muted); font-size: var(--fs-lg); }

/* Past popup — centered card, app .panel look */
/* Fixed dark scrim (NOT var(--ink), which is near-white in dark theme → white wash).
   Matches the #continue / #levelup / game-summary popups. */
.trn-pop { position: fixed; inset: 0; z-index: 40; display: flex; align-items: center; justify-content: center;
  padding: var(--sp-5); background: rgba(0, 0, 0, .55); backdrop-filter: blur(2px); pointer-events: auto; }
.trn-pop.hidden { display: none; }
.trn-popcard { width: 100%; max-width: 360px; max-height: 88vh; display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-xl);
  box-shadow: var(--shadow-panel); padding: var(--sp-5); text-align: left; animation: pop .25s ease; }
.trn-popcard .screen-head { margin-bottom: var(--sp-3); }
.trn-popcard .trn-listwrap { max-height: 40vh; }

/* Profile Medals section — own labeled block (header is .profile-section-head,
   which supplies its own top margin + hairline, matching Stats/Recent) */
  .profile-medals-section { width: 100%; }
  .profile-medals-section.hidden { display: none; }
  .profile-medals { width: 100%; }
  .medals-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
  .medal-chip { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3);
    border-radius: var(--r-md); background: rgba(124, 92, 255, 0.10);
    border: 1px solid rgba(124, 92, 255, 0.22); min-width: 0; }
  .medal-chip .medal-label { font-size: var(--fs-xs); font-weight: 600; color: #e9e6ff;
    line-height: 1.2; min-width: 0; text-align: left; }  /* left-align (profile inherits center); wrap to 2 lines, not clip */
  /* fixed-size icon box so the count badge anchors consistently to its corner */
  .medal-icon { position: relative; flex: 0 0 auto; width: 34px; height: 34px;
    display: flex; align-items: center; justify-content: center; }
  /* count → small badge on the icon's bottom-right corner (not inline in label) */
  .medal-count { position: absolute; right: -5px; bottom: -4px; min-width: 16px; height: 16px;
    padding: 0 4px; border-radius: 8px; background: var(--accent-2); color: #fff;
    font-size: 10px; font-weight: 800; line-height: 16px; text-align: center;
    box-shadow: 0 1px 3px rgba(0,0,0,.45); }
  .medal-glyph { flex: 0 0 auto; filter: drop-shadow(0 1px 2px rgba(0,0,0,.35)); }
  /* freshly-revealed medal: same unlock pop as a new upgrade row (reuses @keyframes
     perkPop), plus a metal-tinted glow so it reads as just-earned next to the burst */
  .medal-chip.new { animation: perkPop 480ms cubic-bezier(.2,1.3,.4,1) both;
    border-color: color-mix(in srgb, var(--perk-hue, var(--accent-2)) 60%, transparent);
    box-shadow: 0 0 14px color-mix(in srgb, var(--perk-hue, var(--accent-2)) 40%, transparent); }
  @media (prefers-reduced-motion: reduce) { .medal-chip.new { animation: none !important; } }
  .medals-empty { font-size: var(--fs-xs); color: #9b93c7; padding: var(--sp-2) 0; }
  .chip-dot { display: inline-block; width: 8px; height: 8px; margin-left: var(--sp-1);
    border-radius: 50%; background: var(--notif-dot); box-shadow: 0 0 6px color-mix(in srgb, var(--notif-dot) 75%, transparent);
    vertical-align: middle; }

/* ---- District picker: lobby location carousel (Phase 1, skin swap) --------
   The prev/next arrows flank the Play button in a flex row (.play-row); the
   district name sits centred just above. Faint neutral-grey --muted chevrons
   that brighten on hover. */
.district-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);  /* vertical centre of the screen */
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  margin: 0;
  padding: 0 0 4px;             /* optically centre the chevron glyph */
  font-size: 34px;
  font-weight: 400;             /* thin chevron */
  line-height: 1;
  color: var(--muted);          /* neutral grey, not the lobby violet */
  opacity: .6;
  background: none;
  border: 0;
  box-shadow: none;
  text-shadow: 0 1px 6px rgba(0, 0, 0, .6);
  cursor: pointer;
  pointer-events: auto;         /* #menu is pointer-events:none; re-enable here */
  -webkit-tap-highlight-color: transparent;
  transition: opacity .15s ease;
}
.district-prev { left: max(6px, env(safe-area-inset-left)); }
.district-next { right: max(6px, env(safe-area-inset-right)); }
/* Pin translateY(-50%) in EVERY interactive state so the global button lift
   (button:hover translateY(-1px)) can't override centring + shift the arrow. */
.district-arrow:hover,
.district-arrow:focus,
.district-arrow:active { opacity: 1; transform: translateY(-50%); }
/* End-of-carousel arrow: fully hidden (fades out) — no neighbour, so no chevron.
   Inert in every state (beats :hover), and unclickable so it can't fire a buzz. */
.district-arrow.is-end,
.district-arrow.is-end:hover,
.district-arrow.is-end:focus,
.district-arrow.is-end:active { opacity: 0; pointer-events: none; cursor: default; }

/* Locked district: Play becomes a non-clickable "Locked" — desaturated, dimmed,
   no sheen. The always-on checklist above it explains what to do. */
/* Kept CLICKABLE so a locked Play tap bumps the unlock popup (guard in the click
   handler blocks the run); the greying stays cosmetic like the LB/tourney gate. */
#play-btn.coming-soon { cursor: default; filter: grayscale(1); opacity: .5; }
#play-btn.coming-soon .play-sheen { display: none; }

/* District name — in-flow caption centred just above the Play CTA (the freed
   tips slot). Quiet muted-violet, soft text-shadow for legibility over the
   world. Purely informative (no pointer events). */
.district-name {
  align-self: stretch;          /* full panel width so the text… */
  display: flex;                /* row: name text + optional difficulty badge */
  align-items: center;
  gap: var(--sp-2);             /* badge sits just after the name */
  text-align: left;             /* …sits at the play-row's left edge (left-aligned) */
  margin: 0 0 var(--sp-3);      /* sits right above the Play button */
  padding: 0;
  background: none;
  border: 0;
  font-size: var(--fs-md);      /* a bit bigger than the old caption */
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--accent-2) 62%, var(--muted));
  text-shadow: 0 1px 6px rgba(0, 0, 0, .6);
  white-space: nowrap;
  pointer-events: none;
}
/* View-only ("Coming Soon…") district: warm gold-brown label, matching the
   Industrial Docks rust skin instead of the lobby's violet. The badge keeps its
   own colour (set below), only the name text recolours. */
.district-name.loc-soon #district-name-text { color: #c79a3e; }

/* Difficulty badge — a compact pill beside the district name (data-driven `tag`).
   HARD = danger-red, telegraphing the whale challenge before you tap in. */
.loc-tag {
  flex: none;
  font-size: .58em;             /* small relative to the name caption */
  font-weight: 800;
  letter-spacing: .12em;
  line-height: 1;
  padding: .28em .55em;
  border-radius: 999px;
  text-shadow: none;
}
.loc-tag[hidden] { display: none; }
.loc-tag-hard {
  color: #ff7a6e;
  background: color-mix(in srgb, #ff5a4d 16%, transparent);
  border: 1px solid color-mix(in srgb, #ff5a4d 55%, transparent);
}
/* EASY = mint — the Proving Grounds learning-range badge. */
.loc-tag-easy {
  color: #6ee7a8;
  background: color-mix(in srgb, #34d17e 14%, transparent);
  border: 1px solid color-mix(in srgb, #34d17e 50%, transparent);
}
/* NORMAL = amber — Main City's baseline badge, sits between EASY mint and HARD red. */
.loc-tag-normal {
  color: #ffd166;
  background: color-mix(in srgb, #f0b429 14%, transparent);
  border: 1px solid color-mix(in srgb, #f0b429 50%, transparent);
}
/* EXTREME = approach-light blue — the Skyline Airport endgame badge. */
.loc-tag-extreme {
  color: #6fd2ff;
  background: color-mix(in srgb, #35a9ff 14%, transparent);
  border: 1px solid color-mix(in srgb, #35a9ff 55%, transparent);
}

/* ============================================================================
   LOCATION unlock popup — a compact card floated at SCREEN CENTRE while a locked
   district is selected. Simple text: no dark scrim, so the lobby/idle-car view
   around it stays visible; pointer-events:none so the carousel swipe passes
   straight through. District's rust-gold identity: header (lock + "Unlock
   <name>") over one plain line per condition. ================================ */
/* --loc-accent is set per-district from JS (d.titleColor) so the card adopts the
   active location's identity colour; falls back to the rust-gold default. */
.loc-req {
  --loc-accent: #c79a3e;
  position: fixed;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 6;
  width: min(300px, calc(100vw - 40px));   /* fixed footprint — independent of text */
  min-height: 132px;                         /* static height: 1-line vs 2-line reads identical */
  box-sizing: border-box;
  padding: var(--sp-6);
  display: flex; flex-direction: column; justify-content: center; gap: var(--sp-4);
  text-align: left; border-radius: var(--r-xl);
  background: color-mix(in srgb, var(--card) 90%, var(--loc-accent) 10%);
  border: 1px solid color-mix(in srgb, var(--loc-accent) 36%, var(--line));
  box-shadow: var(--shadow-panel);
  pointer-events: none;        /* informational — lobby swipe passes through */
}
.loc-cl-head { display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-xs); font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: color-mix(in srgb, var(--loc-accent) 82%, var(--muted)); }
.loc-cl-lock { filter: saturate(.7); }
.loc-cl-title { flex: 1; min-width: 0; line-height: 1.25; }   /* wraps in the narrow card instead of truncating */
.loc-cl-tasks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.loc-req[hidden] { display: none; }
/* Hide the popup during the FTUE gate (whole picker hidden). */
#menu.tutorial .loc-req,
#menu.loc-gated .loc-req { display: none; }

/* One plain condition line (the task's desc) — a small location-tinted bullet + text. */
.loc-line { display: flex; align-items: baseline; gap: 8px;
  font-size: var(--fs-sm); font-weight: 700; color: var(--ink); line-height: 1.3; }
.loc-line::before { content: ""; flex: none; width: 5px; height: 5px; border-radius: var(--r-circle);
  background: color-mix(in srgb, var(--loc-accent) 70%, var(--muted)); transform: translateY(-1px); }

/* Tap-flash: a locked district's greyed Leaderboard/Tournament/Play tap pulses the
   card (preserves the base translate so it stays centred). Gentle: soft scale + a
   thin location-tinted ring. */
.loc-req.flash { animation: locReqFlash .5s ease; }
@keyframes locReqFlash {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  35%      { transform: translate(-50%, -50%) scale(1.022);
             box-shadow: 0 0 0 2px color-mix(in srgb, var(--loc-accent) 30%, transparent), var(--shadow-panel); }
}

/* Locked district: grey the Leaderboard button only — its board is per-location,
   so there's nothing to show on a locked district. Kept CLICKABLE so the tap still
   fires its guard (deny buzz + popup flash); the greying is cosmetic.
   Tournaments are SKILL-gated, never location-gated (see onTournamentTap), so the
   Tournament tile must NOT be dimmed on a locked district — it works there. */
#menu.loc-locked #leaderboard-btn { filter: grayscale(1); opacity: .5; }

/* ============================================================================
   LOCATION-UNLOCK celebration — one-shot confetti + "Unlocked!" card on first
   entry to a freshly-unlocked district. No dark scrim (just a soft centre glow),
   auto-dismisses. Above the lobby; below real modals. =========================*/
#loc-celebrate { z-index: 1100; pointer-events: auto;
  background: radial-gradient(closest-side, rgba(199,154,62,.14), rgba(8,8,14,0) 72%); }
#loc-celebrate.hidden { display: none; }
#loc-celebrate.fading { opacity: 0; transition: opacity .4s ease; }
.cel-burst { position: fixed; inset: 0; pointer-events: none; overflow: hidden; }
.cel-piece { position: absolute; top: 42%; left: 50%; border-radius: 2px; opacity: 0;
  box-shadow: 0 1px 4px rgba(0,0,0,.4);
  animation: celFall var(--dur) cubic-bezier(.15,.6,.35,1) var(--delay) forwards; }
@keyframes celFall {
  0%   { transform: translate(-50%, -50%) rotate(0); opacity: 1; }
  100% { transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) rotate(var(--dr)); opacity: 0; }
}
.cel-card { position: relative; text-align: center; pointer-events: none;
  animation: celPop .5s cubic-bezier(.2,1.3,.4,1) both; }
.cel-emoji { font-size: 46px; line-height: 1; filter: drop-shadow(0 4px 10px rgba(0,0,0,.5)); }
.cel-title { margin-top: 6px; font-size: var(--fs-3xl); font-weight: 800; line-height: 1.1;
  background: linear-gradient(96deg, #f0cd72, #c79a3e);
  -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 2px 8px rgba(0,0,0,.55)); }
.cel-sub { margin-top: 2px; font-size: var(--fs-md); font-weight: 800; letter-spacing: .22em;
  text-transform: uppercase; color: #4ade80; text-shadow: 0 1px 6px rgba(0,0,0,.6); }
@keyframes celPop { 0% { transform: scale(.6); opacity: 0; } 60% { transform: scale(1.06); } 100% { transform: scale(1); opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .cel-piece { animation-duration: .01ms; }
  .cel-card { animation: none; }
}

/* Lobby card: kill the browser's own touch gestures so a horizontal swipe-to-
   switch-district drag isn't claimed/cancelled by WKWebView mid-gesture. Scoped
   to #menu so other overlays' panels (game-over, etc.) keep their scrolling. */
#menu .panel { touch-action: none; }

/* View-only district hides the lobby best-score block — there's no per-location
   score yet, so "your best" would be misleading next to a Coming Soon teaser. */
#menu.loc-locked .big-score,
#menu.loc-locked .score-label,
#menu.loc-locked .rank-line { display: none; }

/* Locked district: fade + blur the idle game view behind the centred checklist
   so it reads as "locked". Sits at z0 (above the world canvas, below the panel's
   z1 UI), so the title / Play / action buttons stay bright above it. Only present
   while a locked district is selected (#menu.loc-locked, toggled in main.js). */
#menu.loc-locked::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: rgba(8, 8, 14, 0.5);
  backdrop-filter: blur(2.5px);
  -webkit-backdrop-filter: blur(2.5px);
  transition: opacity .3s ease;
}

/* Tutorial-stripped lobby hides the picker (matches the .tutorial gating that
   holds back profile / settings / ranks until the FTUE is done). #menu.loc-gated
   tracks the same first-run-tutorial gate (ftuePart1Done in main.js) — kept as a
   distinct class so it can also drive the one-shot "new locations" unlock dot on
   the locked→unlocked crossing. Whole picker hidden either way. */
#menu.tutorial .district-arrow,
#menu.tutorial .district-name,
#menu.loc-gated .district-arrow,
#menu.loc-gated .district-name { display: none; }
/* "New locations" dot on the next arrow — rides the top-right of the chevron box. */
#district-next .notif-dot { position: absolute; top: 7px; right: 7px; }

/* UPDATE-AVAILABLE popup — synthwave update card, same family as #continue /
   #levelup. Fixed dark scrim + blur catches taps so the lobby behind can't be
   touched; only the two buttons act. Below #levelup (50) / .trn-pop (40).
   Signature element: the version delta (muted current → brand-gradient new). */
#update-popup { z-index: 30; pointer-events: auto; background: rgba(8, 8, 14, 0.62); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
/* Tournament result popup — same modal family: dim scrim + blur darkens the
   lobby and (pointer-events:auto vs base .overlay's none) catches taps so the
   UI underneath can't be clicked; only the popup buttons act. */
#tourn-result-popup { z-index: 30; pointer-events: auto; background: rgba(8, 8, 14, 0.62); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
.upd-panel { display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); max-width: 340px; }
.upd-panel button { margin-top: 0; } /* gap owns the spacing, not button's default margin */
.upd-eyebrow { font-size: var(--fs-sm); font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: #28a8ff; }
.upd-title { font-size: var(--fs-3xl); font-weight: 800; line-height: 1.18; color: var(--ink); }
/* Version delta — the memorable bit. Current version sits muted; the new one
   burns in the purple→magenta brand gradient, with an arrow pointing forward. */
.upd-delta { display: inline-flex; align-items: center; gap: var(--sp-3); margin: var(--sp-1) 0 var(--sp-2);
  font-variant-numeric: tabular-nums; }
.upd-ver { font-size: var(--fs-xl); font-weight: 800; letter-spacing: .01em; }
.upd-ver::before { content: "v"; opacity: .55; font-weight: 700; margin-right: 1px; }
.upd-ver-from { color: var(--muted); }
.upd-ver-to { background: linear-gradient(92deg, #9060ff, #f040cc); -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent; }
.upd-ver-to::before { -webkit-text-fill-color: #9060ff; opacity: .8; } /* keep the "v" readable, not clipped */
.upd-arrow { color: var(--muted); font-weight: 700; font-size: var(--fs-lg); transform: translateY(-1px); }
/* Primary CTA: synthwave gradient + white label, mirrors .continue-watch. */
.upd-go { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  background: linear-gradient(92deg, #9060ff, #f040cc); color: #fff; font-weight: 800;
  box-shadow: 0 8px 24px -10px rgba(240, 64, 204, 0.65); }
.upd-go-glyph { display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: var(--r-circle);
  background: rgba(8, 6, 16, 0.28); font-size: var(--fs-sm); line-height: 1; }

/* Tournament result popup — rank-adaptive premium trophy card
   (game/tournamentRewards.js). One --trr-accent per tier reskins the emblem,
   title gradient, reward chips and Claim button. NO EMOJI: the emblem is a CSS
   medallion showing the placement number. */
.trr-panel { text-align: center; max-width: 330px;
  --trr-accent: var(--accent-2); --trr-accent-hi: #c98cff; --trr-go-ink: #fff; --trr-glow: rgba(168,85,247,.5);
  box-shadow: 0 0 0 1px var(--trr-glow), 0 24px 60px -22px var(--trr-glow), var(--shadow-panel); }
/* tier accents — literals, readable on both --card values */
.trr-panel.trr-t-champion { --trr-accent: #ffb020; --trr-accent-hi: #ffd76a; --trr-go-ink: #1a1a20; --trr-glow: rgba(255,176,32,.55); }
.trr-panel.trr-t-podium   { --trr-accent: #c9ccd6; --trr-accent-hi: #eef0f5; --trr-go-ink: #1a1a20; --trr-glow: rgba(201,204,214,.5); }
.trr-panel.trr-t-podium3  { --trr-accent: #cd7f32; --trr-accent-hi: #e8a860; --trr-go-ink: #1a1a20; --trr-glow: rgba(205,127,50,.5); }
.trr-panel.trr-t-top10    { --trr-accent: #28a8ff; --trr-accent-hi: #7cc8ff; --trr-go-ink: #fff; --trr-glow: rgba(40,168,255,.5); }
.trr-panel.trr-t-finish   { --trr-accent: var(--accent-2); --trr-accent-hi: #c98cff; --trr-go-ink: #fff; --trr-glow: rgba(168,85,247,.5); }
/* win-back grant (tournamentRewards.js openWinback) — warm coin-gold, distinct from the champion tier */
.trr-panel.trr-t-winback  { --trr-accent: #e0a72e; --trr-accent-hi: #ffd76a; --trr-go-ink: #1a1a20; --trr-glow: rgba(224,167,46,.5); }
/* medallion — disc + placement number */
.trr-emblem { position: relative; width: 68px; height: 68px; margin: 0 auto var(--sp-2);
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-circle); font-size: var(--fs-3xl); font-weight: 800; color: var(--trr-go-ink);
  background: linear-gradient(145deg, var(--trr-accent-hi), var(--trr-accent));
  box-shadow: inset 0 0 0 2px rgba(255,255,255,.35), 0 6px 18px -6px var(--trr-glow); }
.trr-emblem .trr-num { position: relative; z-index: 1; line-height: 1; }
/* Win-back crest is the coin ◉ itself → render the flat gold token (no medallion
   disc) so it matches the balance chip + reward chip. Tournament emblems keep the
   rank medallion. */
.trr-t-winback .trr-emblem { background: none; box-shadow: none; color: var(--coin-gold);
  text-shadow: 0 0 16px rgba(255,190,50,.4); }
/* text */
.trr-eyebrow { color: var(--trr-accent); }
.trr-title { font-size: var(--fs-3xl); font-weight: 800; letter-spacing: .04em; margin: var(--sp-1) 0;
  background: linear-gradient(92deg, var(--trr-accent-hi), var(--trr-accent));
  -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent; }
.trr-name { opacity: .7; font-size: var(--fs-md); margin-bottom: var(--sp-1); color: var(--ink); }
.trr-rank { font-size: var(--fs-lg); font-weight: 600; margin-bottom: var(--sp-1); color: var(--ink);
  display: inline-flex; align-items: center; gap: 5px; } /* .trn-orb (real blue tournament-points glyph) rides inline */
.trr-winner { opacity: .6; font-size: var(--fs-sm); margin-bottom: var(--sp-2); color: var(--ink); }
/* reward chips */
.trr-rewards { display: flex; flex-direction: column; gap: var(--sp-1); margin: var(--sp-3) 0; }
/* Frameless FLOATING reward — icon badge + value + muted type label, no box. */
.trr-item { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 3px 0; }
.trr-badge { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%; font-style: normal;
  display: flex; align-items: center; justify-content: center; font-size: 16px; line-height: 1;
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.35); }
/* Coin reward: the flat gold ◉ token (same icon as the balance chip), not a
   filled disc — no badge background/ring, just the glyph a touch larger. */
.trr-badge-coin { background: none; box-shadow: none; color: var(--coin-gold);
  font-size: 28px; text-shadow: 0 0 10px rgba(255,190,50,.45); }
.trr-badge-paint { background: radial-gradient(circle at 38% 30%, #fff -10%, var(--pc, #888) 65%);
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.4), 0 0 12px -3px var(--pc, #888); }
.trr-rw { display: flex; flex-direction: column; line-height: 1.12; }
.trr-amt { font-weight: 700; font-size: var(--fs-lg); color: var(--ink); }
/* Coin reward is one centered line (◉ ×N) — no stacked label, so drop the column
   and align the amount on the icon's centre line. */
.trr-coins { align-items: center; }
.trr-amt-coin { font-weight: 800; font-size: var(--fs-xl); color: var(--ink); line-height: 1; }
/* Paint name reads as a title (quoted + italic) so it's clearly a paint, not a value. */
.trr-amt-paint { font-style: italic; }
.trr-lbl { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; opacity: .55; color: var(--ink); }
/* Claim CTA — tier-accent gradient, per-tier ink for contrast */
.trr-go { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  background: linear-gradient(92deg, var(--trr-accent), var(--trr-accent-hi)); color: var(--trr-go-ink);
  font-weight: 800; box-shadow: 0 8px 24px -10px var(--trr-glow); }
.trr-go[hidden] { display: none; } /* explicit display above defeats the bare [hidden] UA rule */

/* ---- Mini-levels HUD + result overlay ---- */
#level-hud {
  position: fixed; top: calc(env(safe-area-inset-top, 0px) + 14px);
  left: 0; right: 0; z-index: 40;
  display: flex; justify-content: center; gap: 18px;
  pointer-events: none;
  font: 700 22px/1 system-ui, sans-serif; color: #eaf0ff;
  text-shadow: 0 2px 8px rgba(0,0,0,0.6);
}
#level-hud .lvl-hud-time { color: #ffd166; }
#level-hud .lvl-hud-count { color: #7ad0ff; }

#level-result {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  background: rgba(8,10,22,0.62); backdrop-filter: blur(3px);
}
#level-result .lvl-result-card {
  background: #161a2e; border: 1px solid rgba(120,140,220,0.35);
  border-radius: 18px; padding: 28px 34px; text-align: center;
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}
#level-result .lvl-result-title {
  font: 800 26px/1.1 system-ui, sans-serif; margin-bottom: 20px; color: #eaf0ff;
}
#level-result .lvl-result-title.win  { color: #6ee7a8; }
#level-result .lvl-result-title.fail { color: #ff7a7a; }
#level-result .lvl-result-btns { display: flex; gap: 12px; justify-content: center; }
#level-result .lvl-result-btns button {
  font: 700 16px/1 system-ui, sans-serif; color: #fff;
  background: #2a3050; border: 1px solid rgba(120,140,220,0.4);
  border-radius: 12px; padding: 12px 22px; cursor: pointer;
}
#level-result .lvl-result-btns button:hover { background: #353c66; }

/* ---- Lobby stage button (current level, sits just above the ENDLESS CTA) --- */
/* A secondary CTA: the same dark panel + red/cyan chromatic split as ENDLESS but
   slimmer. Plays the current (first-uncleared) stage; turns mint + ✓ once every
   stage is cleared. */
#stage-btn {
  position: relative;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; margin: 0 0 var(--sp-2); padding: 11px 16px;
  font-weight: 800; font-size: var(--fs-sm); letter-spacing: 2.5px;
  text-transform: uppercase; color: #fff;
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: 12px;
  background: linear-gradient(#15121d, #100d16);
  text-shadow: -1.2px 0 0 rgba(255, 82, 71, .5), 1.2px 0 0 rgba(40, 168, 255, .5);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .4);
  cursor: pointer;
  transition: transform .12s cubic-bezier(.34, 1.56, .64, 1), border-color .2s ease, box-shadow .2s ease;
}
#stage-btn[hidden] { display: none; } /* dark subfeature: id+attr beats the display:flex above; backend flag un-hides */
#menu.tutorial #stage-btn { display: none; } /* stripped during the tutorial, like .menu-actions */
#stage-btn:hover {
  transform: translateY(-2px);
  border-color: rgba(40, 168, 255, .55);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .5), 0 0 16px rgba(40, 168, 255, .22);
}
#stage-btn:active { transform: translateY(1px) scale(.98); }
#stage-btn .stage-btn-check { display: none; color: #4ade80; font-size: .9em; }
#stage-btn.cleared {
  border-color: rgba(74, 222, 128, .5);
  text-shadow: -1.2px 0 0 rgba(74, 222, 128, .42), 1.2px 0 0 rgba(40, 168, 255, .4);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .4), inset 0 0 0 1px rgba(74, 222, 128, .18);
}
#stage-btn.cleared .stage-btn-check { display: inline-block; }

/* Push soft-prompt — centered modal popup (game/push.js). Same family as
   #update-popup / #tourn-result-popup: dim scrim + backdrop-blur, own pointer
   events (base .overlay is none) so a tap on the scrim closes it and can't leak
   to the lobby underneath. Below #levelup (50) / .trn-pop (40). */
#push-popup { z-index: 30; pointer-events: auto; background: rgba(8, 8, 14, 0.62); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
.push-panel { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--sp-3); max-width: 320px; }
.push-panel button { margin-top: 0; width: 100%; } /* gap owns spacing; full-width CTAs */
/* Account-delete confirm (game/account.js) — same centered-popup family as
   #push-popup: dim scrim + own pointer events, .push-panel layout reused. */
#acct-del-pop { z-index: 30; pointer-events: auto; background: rgba(8, 8, 14, 0.62); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
/* Signature bell — accent-2 halo disc, mirrors .trr-emblem construction. */
.push-glyph {
  width: 60px; height: 60px; display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-circle); font-size: 28px; line-height: 1;
  background: radial-gradient(circle at 38% 30%, color-mix(in srgb, var(--accent-2) 40%, transparent), color-mix(in srgb, var(--accent-2) 16%, transparent));
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent-2) 45%, transparent), 0 8px 22px -8px rgba(168, 85, 247, 0.6);
}
.push-title { font-size: var(--fs-3xl); font-weight: 800; line-height: 1.15; color: var(--ink); }
.push-sub { font-size: var(--fs-md); line-height: 1.4; color: var(--muted); }
.push-enable {
  background: linear-gradient(92deg, #9060ff, #f040cc); color: #fff; font-weight: 800;
  box-shadow: 0 8px 24px -10px rgba(240, 64, 204, 0.65);
}
.push-later { background: transparent; color: var(--muted); }

/* ============================================================
   FRIENDS OVERLAY — #friends panel
   Matches the dark violet/neon DRIFT aesthetic; reuses design
   tokens established by the leaderboard + profile screens.
   ============================================================ */

/* #friends is a full-screen overlay (see the #leaderboard/#recent/#friends
   block above). The body stretches edge-to-edge and scrolls; the overlay's
   own 24px+safe-area padding provides the screen gutter. Left-aligned, with
   the groups sitting as --card surfaces on the --bg page. */
#friends .screen-head { margin-bottom: var(--sp-4); }
#friends .menu-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  text-align: left;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: var(--sp-6);
}
#friends .menu-body::-webkit-scrollbar { display: none; }

/* Bottom "Done" — plain base button (full-width inked CTA, like Settings'
   #settings-done). A direct child of .panel (outside the scrolling .menu-body),
   so it stays PINNED to the bottom of the screen while the lists scroll above. */
#friends-done-bottom { flex: 0 0 auto; margin-top: var(--sp-4); }

/* ---- Zone 1: code card + add form (grouped, iOS-settings density) ---- */
.friends-codezone {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-bottom: var(--sp-5);
}

.fc-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--sp-3) var(--sp-4);
}
.fc-cardtop {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
}
.fc-label {
  font-size: var(--fs-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .8px;
  color: var(--muted);
}
.fc-copy {
  flex: 0 0 auto;
  width: auto;
  margin-top: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .3px;
  color: var(--accent-2);
  background: color-mix(in srgb, var(--accent-2) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-2) 35%, transparent);
  border-radius: var(--r-pill);
  padding: var(--sp-1) var(--sp-3);
  cursor: pointer;
  line-height: 1;
}
.fc-copy svg { width: 13px; height: 13px; }
.fc-copy:active { opacity: .65; }
/* The code sits on its own full-width row below the label/Copy, so it can
   never crowd the button horizontally; long values ellipsize. */
.fc-code {
  width: 100%;
  margin-top: 0;
  display: block;
  text-align: left;
  font-family: ui-monospace, "SF Mono", "Menlo", monospace;
  font-size: var(--fs-2xl);
  font-weight: 800;
  letter-spacing: 3px;
  color: var(--ink);
  background: transparent;
  border: none;
  padding: 0;
  line-height: 1.1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}

.friends-addform {
  display: flex;
  gap: var(--sp-2);
  align-items: stretch;
}
.fc-input {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  margin-top: 0;
  font-family: ui-monospace, "SF Mono", "Menlo", monospace;
  font-size: var(--fs-md);
  font-weight: 600;
  letter-spacing: 1.2px;
  text-align: left;
  text-transform: uppercase;
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--card);
  color: var(--ink);
  outline: none;
}
.fc-input:focus { border-color: var(--accent-2); }
.fc-input::placeholder { color: var(--muted); opacity: 1; letter-spacing: .4px; font-size: var(--fs-sm); text-transform: none; }
.fc-add {
  flex: 0 0 auto;
  width: auto;
  margin-top: 0;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: .4px;
  background: var(--accent-2);
  color: #fff;
  border: none;
  cursor: pointer;
  line-height: 1;
}
.fc-add:active { opacity: .7; }

/* Status line below the add form */
.friends-addmsg {
  font-size: var(--fs-sm);
  color: var(--muted);
  min-height: 1.1em;
  padding: 0 var(--sp-1);
  text-align: left;
}

/* ---- Zone 1b: needs-score gate ---- */
.friends-needscore {
  font-size: var(--fs-md);
  color: var(--muted);
  text-align: center;
  padding: var(--sp-6) var(--sp-4);
}

/* ---- Zone 2 + 3: sections (Requests / Friends) ---- */
.friends-section { margin-bottom: var(--sp-5); }
.friends-section:last-child { margin-bottom: 0; }

/* explicit display above defeats the bare [hidden] UA rule */
.friends-codezone[hidden],
.friends-section[hidden] { display: none; }

.friends-subhead {
  font-size: var(--fs-2xs);
  font-weight: 800;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 var(--sp-1) var(--sp-2);
}

/* Grouped card container — iOS-settings look: one surface, hairline rows */
.friends-group {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.friends-list { list-style: none; margin: 0; padding: 0; }

/* Empty-state hint */
.friends-empty {
  font-size: var(--fs-sm);
  color: var(--muted);
  text-align: center;
  padding: var(--sp-4) var(--sp-3);
}

/* ---- Rows ---- */
.fr-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  position: relative;
}
/* hairline divider, inset past the avatar; hidden on the group's last row */
.fr-row::after {
  content: "";
  position: absolute;
  left: 56px; right: 0; bottom: 0;
  height: 1px;
  background: var(--line);
}
.friends-group > ul:last-child .fr-row:last-child::after { display: none; }
.fr-row--muted .fr-name { color: var(--muted); }

.fr-rowmain {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  margin-top: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
  transition: opacity .15s ease;
}
.fr-rowmain:active { opacity: .55; }
.fr-rowmain--static { cursor: default; }

.fr-dot {
  flex: 0 0 auto;
  width: 34px; height: 34px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-sm);
  font-weight: 800;
  color: #0e0e12;
  letter-spacing: .2px;
}
.fr-mid { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.fr-name {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fr-meta { font-size: var(--fs-xs); color: var(--muted); }
.fr-meta b { font-weight: 700; }

.fr-trail { flex: 0 0 auto; display: flex; align-items: center; gap: var(--sp-2); }
.fr-car {
  width: 34px; height: 34px; flex: 0 0 auto; box-sizing: border-box; padding: 4px;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--accent-2) 9%, var(--card));
  border: 1px solid color-mix(in srgb, var(--accent-2) 16%, transparent);
}
.fr-chev { color: var(--muted); opacity: .55; display: flex; }
.fr-chev svg { width: 16px; height: 16px; }

/* Remove (friend) — round ghost, turns danger on press */
.fr-remove {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  margin-top: 0;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  padding: 0;
}
.fr-remove svg { width: 14px; height: 14px; }
.fr-remove:active { background: color-mix(in srgb, var(--danger) 18%, transparent); border-color: var(--danger); color: var(--danger); }

/* Incoming accept / decline minis */
.fr-reqact { flex: 0 0 auto; display: flex; align-items: center; gap: var(--sp-2); }
.fr-acc, .fr-dec {
  width: 30px; height: 30px;
  margin-top: 0;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  padding: 0;
  border: 1px solid;
}
.fr-acc svg, .fr-dec svg { width: 15px; height: 15px; }
.fr-acc {
  color: #2ecf72;
  background: color-mix(in srgb, #2ecf72 14%, transparent);
  border-color: color-mix(in srgb, #2ecf72 45%, transparent);
}
.fr-acc:active { background: #2ecf72; color: #06240f; }
.fr-dec {
  color: var(--muted);
  background: transparent;
  border-color: var(--line);
}
.fr-dec:active { background: color-mix(in srgb, var(--danger) 18%, transparent); border-color: var(--danger); color: var(--danger); }

/* Outgoing pending pill */
.fr-pending {
  flex: 0 0 auto;
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: var(--sp-1) var(--sp-2);
  white-space: nowrap;
  line-height: 1;
}

/* ---- Account (Sign in with Apple) ---- */
/* HIG: the Apple button is black with white text/logo, never re-tinted. */
.apple-btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; padding: 12px 16px; border-radius: 12px; border: none;
  background: #000; color: #fff; font-weight: 600; font-size: 15px;
}
.apple-btn svg { width: 15px; height: 18px; }
[data-theme="dark"] .apple-btn { background: #fff; color: #000; }
.apple-btn.shake { animation: sp-shake .34s ease; }
.acct-hint { margin-top: 6px; font-size: 12px; opacity: .65; text-align: center; }
.acct-linked { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.acct-status { font-weight: 600; font-size: 14px; color: var(--good, #2e9e5b); margin-right: auto; }
.acct-small { font-size: 12px; padding: 6px 10px; }
.acct-danger { color: var(--accent, #ff5247); }
#settings-account, #profile-account { margin: 10px 0; }
#settings-account[hidden], #profile-account[hidden] { display: none; }
