/* JakeCrate platform styles — tokens, buttons, lobby, podium, reveal, TV, kit, shared overlays.
   Carved from public/index.html (Stage 1 monolith split). Load FIRST, before game sheets. */
        /* Display font is BUNDLED (public/vendor/fonts/, OFL — see vendor/ATTRIBUTION.md),
           not Google-hosted: offline hotspot parties used to silently fall back to
           monospace because the fonts.googleapis.com link never resolved. Same family
           name, so every var(--font-display) usage and the canvas literals still work. */
        @font-face {
            font-family: 'Press Start 2P';
            font-style: normal;
            font-weight: 400;
            font-display: swap;
            src: url('/vendor/fonts/press-start-2p.woff2') format('woff2');
        }
        /* Playtest overhaul (2026-07-13): Bungee = new display font (arcade signage,
           Jake's pick from tools/mock/visual-direction.html), Nunito = body font.
           Press Start 2P stays bundled — canvas draw calls still name it directly. */
        @font-face {
            font-family: 'Bungee'; font-style: normal; font-weight: 400; font-display: swap;
            src: url('/vendor/fonts/bungee.woff2') format('woff2');
        }
        /* v2 correction (Jake's second feedback round): ONE bouncy display font PER
           GAME — Bungee stays for Sleight of Hands; the others get their own face
           via --font-display under body[data-game]. Body text stays Nunito. */
        @font-face {
            font-family: 'Baloo 2'; font-style: normal; font-weight: 800; font-display: swap;
            src: url('/vendor/fonts/baloo2.woff2') format('woff2');
        }
        @font-face {
            font-family: 'Shrikhand'; font-style: normal; font-weight: 400; font-display: swap;
            src: url('/vendor/fonts/shrikhand.woff2') format('woff2');
        }
        @font-face {
            font-family: 'Luckiest Guy'; font-style: normal; font-weight: 400; font-display: swap;
            src: url('/vendor/fonts/luckiest.woff2') format('woff2');
        }
        @font-face {
            font-family: 'Titan One'; font-style: normal; font-weight: 400; font-display: swap;
            src: url('/vendor/fonts/titan-one.woff2') format('woff2');
        }
        @font-face {
            font-family: 'Nunito'; font-style: normal; font-weight: 400; font-display: swap;
            src: url('/vendor/fonts/nunito-400.woff2') format('woff2');
        }
        @font-face {
            font-family: 'Nunito'; font-style: normal; font-weight: 700; font-display: swap;
            src: url('/vendor/fonts/nunito-700.woff2') format('woff2');
        }
        *, *::before, *::after { box-sizing: border-box; }
        body, html { margin:0; padding:0; width:100%; height:100%; background:#050508; overflow:hidden; font-family:'Nunito',system-ui,sans-serif; overscroll-behavior:none; touch-action:none; }
        /* height:100% resolves against the LARGE viewport on mobile — the one that
           assumes the URL bar is hidden. Every stage is inset:0 inside an
           overflow:hidden body, so with the browser bar actually showing, whatever
           sits at the bottom of a stage (Squiggle's LEFT/RIGHT vote buttons, the
           "waiting on…" caption, Split Crew's task rows) is pushed off the visible
           area with no way to scroll to it. dvh tracks the viewport that is really
           on screen. Kept behind @supports so older webviews keep the 100% path. */
        @supports (height:100dvh) { body, html { height:100dvh; } }

        /* ── Design tokens + button system ─────────────────────────────────────
           Exactly three button weights. Every screen gets ONE .btn-primary (the
           single thing the player should do next); everything else is secondary
           or quiet. New/migrated UI should use these instead of inline styles. */
        :root {
            /* Theme tokens — the Workshop editor (workshop/, local dev only) can
               retint every one of these live via the 'themeVars' socket event;
               committed overrides ride in config/tunables.json (theme section). */
            --c-card:#0d1525; --c-border:#2a3450;
            /* ink3 raised from #4a6a8a — hints/placeholders now clear 4.5:1 on the dark grounds */
            --c-ink:#fff; --c-ink2:#9fb0cc; --c-ink3:#8296b8;
            --c-brand:#ffeb3b; --c-danger:#c62828;
            /* Ink to use ON TOP of a solid --c-brand fill. Not the same thing as
               --c-brand-ink below, which is brand-coloured text on a light ground.
               Dark-brand themes override this to #fff; the baseline yellow needs
               near-black. Chips/pills that fill with the brand read from here so a
               dark brand can't produce dark-on-dark. */
            --c-on-brand:#111;
            --c-hot:#ff6600;   /* battle orange (VS, timers, round banners) */
            --c-gold:#ffd700;  /* winner gold */
            --c-mint:#8fa;     /* progress/status mint */
            /* ── Text-safe variants ────────────────────────────────────────────
               --c-brand and --c-mint are chosen as FILLS: saturated, high-energy,
               correct behind a solid shape. Used as body text on the cream games
               they fall to 1.0–3.4:1 (2026-08-02 sweep). These two tokens are the
               text-safe counterparts — same hue family, contrast that holds.
               On the dark baseline they are simply the originals; the light game
               themes below override them. Rule of thumb: fills and borders use
               --c-brand / --c-mint, anything you have to READ uses -ink. */
            --c-brand-ink:var(--c-brand);
            --c-mint-ink:var(--c-mint);
            /* ── Portal palette (lobby identity) — the Spawnpoint "Rising Ember" anchors.
               Coral + teal lead; amber is a sparing spark accent; ground is the blue-black.
               Scoped to the lobby chrome + wordmark + hero; not a repo-wide reskin. */
            --portal-coral:#FF5D73; --portal-teal:#35E0C8; --portal-amber:#FFCF5C; --portal-ground:#0A0F1C;
            --font-display:'Bungee', 'Press Start 2P', monospace;
            /* Scale tokens (lobby reskin) — radius/shadow/spacing route through these */
            /* Height reserved for the fixed LEAVE/LOBBY row while it is on screen
               (button box ~27px + its 6px top offset + breathing room). */
            --jc-chrome-h:40px;
            --radius:14px; --radius-sm:10px;
            --shadow-1:0 2px 10px rgba(0,0,0,.35); --shadow-2:0 10px 30px rgba(0,0,0,.45);
            --focus-ring:3px solid var(--c-brand);
        }
        .btn { border:none; cursor:pointer; font-weight:800; letter-spacing:.04em; border-radius:12px; -webkit-tap-highlight-color:transparent; font-family:inherit; flex-grow:0; text-transform:uppercase; transition:filter .1s ease, transform .1s ease, box-shadow .1s ease, background-color .15s ease, border-color .15s ease; }
        .btn:disabled { opacity:.4; cursor:not-allowed; }
        .btn-danger { color:var(--c-danger) !important; border-color:rgba(198,40,40,.5) !important; }
        .btn-primary { background:var(--c-brand); color:#090b12; padding:16px 22px; font-size:1em; }
        .btn-secondary { background:var(--c-card); color:var(--c-ink2); border:1.5px solid var(--c-border); padding:11px 18px; font-size:.8em; }
        .btn-quiet { background:transparent; color:var(--c-ink3); padding:8px 12px; font-size:.8em; }
        .btn-quiet:active, .btn-secondary:active { filter:brightness(1.35); }
        /* Every cartridge has a few bespoke controls outside .btn. Give all real
           buttons the same immediate tap acknowledgement without imposing a
           transform that would fight game-specific positioning/gestures. */
        button { -webkit-tap-highlight-color:transparent; }
        button:not(:disabled):active { filter:brightness(1.16); }
        button:disabled { cursor:not-allowed; }
        button:focus-visible, [role="button"]:focus-visible, input:focus-visible,
        textarea:focus-visible, select:focus-visible {
            outline:3px solid var(--c-brand); outline-offset:3px;
        }
        label:has(input[type="file"]):focus-within {
            outline:3px solid var(--c-brand); outline-offset:3px;
        }
        .btn-secondary.btn-active { background:rgba(94,230,197,.15); border-color:#5ee6c5; color:#5ee6c5; }
        canvas { display:block; touch-action:none; cursor:crosshair; }
        /* Sleight of Hands' full-viewport stage. Starts DOWN: it had no rule of its
           own and nothing ever hid it, so it painted under every other game and
           under the lobby. tsSyncStage() (tacostealth.js) raises and drops it. */
        #gameCanvas { display:none; }
        #global-leave { display:none; position:fixed; top:calc(env(safe-area-inset-top) + 6px); left:6px; z-index:250; background:rgba(198,40,40,0.82); color:#fff; border:none; border-radius:8px; padding:6px 10px; font-size:0.7em; font-weight:bold; letter-spacing:1px; cursor:pointer; -webkit-user-select:none; user-select:none; }
        #global-host-lobby { display:none; position:fixed; top:calc(env(safe-area-inset-top) + 6px); right:6px; z-index:250; background:rgba(255,235,59,0.88); color:#111; border:none; border-radius:8px; padding:6px 10px; font-size:0.7em; font-weight:bold; letter-spacing:1px; cursor:pointer; -webkit-user-select:none; user-select:none; }

        /* ── Fixed chrome reserves its space ───────────────────────────────────
           LEAVE and LOBBY are position:fixed and used to reserve nothing, so the
           stage ran underneath them: the 2026-08-02 sweep caught them painting
           over Split Crew's LAP label and race timer, Sleight of Hands' help
           button, and (in the real party) PikPic's clue banner and the
           "YOU'RE THE CURATOR" title.
           syncGlobalLeave/syncGlobalHostLobby put .jc-chrome on <body> whenever
           either button is up; every full-bleed stage is inset:0, so shifting
           `top` shortens the stage instead of letting it slide under the chrome.
           Sleight of Hands is deliberately excluded — its stage IS the canvas
           play area and moving it would shift the world; its HUD row is nudged
           instead, just below. */
        body.jc-chrome .qp-screen, body.jc-chrome .rr-screen, body.jc-chrome .sc-screen,
        body.jc-chrome #pp-upload-screen, body.jc-chrome #pp-clue-screen, body.jc-chrome #pp-wait-screen,
        body.jc-chrome #pp-submit-screen, body.jc-chrome #pp-vote-screen, body.jc-chrome #pp-result-screen,
        body.jc-chrome #cd-upload-screen, body.jc-chrome #cd-gallery-screen,
        body.jc-chrome #cd-reveal-screen, body.jc-chrome #cd-result-screen,
        body.jc-chrome #sq-battle-screen, body.jc-chrome #sq-results-screen,
        body.jc-chrome #so-screen, body.jc-chrome #so-memorize-screen,
        body.jc-chrome #so-reveal-screen, body.jc-chrome #so-vote-screen,
        body.jc-chrome #kit-screen {
            top:var(--jc-chrome-h);
        }
        /* Canvas games keep their stage; only the HUD row moves clear. */
        body.jc-chrome #nav-desk { top:calc(8px + env(safe-area-inset-top) + var(--jc-chrome-h)); }
        body.jc-chrome #phase-hud { top:calc(48px + env(safe-area-inset-top) + var(--jc-chrome-h)); }
        body.jc-chrome #poke-counter, body.jc-chrome #vp-points-hud { top:calc(88px + env(safe-area-inset-top) + var(--jc-chrome-h)); }
        /* The "+1" popup drifts up 70px as it fades, which walked it straight
           under the LOBBY button at the top-right. Same offset as the rest. */
        body.jc-chrome .vp-float { top:calc(86px + env(safe-area-inset-top) + var(--jc-chrome-h)); }

        /* ── Per-game identity (playtest overhaul, 2026-07-13) ─────────────────
           While a game runs, jcApplyGameTheme() sets body[data-game] and each
           game re-tints --c-brand + gets a signature backdrop. Split Crew is
           LOCKED as-is per Jake's playtest notes — no override on purpose.
           Palettes: tools/mock/visual-direction.html, variant A everywhere
           except Squiggle Fight (ships dark "Neon Versus" B — light A needs a
           dedicated contrast pass before it's safe on this dark-baseline UI). */
        /* Dark themes re-declare -ink alongside their brand so it tracks the retint.
           A custom property that references var() resolves in the scope where it is
           DECLARED, so the :root definition would otherwise stay stuck on #ffeb3b. */
        body[data-game="tacostealth"]   { --c-brand:#ffb648; --c-brand-ink:var(--c-brand); --font-display:'Bungee', sans-serif; background:radial-gradient(circle at 50% 8%,#1b2b21,#0d1512 78%); }
        /* v2 correction: PikPic + Snap Chain go LIGHT (Jake: "not everything dark").
           Token-level flip — inside these scopes the ink/card/border tokens invert,
           so token-driven children restyle themselves; the handful of hardcoded
           dark-ground accents get explicit overrides just below. */
        body[data-game="pikpic"] {
            /* Reconciled 2026-07-16 (Jake: "I don't like the blue") — PikPic's ONE accent
               is the coral its lobby card always had; the cream album ground stays. */
            --c-brand:#ff6242; --font-display:'Baloo 2', sans-serif;
            --c-card:#fffdf6; --c-border:#ddd3c0;
            --c-ink:#28251f; --c-ink2:#5a5346; --c-ink3:#665f54;
            --c-gold:#a8731f; /* gold timers/badges need a darker amber on cream */
            /* Text-safe coral + mint on the cream ground (4.56:1 / 4.65:1).
               The bright originals stay for fills — only READ text uses these. */
            --c-brand-ink:#a33f2a; --c-mint-ink:#386946;
            background:radial-gradient(circle at 50% 8%,#f1ece2,#e7e0d2 78%);
        }
        body[data-game="pikpic"] #pp-upload-screen, body[data-game="pikpic"] #pp-clue-screen,
        body[data-game="pikpic"] #pp-wait-screen, body[data-game="pikpic"] #pp-submit-screen,
        body[data-game="pikpic"] #pp-vote-screen, body[data-game="pikpic"] #pp-result-screen {
            background:radial-gradient(circle at 50% 20%,#f1ece2,#e2dac8 70%); color:#28251f;
        }
        body[data-game="pikpic"] .pp-timer-big { text-shadow:none; }
        body[data-game="pikpic"] .pp-clue-display { background:rgba(255,98,66,.08); border-color:rgba(255,98,66,.3); }
        body[data-game="pikpic"] .btn-primary { color:#fffdf7; }
        body[data-game="coolquip"]      { --c-brand:#ffc94b; --c-brand-ink:var(--c-brand); --font-display:'Shrikhand', serif; background:radial-gradient(circle at 50% 8%,#261440,#160e24 78%); }
        body[data-game="squiggle"] {
            --c-brand:#e8336d; --font-display:'Luckiest Guy', sans-serif;
            --c-card:#fffdf6; --c-border:#d9d2bf;
            --c-ink:#2b2b31; --c-ink2:#585349; --c-ink3:#8b8577;
            --c-gold:#a8731f;
            /* 4.58:1 / 4.51:1 on this game's #efeadd panels */
            --c-brand-ink:#be2a59; --c-mint-ink:#3c704b;
            background:radial-gradient(circle at 50% 8%,#f5f2e9,#eae6d8 78%);
        }
        body[data-game="squiggle"] #so-screen { background:#efeadd; }
        body[data-game="squiggle"] #sq-battle-screen { background:#efeadd; color:#2b2b31; }
        body[data-game="squiggle"] .sq-panel canvas { background:#f5f2e9; border-color:#cfc7b2; }
        body[data-game="squiggle"] .sq-vote-btn { background:rgba(0,0,0,0.05); border-color:#b9b09a; color:#2b2b31; }
        body[data-game="squiggle"] #sq-results-screen { background:rgba(243,239,229,0.97); color:#2b2b31; }
        body[data-game="squiggle"] #so-timer { text-shadow:none; }
        body[data-game="telestrations"] {
            --c-brand:#d81b7a; --font-display:'Titan One', sans-serif;
            --c-mint:#0aa6c9;
            --c-card:#fffdf6; --c-border:#e0d4be;
            --c-ink:#38292f; --c-ink2:#6b5560; --c-ink3:#95808a;
            --c-gold:#a8731f;
            /* The pink already clears 4.73:1 on cream, so brand-ink just tracks it;
               the teal mint does not (2.82:1) and gets a darkened text variant. */
            --c-brand-ink:#be186b; --c-mint-ink:#076e85;
            --c-on-brand:#fff; /* deep pink fill needs white ink, not the #111 baseline */
            background:radial-gradient(circle at 50% 8%,#f7f1e6,#eee5d2 78%);
        }
        body[data-game="telestrations"] #kit-screen { background:radial-gradient(circle at 50% 10%,#f7f1e6,#eae1cd 70%); }
        body[data-game="telestrations"] .kit-item-card { background:#fffdf6; border-color:#e0d4be; }
        /* The colour half of this is now --c-on-brand in the theme block above, so
           every brand-filled chip gets readable ink instead of just this one game. */
        body[data-game="telestrations"] .kit-magnet.chosen { box-shadow:0 0 14px rgba(216,27,122,.35); }
        body[data-game="telestrations"] .btn-primary { color:#fffdf7; }
        /* Lobby cards preview each game's font via inline --gf from JC_GAMES (renderGameShelf) */

        /* Title card — every game opens on a ~4s intro so everyone knows what
           they're playing (jcMaybeTitleCard shows it once per game start). */
        #jc-title-card { display:none; position:fixed; inset:0; z-index:390; flex-direction:column; align-items:center; justify-content:center; gap:14px; padding:32px 20px; text-align:center; color:#fff; background:radial-gradient(circle at 50% 12%, var(--tc-bg1,#221a3d), var(--tc-bg2,#05050a) 80%); }
        #jc-title-card.open { display:flex; animation:tc-in .45s ease-out; }
        #jc-title-card.closing { animation:tc-out .4s ease-in forwards; }
        @keyframes tc-in { from { opacity:0; } }
        @keyframes tc-out { to { opacity:0; } }
        #jc-tc-eyebrow { font-size:.72em; font-weight:800; letter-spacing:.3em; color:var(--tc-acc2,#ff5286); }
        #jc-tc-title { font-family:var(--font-display); font-size:clamp(30px,9vw,54px); line-height:1.05; color:var(--tc-acc,#ffc94b); text-shadow:0 3px 0 rgba(0,0,0,.45), 0 7px 22px rgba(0,0,0,.5); animation:tc-pop .5s cubic-bezier(.2,.9,.25,1.25) backwards; }
        @keyframes tc-pop { from { transform:scale(.7); opacity:0; } }
        #jc-tc-tag { font-size:.95em; color:var(--tc-ink,#f5eedd); opacity:.92; max-width:36ch; line-height:1.5; }
        #jc-tc-chips { display:flex; gap:8px; flex-wrap:wrap; justify-content:center; }
        #jc-tc-chips span { font-size:.66em; font-weight:800; letter-spacing:.08em; border:1px solid var(--tc-acc,#ffc94b); color:var(--tc-acc,#ffc94b); border-radius:99px; padding:6px 12px; opacity:.85; }
        @media (prefers-reduced-motion: reduce){ #jc-title-card.open, #jc-title-card.closing, #jc-tc-title { animation:none; } }

        /* ── Between-round standings beat (playtest: "who's ahead?") ── */
        #round-standings { display:none; position:fixed; inset:0; z-index:385; flex-direction:column; align-items:center; justify-content:center; padding:28px 18px; background:rgba(0,0,0,0.55); }
        #round-standings.open { display:flex; }
        #rs-card { background:var(--c-card); border:1.5px solid var(--c-border); border-radius:18px; padding:22px 20px; width:min(400px,92vw); max-height:76vh; overflow-y:auto; display:flex; flex-direction:column; gap:12px; }
        #rs-title { font-family:var(--font-display); font-size:1.02em; color:var(--c-brand-ink); text-align:center; letter-spacing:.06em; }
        #rs-rows { display:flex; flex-direction:column; gap:7px; }
        .rs-row { display:flex; align-items:center; gap:10px; background:rgba(128,128,128,0.10); border-radius:10px; padding:9px 12px; color:var(--c-ink); font-weight:700; }
        .rs-place { min-width:1.8em; text-align:center; }
        .rs-name { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        .rs-you { display:inline-block; font-size:.62em; font-weight:800; letter-spacing:.08em; background:var(--c-brand); color:#111; border-radius:99px; padding:2px 7px; margin-left:6px; vertical-align:1px; }
        .rs-delta { font-size:.78em; color:var(--c-mint-ink,#5ee6c5); font-weight:800; }
        .rs-score { font-family:monospace; font-size:1.05em; color:var(--c-brand-ink); min-width:2.5em; text-align:right; }
        #rs-sub { font-size:.8em; color:var(--c-ink3); text-align:center; }

        /* ── Universal winner podium ── */
        #podium-overlay { display:none; position:fixed; inset:0; z-index:400; background:radial-gradient(circle at 50% 18%, #221a3d, #05050a 75%); flex-direction:column; align-items:center; justify-content:center; gap:16px; padding:calc(env(safe-area-inset-top) + 24px) 16px calc(env(safe-area-inset-bottom) + 20px); color:#fff; overflow-y:auto; }
        #podium-overlay.open { display:flex; }
        #podium-title { font-family:var(--font-display); font-size:1.05em; color:var(--c-gold); text-shadow:0 0 16px rgba(255,215,0,0.55); text-align:center; line-height:1.4; }
        #podium-sub { font-size:0.92em; color:#cfd6e6; text-align:center; max-width:340px; line-height:1.5; margin-top:-4px; }
        #podium-stage { display:flex; align-items:flex-end; justify-content:center; gap:8px; width:min(440px,96%); margin-top:8px; }
        .podium-col { flex:1 1 0; display:flex; flex-direction:column; align-items:center; gap:4px; min-width:0; }
        .podium-name { font-weight:900; font-size:0.82em; text-align:center; word-break:break-word; line-height:1.15; max-width:100%; }
        .podium-score { font-family:monospace; font-size:0.74em; color:#ffd76b; }
        .podium-medal { font-size:1.5em; line-height:1; }
        .podium-bar { width:100%; border-radius:8px 8px 0 0; display:flex; align-items:center; justify-content:center; box-shadow:0 -2px 12px rgba(0,0,0,0.4) inset; }
        .podium-bar .podium-place { font-family:var(--font-display); font-size:0.9em; color:rgba(0,0,0,0.55); }
        .podium-1 .podium-bar { background:linear-gradient(180deg,#ffe14d,#c79200); height:120px; }
        .podium-2 .podium-bar { background:linear-gradient(180deg,#e2e2e2,#8d8d8d); height:90px; }
        .podium-3 .podium-bar { background:linear-gradient(180deg,#e6975a,#9c5a23); height:66px; }
        #podium-rest { width:min(440px,96%); display:flex; flex-direction:column; gap:5px; }
        .podium-rest-row { display:flex; justify-content:space-between; align-items:center; background:rgba(255,255,255,0.05); border-radius:8px; padding:8px 12px; font-size:0.85em; }
        .podium-rest-row .pr-rank { color:#8a93a8; font-family:monospace; margin-right:10px; }
        .podium-rest-row .pr-score { color:#ffd76b; font-family:monospace; }
        #podium-close { margin-top:8px; }

        /* ── Shared reveal engine (podium + round-standings animations) ──────
           One system for every game's "here's where everyone stands" moment.
           JS side: animateStandingsReveal() / jcCountUp() near showPodium().
           CUSTOMIZATION HOOKS for a future per-game bespoke pass — override on
           a game's own container (they inherit), e.g.
               #bb-round-list { --reveal-accent:#ff9800; --reveal-stagger:140ms; }
             --reveal-stagger     delay between successive rows      (default 90ms)
             --reveal-duration    one row's slide-in time            (default 420ms)
             --reveal-easing      easing for rows/badges/podium bars (springy default)
             --reveal-accent      leader-row ring color              (default --c-brand)
             --reveal-base-delay  flat delay before the first row    (default 0ms)   */
        :root { --reveal-stagger:90ms; --reveal-duration:420ms; --reveal-easing:cubic-bezier(.2,.9,.25,1.25); --reveal-accent:var(--c-brand); --reveal-base-delay:0ms; }
        .reveal-row { animation:reveal-row-in var(--reveal-duration) var(--reveal-easing) calc(var(--reveal-base-delay) + var(--reveal-stagger) * var(--i,0)) backwards; }
        @keyframes reveal-row-in { from { opacity:0; transform:translateY(12px) scale(.96); } }
        .reveal-row.reveal-top { box-shadow:0 0 0 1.5px var(--reveal-accent), 0 0 16px -4px var(--reveal-accent); }
        .reveal-badge { display:inline-block; animation:reveal-badge-pop .45s var(--reveal-easing) calc(var(--reveal-base-delay) + var(--reveal-stagger) * var(--i,0) + 180ms) backwards; }
        @keyframes reveal-badge-pop { from { transform:scale(0) rotate(-25deg); } }

        /* Podium reveal choreography: bars rise 3rd→2nd→1st, medals pop, names/
           scores fade up, gold bar shimmers + drops confetti for 1st place.
           NOTE: --pd-delay values MUST stay in sync with PD_DELAY in showPodium(). */
        #podium-overlay.open { animation:pd-fade-in .3s ease-out; }
        #podium-title, #podium-sub { animation:pd-fade-up .45s ease-out backwards; }
        .podium-col { position:relative; --pd-delay:0ms; }
        .podium-2 { --pd-delay:320ms; }
        .podium-1 { --pd-delay:640ms; }
        .podium-bar { transform-origin:bottom center; animation:pd-bar-rise .55s var(--reveal-easing) var(--pd-delay) backwards; }
        @keyframes pd-bar-rise { from { transform:scaleY(.05); } }
        .podium-medal { animation:pd-medal-pop .5s var(--reveal-easing) calc(var(--pd-delay) + 300ms) backwards; }
        @keyframes pd-medal-pop { 0% { transform:scale(0); opacity:0; } 70% { transform:scale(1.35); opacity:1; } 100% { transform:scale(1); opacity:1; } }
        .podium-name, .podium-score { animation:pd-fade-up .4s ease-out calc(var(--pd-delay) + 260ms) backwards; }
        @keyframes pd-fade-up { from { opacity:0; transform:translateY(8px); } }
        .podium-bar .podium-place { animation:pd-fade-in .35s ease-out calc(var(--pd-delay) + 430ms) backwards; }
        @keyframes pd-fade-in { from { opacity:0; } }
        .podium-1 .podium-bar { position:relative; overflow:hidden; }
        .podium-1 .podium-bar::after { content:''; position:absolute; inset:0; background:linear-gradient(115deg,transparent 32%,rgba(255,255,255,.5) 50%,transparent 68%); transform:translateX(-130%); animation:pd-shine 2.6s ease-in-out calc(var(--pd-delay) + 900ms) infinite; }
        @keyframes pd-shine { 0% { transform:translateX(-130%); } 45%, 100% { transform:translateX(130%); } }
        .pd-confetti { position:absolute; top:-22px; left:0; right:0; height:0; pointer-events:none; }
        .pd-confetti i { position:absolute; top:0; width:7px; height:11px; border-radius:2px; opacity:0; animation:pd-confetti-fall 1.5s ease-in forwards; }
        @keyframes pd-confetti-fall { 0% { opacity:0; transform:translate(0,-8px) rotate(0deg); } 12% { opacity:1; } 100% { opacity:0; transform:translate(var(--dx,0px),150px) rotate(520deg); } }
        @media (prefers-reduced-motion: reduce) {
            .reveal-row, .reveal-badge, #podium-overlay.open, #podium-title, #podium-sub, .podium-bar, .podium-medal, .podium-name, .podium-score, .podium-bar .podium-place { animation-duration:1ms !important; animation-delay:0ms !important; }
            .podium-1 .podium-bar::after, .pd-confetti i { animation:none !important; }
            /* Lobby: no card lift, no adaptive-shell retint sweep */
            #room-lobby, .tv-screen, .game-card, .rl-code, .rl-marquee, .tv-logo, .tv-code, #rl-start-btn { transition:none !important; }
            .game-card:hover { transform:none !important; }
        }

        /* ── TV / spectator screen ── */
        #tv-shell { display:none; }
        body.tv-mode #tv-shell { display:block; }
        /* TV mode never shows the phone-control chrome — hub, lobby, leave button, every
           per-game phone screen — only whichever #tv-* screen is currently active. */
        body.tv-mode #hub-screen, body.tv-mode #room-lobby, body.tv-mode #global-leave, body.tv-mode #global-host-lobby { display:none !important; }
        .tv-screen { position:fixed; inset:0; z-index:55; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:22px; padding:40px; color:#fff; background:radial-gradient(circle at 50% 20%, var(--c-card), #050508 75%); overflow:hidden; }
        .tv-logo { font-family:var(--font-display); font-size:3.4em; letter-spacing:6px; color:var(--c-brand); text-shadow:0 0 32px rgba(255,235,59,0.5); }
        .tv-code { font-family:var(--font-display); font-size:2.2em; letter-spacing:12px; color:#fff; background:rgba(255,255,255,0.06); border:2px solid rgba(255,235,59,0.4); border-radius:14px; padding:14px 32px; }
        #tv-qr { background:var(--c-brand); border-radius:12px; padding:14px; }
        #tv-qr canvas, #tv-qr img { display:block; width:220px !important; height:220px !important; }
        .tv-qr-cap { font-family:monospace; font-size:1.1em; color:var(--c-mint); }
        .tv-player-list { display:flex; flex-wrap:wrap; gap:10px; justify-content:center; max-width:min(900px,90vw); }
        .tv-player-chip { background:rgba(255,255,255,0.08); border:1px solid rgba(255,255,255,0.16); border-radius:10px; padding:8px 18px; font-size:1.15em; font-weight:700; }
        .tv-game-name { font-family:var(--font-display); font-size:1.3em; color:#ffcc00; text-shadow:0 0 10px rgba(255,153,0,0.5); }
        .tv-sub { font-size:1.1em; color:#aaa; }
        /* ── TV lobby: adaptive pack shell (mirrors #room-lobby; other tv-* screens keep
           their neutral ground). JS sets the --shell-* vars from JC_GAMES on selection. */
        #tv-lobby { --shell-acc:var(--c-brand); --shell-bg1:#1b2740; --shell-bg2:#0d1322; --shell-font:var(--font-display);
            background:radial-gradient(circle at 50% 20%,
                color-mix(in oklab, var(--shell-bg1) 46%, #0a0e1a),
                color-mix(in oklab, var(--shell-bg2) 46%, #05070d) 78%);
            transition:background .45s ease; }
        #tv-lobby .tv-logo { color:var(--shell-acc); text-shadow:0 0 32px color-mix(in srgb, var(--shell-acc) 45%, transparent); transition:color .45s ease; }
        #tv-lobby .tv-code { border-color:color-mix(in srgb, var(--shell-acc) 55%, transparent); transition:border-color .45s ease; }
        #tv-lobby .tv-game-name { font-family:var(--shell-font); color:var(--shell-acc); text-shadow:none; transition:color .45s ease; }
        .tv-timer { font-family:var(--font-display); font-size:1.6em; color:#ff4444; text-shadow:0 0 10px #ff0000; }
        .tv-header-row { position:relative; width:100%; display:flex; align-items:center; justify-content:space-between; padding:0 12px; }
        /* PikPic */
        .tv-pp-clue { font-size:2.2em; font-weight:900; text-align:center; }
        .tv-pp-progress { font-family:monospace; font-size:1.1em; color:var(--c-mint); }
        .tv-pp-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(180px,1fr)); gap:18px; width:min(1200px,92vw); max-height:60vh; overflow-y:auto; }
        .tv-pp-card { position:relative; border-radius:14px; overflow:hidden; border:3px solid rgba(255,255,255,0.15); background:#111; }
        .tv-pp-card img { width:100%; height:160px; object-fit:cover; display:block; }
        .tv-pp-card .tv-pp-votes { position:absolute; bottom:0; left:0; right:0; background:rgba(0,0,0,0.72); padding:6px 10px; font-family:monospace; font-size:0.95em; }
        .tv-pp-card.tv-pp-correct { border-color:#4caf50; box-shadow:0 0 22px rgba(76,175,80,0.6); }
        /* Punch-Up */
        .tv-qp-prompt { font-size:1.8em; font-weight:900; text-align:center; max-width:min(1000px,90vw); }
        .tv-qp-versus { display:flex; align-items:stretch; gap:24px; width:min(1100px,92vw); }
        .tv-qp-side { flex:1; background:rgba(255,255,255,0.05); border:2px solid rgba(255,255,255,0.14); border-radius:16px; padding:18px; display:flex; flex-direction:column; gap:10px; align-items:center; text-align:center; transition:border-color .3s, background .3s; }
        .tv-qp-side.tv-qp-winner { border-color:var(--c-gold); background:rgba(255,215,0,0.1); }
        .tv-qp-name { font-weight:900; font-size:1.15em; color:#ffcc00; }
        .tv-qp-answer { font-size:1.3em; min-height:1.6em; }
        .tv-qp-vs { align-self:center; font-family:var(--font-display); font-size:1.4em; color:var(--c-hot); }
        .tv-qp-votebar { width:100%; height:16px; border-radius:8px; background:#222; overflow:hidden; }
        .tv-qp-votefill { height:100%; width:0%; background:linear-gradient(90deg,#1565c0,#00e5ff); transition:width .5s ease-out; }
        .tv-qp-votecount { font-family:monospace; color:var(--c-mint); }
        .tv-qp-progress { font-family:monospace; font-size:1em; color:var(--c-mint); }
        /* Rizz or Roast — battle cards reuse .tv-qp-*; only the audio-speaking glow is new */
        .tv-qp-side.tv-rr-speaking { border-color:#e040fb; background:rgba(224,64,251,0.12); }
        /* Squiggle Fight */
        .tv-sq-prompt { font-size:1.5em; font-weight:900; color:#fff; text-align:center; max-width:92vw; }
        .tv-sq-stage { display:flex; align-items:center; gap:28px; }
        .tv-sq-side { display:flex; flex-direction:column; align-items:center; gap:10px; }
        .tv-sq-side canvas { height:min(58vh,640px); aspect-ratio:450/800; background:#111; border:3px solid rgba(255,255,255,0.14); border-radius:12px; transition:border-color .3s, box-shadow .3s; }
        .tv-sq-side canvas.sq-winner { border-color:var(--c-gold); box-shadow:0 0 34px rgba(255,215,0,0.45); }
        .tv-sq-standings { display:flex; flex-direction:column; gap:8px; min-width:min(560px,90vw); }
        .tv-sq-row { display:flex; justify-content:space-between; gap:18px; background:rgba(255,255,255,0.05); border:2px solid rgba(255,255,255,0.12); border-radius:10px; padding:10px 18px; font-size:1.15em; }
        .tv-sq-row .pts { color:var(--c-brand); font-family:monospace; }
        /* Crop Detective TV — same viewport mechanics, living-room sized */
        .tv-cd-viewport { width:min(62vh,58vw); }
        #tv-cd-gallery img { width:100%; aspect-ratio:1; object-fit:cover; border-radius:12px; border:3px solid rgba(255,255,255,0.18); }
        /* Split Crew */
        .tv-sc-roster { display:flex; flex-wrap:wrap; gap:16px; justify-content:center; }
        .tv-sc-team { background:rgba(255,255,255,0.05); border:2px solid rgba(255,255,255,0.14); border-radius:14px; padding:14px 20px; min-width:220px; text-align:center; }
        .tv-sc-team.tv-sc-crashed { border-color:#b71c1c; background:rgba(183,28,28,0.12); opacity:0.7; }
        .tv-sc-team-driver { font-weight:900; font-size:1.1em; color:#ffcc00; }
        .tv-sc-team-crew { font-size:0.85em; color:#aaa; }
        .tv-sc-progress-bar { width:100%; height:10px; border-radius:5px; background:#222; overflow:hidden; margin-top:8px; }
        .tv-sc-progress-fill { height:100%; background:linear-gradient(90deg,#4caf50,#8bc34a); transition:width .4s ease-out; }
        #tv-sc-race-canvas { width:min(1400px,94vw); height:min(70vh,760px); }
        .tv-sc-reveal { width:min(1200px,92vw); display:flex; flex-wrap:wrap; gap:14px; justify-content:center; }
        /* Sleight of Hands */
        #tv-soh-canvas { position:absolute; inset:0; width:100%; height:100%; }

        /* ── Hub landing ── */
        #hub-screen { --spawna-ground:#111522; --spawna-shape:#f2e9dc; --spawna-ember:#ff754c; --spawna-edge:#2a2f3d; display:flex; flex-direction:column; align-items:stretch; position:absolute; inset:0; z-index:50; background:var(--spawna-ground); color:var(--spawna-shape); overflow-x:hidden; overflow-y:auto; scroll-behavior:smooth; }
        .hub-start { position:relative; min-height:100%; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:18px; padding:calc(28px + env(safe-area-inset-top)) 24px calc(46px + env(safe-area-inset-bottom)); overflow:hidden; isolation:isolate; }
        .hub-start::after { content:""; position:absolute; width:min(76vw,620px); aspect-ratio:1; right:min(-28vw,-120px); bottom:min(-42vw,-240px); border:1px solid var(--spawna-edge); border-radius:50%; z-index:-1; }
        .hub-start::before { content:""; position:absolute; width:270px; height:270px; border-radius:50%; background:color-mix(in srgb,var(--spawna-ember) 10%,transparent); filter:blur(80px); z-index:-1; }
        .hub-brand { display:flex; align-items:center; gap:15px; margin-bottom:2px; transform:rotate(-1deg); }
        .hub-mark { width:64px; height:64px; color:var(--spawna-shape); }
        .hub-logo { margin:0; font-family:'Nunito',system-ui,sans-serif; font-size:3.15em; line-height:1; font-weight:900; letter-spacing:-.07em; color:var(--spawna-shape); text-shadow:none; }
        .hub-sub { font-family:'Nunito',system-ui,sans-serif; color:var(--spawna-ember); letter-spacing:.14em; font-size:.68em; font-weight:900; margin-top:-5px; }
        .hub-label { width:min(380px,92%); font-size:.68em; font-weight:900; letter-spacing:.14em; color:var(--spawna-shape); opacity:.68; text-transform:uppercase; margin-bottom:-12px; text-align:left; }
        #hubNameInput { width:min(380px,92%); padding:16px; font-size:1.1em; text-align:left; border-radius:16px; border:2px solid var(--spawna-edge); font-weight:900; letter-spacing:.1em; text-transform:uppercase; background:#242935; color:var(--spawna-shape); box-shadow:0 8px 24px rgba(0,0,0,.18); }
        .hub-btn-row { display:flex; gap:10px; width:min(380px,92%); align-items:stretch; }
        .hub-btn-row .btn { flex:1; border-radius:16px; text-transform:none; letter-spacing:0; font-size:.94em; }
        #hubCreateBtn { flex:1.65; background:var(--spawna-ember); color:var(--spawna-ground); box-shadow:0 8px 0 #b83428,0 16px 30px rgba(0,0,0,.24); }
        #hubCreateBtn:active { transform:translateY(5px); box-shadow:0 3px 0 #b83428; }
        #hubJoinBtn { background:transparent; color:var(--spawna-shape); border-color:var(--spawna-shape); }
        #hubCodeWrap { display:none; flex-direction:column; align-items:center; gap:10px; width:min(380px,92%); }
        #hubCodeInput { width:100%; padding:16px; font-size:1.4em; text-align:center; border-radius:14px; border:1px solid var(--spawna-edge); font-weight:800; text-transform:uppercase; letter-spacing:8px; background:#242935; color:var(--spawna-shape); max-width:220px; }
        #hubJoinConfirm { background:var(--spawna-shape); color:var(--spawna-ground); }
        .hub-games-link { position:absolute; bottom:calc(15px + env(safe-area-inset-bottom)); color:var(--spawna-shape); opacity:.48; font-size:.64em; font-weight:900; letter-spacing:.16em; text-decoration:none; text-transform:uppercase; }
        .hub-games { min-height:100%; padding:72px max(24px,calc((100vw - 900px)/2)) 90px; background:#f5f1e9; color:#171922; }
        .hub-games h2 { margin:0 0 26px; font-family:'Nunito',system-ui,sans-serif; font-size:clamp(2rem,6vw,4rem); letter-spacing:-.06em; }
        .hub-game-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:14px; }
        .hub-game-preview { min-height:180px; display:flex; flex-direction:column; justify-content:flex-end; padding:20px; border-radius:20px; color:var(--game-ink,#fff); background:linear-gradient(145deg,var(--game-a),var(--game-b)); box-shadow:0 10px 24px rgba(17,21,34,.12); }
        .hub-game-preview > span { margin-bottom:auto; font-size:1.5rem; }
        .hub-game-preview h3 { margin:18px 0 3px; font-family:var(--game-font,'Nunito'),system-ui,sans-serif; font-size:clamp(1rem,2.4vw,1.35rem); line-height:1.05; }
        .hub-game-preview p { margin:0; opacity:.7; font-size:.72em; font-weight:800; letter-spacing:.08em; text-transform:uppercase; }
        .hub-game-preview.pikpic { --game-a:#f1ece2; --game-b:#e7e0d2; --game-ink:#28251f; --game-font:'Baloo 2'; }
        .hub-game-preview.wisecrack { --game-a:#261440; --game-b:#160e24; --game-font:'Shrikhand'; }
        .hub-game-preview.squiggle { --game-a:#f5f2e9; --game-b:#eae6d8; --game-ink:#2b2b31; --game-font:'Luckiest Guy'; }
        .hub-game-preview.sleight { --game-a:#1b2b21; --game-b:#0d1512; --game-font:'Bungee'; }
        .hub-game-preview.split { --game-a:#2b2416; --game-b:#131006; --game-font:'Bungee'; }
        .hub-game-preview.snap { --game-a:#f7f1e6; --game-b:#eee5d2; --game-ink:#38292f; --game-font:'Titan One'; }
        @media(max-width:620px){ .hub-game-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.hub-game-preview{min-height:150px;padding:16px} }
        #hubJoinConfirm { width:100%; }
        /* Keyboard focus is visible on every lobby-layer control (was outline:none) */
        #hub-screen :focus-visible, #room-lobby :focus-visible, #tv-shell :focus-visible {
            outline:3px solid var(--shell-acc, var(--c-brand)); outline-offset:2px; }

        /* ── Room lobby ── */
        /* ── Adaptive pack shell — a clean tokenized stage that retints to whichever
           cartridge the room is looking at. JS (jcShellTheme) sets the --shell-* vars
           from JC_GAMES; neutral defaults = brand yellow on the deep-blue stage.
           No texture under small text (the scanline pattern is gone on purpose). */
        #room-lobby { display:none;
            --shell-acc:var(--portal-coral); --shell-bg1:#1b2740; --shell-bg2:#0d1322; --shell-font:var(--font-display);
            position:absolute; inset:0; z-index:48;
            /* The gradient moved to the two cross-faded .rl-shell-layer elements —
               animating `background` here repainted the whole viewport for 450ms
               (the flash) and made anything pinned over it judder while scrolling.
               This stays a flat opaque base so there is never a transparent frame. */
            background:#0a0e1a; isolation:isolate;
            flex-direction:column; align-items:center;
            /* Bottom pad clears the fixed #rl-actionbar so the last card row is reachable. */
            /* Bottom pad clears the pinned #rl-actionbar. 176px covers its measured
               149-151px plus slack for a marquee that wraps to two lines. Static on
               purpose: a JS measure-and-set needed a layout read on every lobby
               entry and bought nothing this does not. */
            padding:5vh 16px calc(176px + env(safe-area-inset-bottom)); color:var(--c-ink);
            overflow-y:auto; -webkit-overflow-scrolling:touch; overscroll-behavior:contain;
            touch-action:pan-y; gap:12px; }
        /* Full-bleed tint layers. fixed (not absolute) so scrolling never moves them;
           only opacity animates, which the compositor handles without repainting. */
        /* z-index:-1 puts the layers behind every sibling without touching any of
           them. The first version instead forced `position:relative` on all other
           children to lift them above — which silently broke every child relying on
           its own positioning: it un-pinned #rl-actionbar, and knocked .rl-topbar
           out of its absolute top-right corner into the centre of the flow.
           #room-lobby isolates, so the negative z-index stays inside the lobby and
           still paints above the lobby's own background. */
        .rl-shell-layer { position:fixed; inset:0; z-index:-1; pointer-events:none;
            opacity:0; transition:opacity .45s ease; will-change:opacity;
            transform:translateZ(0); }
        @media (prefers-reduced-motion: reduce) { .rl-shell-layer { transition:none; } }

        .skip-link { position:absolute; left:-9999px; top:calc(8px + env(safe-area-inset-top)); z-index:10;
            background:var(--c-card); color:var(--c-ink); padding:12px 16px; border-radius:var(--radius-sm); font-weight:800; text-decoration:none; }
        .skip-link:focus { left:8px; }
        /* Wordmark: portal-mark SVG + JAKECRATE in solid white (the old white+amber
           two-tone split is retired). */
        .rl-logo { display:flex; align-items:center; justify-content:center; gap:10px;
            font-family:var(--font-display); font-size:1.5em; letter-spacing:3px; color:var(--c-ink);
            text-shadow:0 2px 0 rgba(0,0,0,.4); margin:0; font-weight:normal; }
        .rl-portal-mark { display:none; width:30px; height:30px; flex-shrink:0;
            filter:drop-shadow(0 2px 6px color-mix(in srgb, var(--portal-coral) 40%, transparent)); }
        .rl-spawna-mark { width:34px; height:34px; flex-shrink:0; color:var(--c-ink); }
        .rl-wordmark { color:var(--c-ink); }
        .rl-code { font-family:var(--font-display);
            font-size:1.5em; font-weight:normal; letter-spacing:8px;
            color:var(--shell-acc);
            background:rgba(0,0,0,0.38);
            border:2px solid color-mix(in srgb, var(--shell-acc) 70%, transparent);
            border-radius:var(--radius-sm);
            padding:13px 12px 13px 20px;
            box-shadow:var(--shadow-1);
            transition:color .45s ease, border-color .45s ease; }
        .rl-hint { color:var(--c-ink3); font-size:0.8em; font-weight:700; letter-spacing:1px; }
        .rl-code-label { font-size:0.66em; font-weight:700; letter-spacing:.24em; color:var(--c-ink3); }
        #rl-players { display:flex; flex-wrap:wrap; gap:8px; justify-content:center; max-width:420px; }
        .lobby-chip { background:rgba(255,255,255,0.08); border:1px solid #3a4a66; border-radius:20px; padding:7px 12px; font-weight:bold; font-size:0.9em; user-select:none; -webkit-user-select:none; display:inline-grid; grid-template-columns:auto auto; align-items:center; gap:2px 5px; }
        .lobby-player-id { display:inline-flex; align-items:center; gap:4px; }
        .lobby-player-actions { display:inline-flex; align-items:center; }
        .lobby-tagline { grid-column:1/-1; color:#9aa8bf; font-size:.72em; font-weight:650; line-height:1.2; text-align:left; padding-left:31px; max-width:190px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        /* ── Cartridge rack — two-across grid of tiles, every visual from ONE JC_GAMES
           record (inline vars set by renderGameShelf): --ga accent, --gb1/--gb2 gradient,
           --gi name ink, --gf typeface, --gs per-face optical-size compensation. */
        #rl-game-pick { display:grid; grid-template-columns:1fr 1fr; gap:10px; width:min(380px,92%); margin-top:4px; }
        .game-card { position:relative; display:flex; flex-direction:column; justify-content:space-between;
            gap:8px; min-height:122px; text-align:left; padding:12px;
            font-family:inherit; font-size:1em; text-transform:none; flex-grow:0; /* undo the global button rule */
            cursor:pointer; user-select:none; -webkit-user-select:none;
            color:var(--gi, var(--c-ink));
            background:linear-gradient(150deg, var(--gb1, var(--c-card)), var(--gb2, #0d1322));
            border:1.5px solid color-mix(in srgb, var(--ga, var(--c-border)) 45%, transparent);
            border-radius:var(--radius); box-shadow:var(--shadow-1);
            transition:transform .12s ease, box-shadow .12s ease; }
        .game-card:hover { transform:translateY(-3px);
            box-shadow:0 0 0 1.5px var(--ga, var(--c-brand)), 0 10px 22px rgba(0,0,0,.45); }
        .game-card.selected { box-shadow:0 0 0 2.5px var(--ga, var(--c-brand)),
            0 0 24px color-mix(in srgb, var(--ga, var(--c-brand)) 40%, transparent), var(--shadow-2); }
        .game-card.selected::after { content:"✓"; position:absolute; top:8px; right:8px;
            width:22px; height:22px; border-radius:50%; background:var(--ga, var(--c-brand)); color:#0a0c12;
            font-weight:800; font-size:.8em; display:grid; place-items:center; }
        #rl-pick-title { font-family:var(--font-display); font-size:.78em; letter-spacing:.14em; color:var(--c-ink2); margin:4px 0 2px; text-align:center; }
        .game-card.soon { opacity:.45; cursor:default; pointer-events:none; }
        .gc-icon { font-size:2.05em; line-height:1; flex-shrink:0; filter:drop-shadow(0 2px 4px rgba(0,0,0,.35)); }
        .gc-name { font-family:var(--gf, var(--font-display)); font-size:calc(1.04rem * var(--gs, 1));
            line-height:1.18; text-wrap:balance; }
        .gc-foot { display:flex; align-items:center; justify-content:space-between; min-height:1.2em; gap:4px; }
        .gc-kit { font-size:.6em; font-weight:800; letter-spacing:.1em; color:var(--gi, var(--c-ink)); opacity:.85;
            border:1px solid color-mix(in srgb, var(--ga, var(--c-border)) 65%, transparent); border-radius:8px; padding:2px 7px; }
        .gc-cap { margin-left:auto; font-size:.62em; font-weight:900; letter-spacing:.06em; color:var(--gi, var(--c-ink)); opacity:.8;
            border:1px solid color-mix(in srgb, var(--ga, var(--c-border)) 55%, transparent); border-radius:999px; padding:3px 7px; white-space:nowrap; }
        .gc-voters { display:flex; flex-wrap:wrap; gap:1px; font-size:1.05em; line-height:1; min-height:1.1em; }
        /* Action bar flows inline beneath the game tiles — it used to be a sticky bottom
           dock (position:sticky; bottom:0 + margin-top:auto + a gradient), which overlapped
           the last cards of the 6-game grid and juddered while scrolling. Now START GAME and
           the preview marquee scroll WITH the content. */
        /* Pinned, not sticky. The earlier sticky version juddered — but the cause was
           the full-viewport background repaint behind it, not sticky itself. With the
           tint moved to cross-faded layers, a FIXED bar sits outside the scroll flow
           entirely and the compositor never has to re-derive its position.
           Deliberately no backdrop-filter: it would reintroduce per-frame GPU work
           and the judder with it. */
        #rl-actionbar { position:fixed; left:0; right:0; bottom:0; z-index:3;
            display:flex; flex-direction:column; align-items:center; gap:4px;
            padding:8px 16px calc(8px + env(safe-area-inset-bottom));
            /* Opaque, not a fade-out: cards scroll UNDER this bar, and a translucent
               top edge let their icons and names ghost through it. A hairline rule
               reads as a deliberate dock instead of a rendering fault. */
            background:#05070d; border-top:1px solid var(--c-border);
            transform:translateZ(0); pointer-events:none; }
        #rl-actionbar > * { pointer-events:auto; width:min(380px,100%); }
        /* Marquee: the action bar doubles as the preview pane — name in the game's own
           face + the blurb, for whatever card is hovered/focused/selected. */
        .rl-marquee { font-family:var(--shell-font); font-size:1.08em; letter-spacing:.05em; color:var(--shell-acc);
            min-height:1.35em; transition:color .45s ease; text-shadow:0 2px 8px rgba(0,0,0,.5); text-align:center; }
        .rl-marquee-desc { font-size:.82em; font-weight:700; color:var(--c-ink2); text-align:center;
            min-height:2.5em; max-width:98%; line-height:1.25; }
        #rl-start-btn { width:100%; text-transform:uppercase; display:none;
            background:var(--shell-acc); transition:background .45s ease; }
        #rl-start-btn:disabled { opacity:1; background:#4b5363; color:#d9deea; box-shadow:none; }
        #rl-wait { font-weight:700; color:var(--c-ink3); font-size:0.85em; text-align:center; }
        /* Room code + invite live on one row; QR/share collapse into a panel */
        .rl-code-row { display:flex; align-items:center; gap:10px; }
        #rl-invite { display:none; flex-direction:column; align-items:center; gap:8px; }
        #rl-invite.open { display:flex; }
        /* Leave/Close live behind the ⋯ corner menu — rare, destructive actions
           shouldn't take permanent lobby real estate (and CLOSE was a misclick hazard) */
        .rl-topbar { position:absolute; top:calc(8px + env(safe-area-inset-top)); right:calc(10px + env(safe-area-inset-right)); z-index:6; }
        #rl-menu-btn { font-size:1.5em; line-height:1; padding:8px 14px; min-width:44px; min-height:44px; color:var(--c-ink2); }
        .btn-quiet { min-height:44px; }
        #rl-menu { position:absolute; right:0; top:44px; background:var(--c-card); border:1.5px solid var(--c-border); border-radius:12px; display:none; flex-direction:column; min-width:180px; overflow:hidden; box-shadow:0 8px 24px rgba(0,0,0,.5); }
        #rl-menu button { background:none; border:none; padding:14px 18px; color:var(--c-ink2); text-align:left; font-size:0.85em; font-weight:700; cursor:pointer; font-family:inherit; }
        #rl-menu button:active { background:rgba(255,255,255,.06); }
        #rl-menu #rl-close-btn { color:var(--c-danger); }

        /* ── Shared game HUD ── */
        #monitor-hud { position:absolute; top:calc(10px + env(safe-area-inset-top)); left:calc(12px + env(safe-area-inset-left)); display:flex; align-items:center; gap:8px; pointer-events:none; z-index:10; font-family:monospace; color:#fff; font-size:1em; text-shadow:2px 2px 2px #000; max-width:46vw; }
        #cam-title { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        @keyframes pulse { 50% { opacity:1; } }
        .recording { animation:pulse 1s infinite; }
        #nav-desk { position:absolute; top:calc(8px + env(safe-area-inset-top)); right:calc(12px + env(safe-area-inset-right)); display:flex; gap:6px; z-index:10; }
        .nav-btn { padding:7px 12px; background:rgba(25,25,30,0.9); color:white; border:1px solid #555; border-radius:6px; font-weight:bold; font-size:0.8em; cursor:pointer; white-space:nowrap; }
        #phase-hud { position:absolute; top:calc(48px + env(safe-area-inset-top)); left:50%; transform:translateX(-50%); display:flex; gap:12px; align-items:center; z-index:10; pointer-events:none; font-family:monospace; color:#fff; font-size:1.3em; font-weight:bold; text-shadow:0 0 8px rgba(255,255,255,0.4),2px 2px 2px #000; }
        #timer-hud { font-size:1.8em; color:var(--c-brand); text-shadow:0 0 12px rgba(255,235,59,0.8),2px 2px 2px #000; }
        /* Spotlight / poke HUDs — centered below the phase row so nothing overlaps */
        #poke-counter, #vp-points-hud { position:absolute; top:calc(88px + env(safe-area-inset-top)); left:50%; transform:translateX(-50%); z-index:10; font-family:monospace; font-weight:bold; pointer-events:none; text-align:center; white-space:nowrap; }
        #poke-counter { color:#ff5252; font-size:1em; text-shadow:0 0 8px rgba(255,60,60,0.6),1px 1px 2px #000; }
        #vp-points-hud { color:var(--c-brand); font-size:1.15em; text-shadow:0 0 10px rgba(255,200,0,0.8),1px 1px 2px #000; }
        /* Compact locate-my-hand control, clear of the unified bottom hide bar. */
        #findMeBtn { position:absolute; right:calc(12px + env(safe-area-inset-right)); bottom:calc(122px + env(safe-area-inset-bottom)); z-index:11; width:46px; height:46px; min-width:46px; padding:0; display:grid; place-items:center; flex-grow:0; color:#eef7ff; background:rgba(10,14,24,.82); border:1px solid rgba(176,199,232,.48); border-radius:50%; box-shadow:0 8px 24px rgba(0,0,0,.38); backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px); }
        #findMeBtn svg { width:23px; height:23px; fill:none; stroke:currentColor; stroke-width:1.8; stroke-linecap:round; }
        #findMeBtn:active { transform:scale(.94); background:#286bd1; }
        body.ts-paint-mode #findMeBtn { bottom:calc(180px + env(safe-area-inset-bottom)); }
        /* MOVE/PAINT segmented toggle — both states always visible, active one lit.
           Shared by the rail control (#modeBtn) and the floating pill (#ts-mode-float). */
        .ts-mode-seg button { opacity:0.45; background:#26262e; border:1px solid #444; color:#fff; }
        .ts-mode-seg button.active { opacity:1; }
        .ts-mode-seg button.seg-move.active { background:#1565c0; border-color:#7CC4FF; box-shadow:0 0 12px rgba(21,101,192,0.7); }
        .ts-mode-seg button.seg-paint.active { background:#2e7d32; border-color:#7CFC9A; box-shadow:0 0 12px rgba(46,160,67,0.6); }
        #ts-mode-float { position:absolute; z-index:10; display:none; gap:4px; background:rgba(18,18,28,0.94); border:1px solid #555; border-radius:999px; padding:4px; box-shadow:0 2px 10px rgba(0,0,0,0.6); pointer-events:auto; touch-action:none; }
        #ts-mode-float button { flex-grow:0; padding:6px 10px; border-radius:999px; font-size:0.72em; font-weight:bold; touch-action:none; }

        /* The one shared toast (showToast). z-index must clear every screen/overlay it can
           fire over: game screens 55, lightbox 200, podium 400, reconnect overlay 600 — at
           25 it was literally invisible everywhere except the Sleight of Hands canvas. */
        #volunteer-toast { display:none; position:fixed; top:calc(92px + env(safe-area-inset-top)); left:50%; transform:translateX(-50%); z-index:650; background:rgba(13,21,37,0.97); color:#f2f5fb; font-family:monospace; padding:10px 22px; border-radius:10px; font-size:0.9em; text-align:center; pointer-events:none; border:1.5px solid var(--c-border); box-shadow:0 6px 24px rgba(0,0,0,.55); max-width:92vw; white-space:normal; animation:toast-in 0.25s ease-out; }
        @keyframes toast-in { from{opacity:0;transform:translateX(-50%) translateY(-8px)} to{opacity:1;transform:translateX(-50%) translateY(0)} }
        @keyframes btn-wiggle { 0%,88%,100%{transform:rotate(0deg)} 91%{transform:rotate(-3deg)} 95%{transform:rotate(3deg)} 98%{transform:rotate(-1deg)} }
        #whistleBtn { animation: btn-wiggle 5s ease-in-out infinite; }
        #score-list { max-height:52vh; overflow-y:auto; scrollbar-width:thin; scrollbar-color:#444 transparent; }
        #player-count { font-size:0.7em; color:#888; font-family:monospace; margin-left:4px; }
        #unified-scoreboard { background:var(--c-card); border:1px solid var(--c-border); border-radius:12px; padding:14px 16px; display:none; flex-direction:column; gap:8px; width:min(420px,92%); font-family:monospace; color:white; box-shadow:0 4px 24px rgba(0,0,0,0.7); }
        #unified-scoreboard .sb-title { font-size:0.9em; font-weight:bold; letter-spacing:2px; color:var(--c-brand); border-bottom:1px solid var(--c-border); padding-bottom:6px; text-align:center; }
        #unified-scoreboard .sb-row { display:flex; align-items:center; gap:0; padding:5px 2px; border-radius:4px; font-size:0.82em; }
        #unified-scoreboard .sb-row:nth-child(odd) { background:rgba(255,255,255,0.03); }
        #unified-scoreboard .sb-rank { width:22px; color:var(--c-ink3); font-size:0.9em; text-align:center; flex-shrink:0; }
        #unified-scoreboard .sb-name { flex:1; font-weight:bold; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-right:4px; }
        #unified-scoreboard .sb-col { width:30px; text-align:center; font-size:0.85em; }
        #unified-scoreboard .sb-pts { width:46px; text-align:right; font-size:0.9em; font-weight:bold; color:var(--c-brand); flex-shrink:0; }
        #unified-scoreboard .sb-header { display:flex; align-items:center; font-size:0.68em; color:var(--c-ink3); padding:0 2px 2px; }
        #unified-scoreboard .sb-row.sb-leader { background:rgba(94,230,197,0.09); }
        #unified-scoreboard .sb-legend { font-size:0.6em; color:var(--c-ink3); text-align:center; letter-spacing:0.3px; padding-top:2px; border-top:1px solid var(--c-border); }
        /* keep old IDs for backward compat but hide them */
        #scoreboard, #so-scoreboard { display:none !important; }
        #scoreboard .sb-title { font-size:0.85em; font-weight:bold; letter-spacing:2px; color:var(--c-brand); border-bottom:1px solid #333; padding-bottom:6px; margin-bottom:2px; text-align:center; }
        #scoreboard .sb-header { display:flex; justify-content:space-between; font-size:0.65em; color:#666; padding:0 2px; }
        .sb-row { display:flex; align-items:center; gap:6px; padding:4px 2px; border-radius:4px; font-size:0.82em; }
        .sb-row:nth-child(odd) { background:rgba(255,255,255,0.03); }
        .sb-rank { width:18px; color:#666; font-size:0.9em; text-align:center; flex-shrink:0; }
        .sb-name { flex:1; font-weight:bold; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        .sb-stat { width:44px; text-align:center; font-size:0.9em; }
        .sb-survivals { color:#81c784; }
        .sb-catches { color:#ef9a9a; }
        .sb-empty { color:#555; font-size:0.8em; text-align:center; padding:8px 0; }
        #pokeBtn { display:none; position:absolute; bottom:calc(26px + env(safe-area-inset-bottom)); left:50%; transform:translateX(-50%); z-index:16; padding:16px 46px; font-size:1.35em; font-weight:bold; letter-spacing:2px; color:#fff; background:var(--c-danger); border:none; border-radius:40px; box-shadow:0 6px 22px rgba(0,0,0,0.6); text-transform:uppercase; cursor:pointer; }
        #pokeBtn:active { transform:translateX(-50%) scale(0.94); }
        #pokeBtn:disabled { background:#666; opacity:0.55; }
        #lobbyDoneBtn { display:none; position:absolute; top:calc(12px + env(safe-area-inset-top)); left:50%; transform:translateX(-50%); z-index:17; padding:12px 30px; font-size:1em; font-weight:bold; letter-spacing:1px; color:#fff; background:#2e7d32; border:none; border-radius:30px; box-shadow:0 4px 16px rgba(0,0,0,0.5); text-transform:uppercase; cursor:pointer; }
        #framer-modal { display:none; width:300px; top:50%; transform:translate(-50%,-50%); z-index:700; }

        /* Unified rules/onboarding overlay — fixed so it also covers the ts full-screen canvas.
           z-index tops every game screen (max 250) but stays under podium (400) / reconnect (600). */
        #jc-rules-overlay { position:fixed; z-index:300; }
        /* GET READY strip — rides above the rules overlay (300) so the countdown/ready
           state stays visible while reading the how-to; under podium (400)/reconnect (600). */
        /* It is a CARD, not a pill. It used to be `left:50%` + `border-radius:999px`
           with no width or overflow at all: an absolutely-positioned box with
           left:50%/right:auto only gets ~50vw of shrink-to-fit budget, while the
           nowrap text and the wrapping roster inside both measured wider than
           that. The box grew past the viewport, chips spilled off the right edge,
           and a 999px radius on a ~250px-tall box clamps to 50% — which is where
           the giant yellow ellipse came from. Explicit width + column flow +
           a real radius + overflow containment, so any player count fits. */
        #jc-prep-strip { display:none; position:fixed; top:calc(10px + env(safe-area-inset-top)); left:50%; transform:translateX(-50%); z-index:310; flex-direction:column; align-items:center; gap:8px; width:min(560px, calc(100vw - 20px)); max-height:calc(100vh - 20px - env(safe-area-inset-top)); overflow-y:auto; overscroll-behavior:contain; background:rgba(14,17,23,0.97); border:1px solid var(--c-brand); border-radius:18px; padding:10px 14px; box-shadow:0 2px 14px rgba(0,0,0,0.7); }
        /* min-width:0 so the roster can actually shrink inside the flex column
           instead of forcing the card wider than its own width. */
        #jc-prep-roster { width:100%; min-width:0; }
        /* Wraps now — the countdown line is long enough to need two lines at
           small widths, and nowrap was part of what pushed the card oversize. */
        #jc-prep-text { font-size:0.72em; font-weight:800; letter-spacing:1px; color:var(--c-brand); text-transform:uppercase; text-align:center; }
        #jc-rules-body .ob-line { font-size:0.95em; color:#cdd6e6; margin:10px 0; line-height:1.45; text-align:left; }
        #jc-rules-body .ob-line b { color:#ffd54f; }
        /* Shared helper-text style — the one class every game's small gray sub-label should use. */
        .jc-hint { font-size:0.78em; color:#7a8fa8; line-height:1.4; }
        .pavt { display:inline-flex; align-items:center; justify-content:center; width:22px; height:22px; border-radius:50%; font-size:0.78em; font-weight:900; color:#fff; vertical-align:middle; flex-shrink:0; }
        /* Spawna circle-face badge (generated in public/js/spawnling.js) */
        .jc-sp { display:inline-block; vertical-align:middle; flex-shrink:0; }
        .pavt-sp { overflow:hidden; border-radius:50%; }
        /* Compact Spawna avatar popup (public/js/spawn-customize.js) */
        #sc-modal { display:none; position:fixed; inset:0; z-index:700; background:rgba(6,9,16,.72); align-items:center; justify-content:center; padding:18px; }
        #sc-modal .sc-card { background:linear-gradient(180deg,#141d31,#0e1526); border:1px solid rgba(255,255,255,.12); border-radius:20px; padding:20px; width:min(420px,94vw); display:flex; flex-direction:column; align-items:center; gap:12px; box-shadow:0 24px 60px rgba(0,0,0,.6); }
        #sc-modal .sc-head { font-family:var(--font-display,inherit); font-weight:900; font-size:1.2em; }
        #sc-modal .sc-sub { color:#8695b2; font-size:.78em; margin-top:-8px; }
        #sc-modal .sc-draw-wrap { position:relative; width:200px; height:200px; flex:0 0 auto; }
        #sc-modal .sc-preview { position:absolute; inset:0; display:grid; place-items:center; border-radius:50%; }
        #sc-modal .sc-preview svg { width:100%; height:100%; }
        #sc-modal #sc-draw { position:absolute; inset:0; width:100%; height:100%; touch-action:none; cursor:crosshair; }
        #sc-modal .sc-colors { display:grid; grid-template-columns:repeat(9,1fr); gap:7px; width:100%; }
        #sc-modal .sc-swatch { width:25px; height:25px; min-width:0; min-height:0; padding:0; border-radius:50%; border:2px solid rgba(255,255,255,.16); background:var(--swatch); box-shadow:none; }
        #sc-modal .sc-swatch.selected { outline:3px solid #fff8ea; outline-offset:2px; transform:scale(.88); }
        #sc-modal .sc-swatch:disabled { opacity:.16; filter:grayscale(.8); cursor:not-allowed; }
        #sc-modal .sc-face-actions { display:grid; grid-template-columns:1.5fr 1fr 1fr; gap:7px; width:100%; }
        #sc-modal .sc-btn { font-weight:600; font-size:.85em; cursor:pointer; border:1px solid rgba(255,255,255,.14); background:#16223a; color:#eaf0fb; border-radius:10px; padding:8px 12px; }
        #sc-modal .sc-btn:hover { border-color:var(--c-mint,#38e8b0); }
        #sc-modal .sc-tag-label { width:100%; color:#eaf0fb; font-size:.75em; font-weight:800; letter-spacing:.08em; text-transform:uppercase; }
        #sc-modal .sc-tag-label span { color:#8695b2; font-weight:600; letter-spacing:0; text-transform:none; float:right; }
        #sc-modal .sc-tagline { box-sizing:border-box; width:100%; border:1px solid rgba(255,255,255,.14); border-radius:11px; background:#0d1422; color:#eaf0fb; padding:11px 12px; font:700 .92em inherit; }
        #sc-modal .sc-done { width:100%; }
        @media (prefers-reduced-motion: no-preference){ #sc-modal .sc-preview svg { transition:filter .2s; } }
        /* ── Party Kit (generic renderer for games/<id>/game.json — kit-engine.js) ── */
        #kit-screen { display:none; position:absolute; inset:0; z-index:55; background:radial-gradient(circle at 50% 10%,#0a1a2e,#050508 70%); flex-direction:column; align-items:center; overflow-y:auto; color:var(--c-ink); padding:calc(env(safe-area-inset-top) + 16px) 16px calc(env(safe-area-inset-bottom) + 20px); gap:13px; }
        .kit-header { font-family:var(--font-display); font-size:0.72em; color:var(--c-brand); letter-spacing:1px; text-align:center; }
        .kit-item-card { background:rgba(255,255,255,0.06); border:1.5px solid var(--c-border); border-radius:14px; padding:12px; width:min(440px,94%); text-align:center; }
        .kit-item-card img { max-width:100%; max-height:38vh; border-radius:10px; display:block; margin:0 auto; }
        .kit-item-caption { font-size:1.1em; font-weight:bold; line-height:1.4; padding:6px 4px; }
        .kit-item-by { font-family:monospace; font-size:0.68em; color:var(--c-ink2); margin-top:6px; letter-spacing:1px; }
        .kit-item-subject { font-family:monospace; font-size:0.72em; color:var(--c-brand); letter-spacing:1px; margin-bottom:8px; font-weight:bold; }
        /* Ransom-note word magnets — cream tiles on the dark game UI. */
        .kit-magnet-hint { font-family:monospace; font-size:0.78em; color:var(--c-ink2); letter-spacing:1px; text-align:center; }
        /* NO max-height/overflow here. This used to be `max-height:44vh;
           overflow-y:auto` — a fixed-height scroller nested inside #kit-screen,
           which already scrolls. 15 word chips on a narrow phone are taller than
           44vh, so several dealt words sat outside the tray with no scrollbar and
           no affordance hinting they existed: "the words aren't able to be seen or
           selected at times". On the MATCH step that could hide the TRUE word.
           Let the one real scroll container (#kit-screen) own the overflow. */
        .kit-magnet-tray { display:flex; flex-wrap:wrap; gap:8px; justify-content:center; align-items:center; width:min(500px,98%); padding:6px 2px; }
        /* flex-grow/text-transform/text-align are RESETS, not style: a bare
           `button {}` rule in games/tacostealth.css is loaded for every game and
           leaks flex-grow:1 into these chips, which stretched them to fill each
           row and made wrap points and row heights unpredictable (a long word
           became a full-width bar). It also forced uppercase over a deliberately
           lowercase word pool. */
        .kit-magnet { flex:0 0 auto; text-transform:none; text-align:center; background:#f4ecd8; border:1px solid #d8cba8; border-radius:5px; padding:8px 12px; color:#2a2620; font-family:'Courier New',Courier,monospace; font-weight:bold; font-size:0.95em; cursor:pointer; box-shadow:1px 2px 3px rgba(0,0,0,0.28); transition:transform .1s; }
        .kit-magnet:active { transform:scale(0.93) !important; }
        /* Brand colours are per-game and some are dark, so #111 ink is a coin flip
           (Snap Chain's deep pink needed a bespoke patch further up). Pick the ink
           from the brand's own contrast instead of hardcoding it. */
        .kit-magnet.chosen { background:var(--c-brand); color:var(--c-on-brand); border-color:var(--c-brand); box-shadow:0 0 14px rgba(255,235,59,0.45); }
        /* Tapped a word on the MATCH step: the tap IS the submit, so without any
           mark the tray just vanished and it read as "nothing happened". */
        .kit-magnet.sending { opacity:.55; transform:scale(0.95); }
        .kit-answer-input { width:min(440px,94%); min-height:74px; background:var(--c-card); color:var(--c-ink); border:1.5px solid var(--c-border); border-radius:12px; padding:12px; font-size:1em; font-family:inherit; resize:none; }
        .kit-vote-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:10px; width:min(480px,96%); }
        .kit-vote-card { background:rgba(255,255,255,0.06); border:1.5px solid var(--c-border); border-radius:12px; padding:10px; cursor:pointer; text-align:center; display:flex; flex-direction:column; gap:6px; transition:transform .12s, border-color .12s; }
        .kit-vote-card:active { transform:scale(0.97); }
        .kit-vote-card.chosen { border-color:var(--c-brand); box-shadow:0 0 12px rgba(255,235,59,0.25); }
        .kit-vote-card.mine { opacity:0.55; cursor:default; }
        .kit-vote-card img { width:100%; max-height:120px; object-fit:cover; border-radius:8px; }
        .kit-vote-name { font-family:monospace; font-size:0.72em; color:var(--c-ink2); letter-spacing:1px; }
        .kit-tally-row { display:flex; justify-content:space-between; gap:12px; font-family:monospace; font-size:0.95em; width:min(360px,92%); padding:6px 10px; background:rgba(255,255,255,0.05); border-radius:9px; }
        .kit-progress { font-family:monospace; color:var(--c-ink2); font-size:0.82em; letter-spacing:1px; }
        .qp-answer-input { width:min(440px,94%); min-height:76px; resize:none; border-radius:12px; border:1.5px solid var(--c-border); background:var(--c-card); color:#fff; font-size:1.05em; padding:12px 14px; font-family:inherit; box-sizing:border-box; }
        .qp-mode-row { display:flex; gap:8px; width:min(440px,94%); }
        .qp-mode-row .btn { flex:1; }
        /* ── Doodle pad toolbar (colour + line weight) ──────────────────────────
           The pad was a single hardcoded black 5px pen. flex:0 0 auto on the
           swatches is a reset, not styling: the global bare `button {}` rule in
           games/tacostealth.css loads for every game and would stretch them. */
        .qp-draw-tools { display:flex; align-items:center; gap:10px; width:min(360px,100%); flex-wrap:wrap; justify-content:center; }
        .qp-swatches { display:flex; gap:6px; flex-wrap:wrap; justify-content:center; }
        .qp-swatch { flex:0 0 auto; width:26px; height:26px; padding:0; border-radius:50%; border:2px solid rgba(255,255,255,0.45); cursor:pointer; box-shadow:0 1px 3px rgba(0,0,0,0.5); }
        .qp-swatch.on { border-color:var(--c-brand); transform:scale(1.18); box-shadow:0 0 8px rgba(255,235,59,0.55); }
        .qp-width-row { display:flex; align-items:center; gap:8px; min-width:140px; flex:1; }
        .qp-width-row input[type=range] { flex:1; min-width:90px; }
        /* Shows the real brush: a "12px" label tells you nothing about how thick
           the line will actually look. Sized/coloured live from JS. */
        .qp-width-dot { flex:0 0 auto; display:inline-block; width:6px; height:6px; border-radius:50%; background:#1b1b1f; outline:1px solid rgba(255,255,255,0.5); }
        /* ── Finale wall — every answer at once (qp-battle-screen only fits two) ── */
        .qp-finale-wall { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:10px; width:min(560px,96%); }
        .qp-finale-card { background:var(--c-card); border:1.5px solid var(--c-border); border-radius:14px; padding:10px; text-align:center; display:flex; flex-direction:column; gap:6px; }
        .qp-finale-card.mine { opacity:.62; }
        .qp-finale-card img.qp-answer-img { width:100%; max-height:150px; object-fit:contain; }
        .qp-finale-card .qp-vote-btn { width:100%; }
        .qp-finale-card.qp-winner { border-color:var(--c-brand); box-shadow:0 0 0 3px rgba(255,235,59,.4),0 0 22px rgba(255,235,59,.5); }
        .qp-preview-img { width:min(260px,70%); border-radius:10px; display:none; border:3px solid #fff; }
        .qp-voice-badge { display:none; font-family:monospace; color:var(--c-brand); font-size:0.85em; letter-spacing:1px; }
        /* Photo-privacy reassurance shown wherever we ask for someone's camera roll
           (PikPic upload screen, kit photoInput). Quiet by design — it should read as a
           calm fact, not a warning banner, so it stays legible without competing with
           the action buttons above it. Inherits currentColor-ish greys so it works on
           both the dark game screens and PikPic's light "Gallery Wall" theme. */
        .jc-photo-privacy {
            /* Absolute floor, not an em: these screens nest inside small-font parents, so
               0.72em landed at ~9px — unreadable, which is worse than saying nothing at
               all for a message whose whole job is to be reassuring.
               Colour comes from --c-ink2 (the per-theme muted-text token, same idea as
               .btn-quiet's --c-ink3) rather than inherit + opacity: the light game themes
               (PikPic Gallery Wall, Snap Chain Photo Booth) put this text on cream, where
               inherited white dropped to ~1.3:1 — invisible. --c-ink2 is defined by every
               theme, so one declaration stays legible on all of them, and dropping the
               opacity avoids muting an already-muted token twice. */
            font-family:monospace; font-size:clamp(11.5px,0.78em,13px); line-height:1.5;
            text-align:center; color:var(--c-ink2); max-width:min(420px,92%);
            margin:4px auto 0; padding:0 4px; text-wrap:balance;
        }
        /* ── Doodle + Voice answer modals — full-screen like the PikPic lightbox (z:200),
           not the small settings-panel .modal class (same z as the qp screens, no backdrop) ── */
        /* overflow-y:auto + margin:auto on the card centers when it fits and scrolls when it
           doesn't — so the CLOSE / USE buttons are always reachable on short/landscape screens
           (a plain justify-content:center would clip the bottom off-screen with no way to scroll). */
        #qp-doodle-modal, #qp-voice-modal { display:none; position:fixed; inset:0; z-index:200; background:rgba(0,0,0,0.88); flex-direction:column; align-items:center; overflow-y:auto; gap:12px; padding:calc(env(safe-area-inset-top) + 12px) 16px calc(env(safe-area-inset-bottom) + 16px); }
        .qp-modal-card { margin:auto; background:#111726; border:1.5px solid var(--c-border); border-radius:16px; padding:16px; display:flex; flex-direction:column; gap:12px; align-items:center; width:min(380px,94%); }
        .qp-modal-topbar { display:flex; width:100%; align-items:center; justify-content:space-between; gap:8px; }
        #qp-doodle-canvas { background:#fff; border-radius:10px; touch-action:none; width:100%; max-width:360px; height:auto; cursor:crosshair; }
        .qp-voice-record-btn { width:80px; height:80px; border-radius:50%; border:4px solid #e91e63; background:#1a0010; color:#e91e63; font-size:2em; cursor:pointer; display:flex; align-items:center; justify-content:center; }
        .qp-voice-record-btn.recording { background:#e91e63; color:#fff; animation:rr-pulse 1s infinite; }
        .qp-voice-record-btn.done { border-color:#2e7d32; color:#2e7d32; background:#001a05; }
        .qp-battle-card { width:min(440px,94%); background:rgba(255,255,255,0.05); border:2px solid var(--c-border); border-radius:14px; padding:14px; display:flex; flex-direction:column; gap:9px; align-items:center; transition:border-color .15s, box-shadow .15s, transform .15s; }
        .qp-battle-card.qp-winner { border-color:var(--c-brand); box-shadow:0 0 0 3px rgba(255,235,59,.4),0 0 22px rgba(255,235,59,.5); transform:scale(1.02); }
        .qp-battle-card.qp-dim { opacity:.5; }
        .qp-battle-name { font-weight:bold; letter-spacing:1px; color:var(--c-ink2); }
        .qp-answer-text { font-size:1.15em; line-height:1.35; text-align:center; }
        .qp-answer-img { width:100%; max-width:280px; border-radius:8px; display:block; }
        .qp-answer-caption { font-size:0.95em; font-style:italic; text-align:center; margin-top:6px; line-height:1.3; }
        .qp-vs { font-weight:900; color:var(--c-ink3); letter-spacing:3px; font-size:0.85em; }
        .qp-vote-btn { border:none; border-radius:10px; padding:11px 22px; font-weight:bold; font-size:0.92em; cursor:pointer; letter-spacing:1px; text-transform:uppercase; background:var(--c-brand); color:#090b12; width:100%; max-width:200px; }
        .qp-vote-btn:disabled { opacity:.35; cursor:default; }
        .qp-scores { width:min(360px,92%); }
        .qp-score-row { display:flex; justify-content:space-between; padding:5px 9px; border-radius:6px; font-size:0.95em; }
        .qp-score-row:nth-child(odd){ background:rgba(255,255,255,0.03); }
        /* Result / end */
        .rr-winner-banner { font-size:1.6em; font-weight:900; text-align:center; color:var(--c-brand); }
        .rr-score-row { display:flex; justify-content:space-between; padding:6px 4px; font-family:monospace; font-size:0.9em; border-bottom:1px solid #1a0a2e; width:min(300px,88%); }

        /* ── Shared juice: directional sparks, impact rings, confetti, cork overlay ── */
        .sc-spark-p       { position:fixed; z-index:71; image-rendering:pixelated; pointer-events:none; animation:sc-spark .55s cubic-bezier(.2,.6,.6,1) forwards; }
        @keyframes sc-spark { 0%{transform:translate(0,0);opacity:1} 70%{transform:translate(var(--sx),var(--sy));opacity:1} 100%{transform:translate(var(--sx),calc(var(--sy) + var(--sg,40px)));opacity:0} }
        .sc-ring-p        { position:fixed; z-index:71; width:14px; height:14px; margin:-7px 0 0 -7px; border:3px solid #fff; border-radius:50%; pointer-events:none; animation:sc-ring .45s ease-out forwards; }
        @keyframes sc-ring { from{transform:scale(.4);opacity:.95} to{transform:scale(5);opacity:0} }
        .sc-confetti-p    { position:fixed; z-index:71; width:7px; height:11px; pointer-events:none; animation:sc-confetti 1.1s ease-in forwards; }
        @keyframes sc-confetti { 0%{transform:translate(0,0) rotate(0deg);opacity:1} 30%{transform:translate(calc(var(--cx)*.5),var(--cy)) rotate(calc(var(--cr)*.4));opacity:1} 100%{transform:translate(var(--cx),150px) rotate(var(--cr));opacity:0} }
        .sc-pop-canvas    { position:fixed; inset:0; z-index:72; pointer-events:none; }

        /* ── Game feel: live timer ring, waiting roster, screen-enter transition ── */
        /* @property lets the conic sweep interpolate smoothly (graceful step where unsupported). */
        @property --jc-timer-frac { syntax:'<number>'; inherits:false; initial-value:1; }
        .jc-timer { position:relative; display:inline-flex; align-items:center; justify-content:center;
            width:62px; height:62px; border-radius:50%; --jc-timer-frac:1; --jc-timer-col:var(--c-brand);
            background:conic-gradient(var(--jc-timer-col) calc(var(--jc-timer-frac) * 360deg), rgba(255,255,255,.13) 0deg);
            transition:--jc-timer-frac .95s linear; }
        .jc-timer::before { content:''; position:absolute; inset:5px; border-radius:50%; background:var(--c-card,#1a1030); }
        .jc-timer-num { position:relative; z-index:1; font-family:var(--font-display); font-weight:900; font-size:1.4em; line-height:1; color:var(--jc-timer-col); }
        .jc-timer--warning { --jc-timer-col:#f0b84b; }
        .jc-timer--urgent { --jc-timer-col:#ff4747; animation:jc-timer-pulse .7s ease-in-out infinite; }
        .jc-timer--done   { --jc-timer-col:#ff4747; }
        @keyframes jc-timer-pulse { 0%,100%{transform:scale(1);} 50%{transform:scale(1.09);} }

        .jc-roster { display:flex; flex-direction:column; align-items:center; gap:10px; }
        /* max-width was min(560px,94%) — but the `94%` resolved against whatever
           the parent happened to be, and inside a content-sized fixed box that is
           circular, so nothing wrapped to the real viewport. 100% of the (now
           explicitly sized) container is what this always meant. */
        .jc-roster-chips { display:flex; flex-wrap:wrap; justify-content:center; gap:8px; width:100%; max-width:100%; min-width:0; }
        .jc-chip { display:inline-flex; align-items:center; gap:6px; padding:5px 11px 5px 5px; border-radius:999px;
            background:rgba(255,255,255,.07); border:1.5px solid rgba(255,255,255,.14); font-weight:700; font-size:.9em;
            opacity:.5; transition:opacity .3s ease, background .3s ease, border-color .3s ease, transform .3s ease; }
        .jc-chip--done { opacity:1; background:color-mix(in srgb, var(--c-mint,#38e8b0) 16%, transparent);
            border-color:color-mix(in srgb, var(--c-mint,#38e8b0) 55%, transparent); }
        .jc-chip-face { width:24px; height:24px; border-radius:50%; overflow:hidden; flex:0 0 auto; background-size:cover;
            background-position:center; display:inline-flex; align-items:center; justify-content:center; }
        .jc-chip-face .pavt { width:24px; height:24px; }
        .jc-chip-name { white-space:nowrap; }
        .jc-chip-mark { font-size:.95em; opacity:.75; }
        .jc-chip--done .jc-chip-mark { color:var(--c-mint-ink,#38e8b0); opacity:1; }
        .jc-roster-caption { font-size:.95em; font-weight:800; opacity:.85; text-align:center; }
        .jc-roster--burst .jc-chip { animation:jc-chip-check .5s cubic-bezier(.2,.9,.25,1.25); }
        @keyframes jc-chip-check { 0%{transform:scale(1);} 40%{transform:scale(1.16);} 100%{transform:scale(1);} }

        @keyframes jc-screen-in { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:none; } }
        .jc-enter { animation:jc-screen-in .28s ease-out; }
        /* The lobby fades WITHOUT translating. jc-screen-in animates transform, and a
           transformed element becomes the containing block for its position:fixed
           descendants — which would un-pin #rl-actionbar from the viewport and slide
           it 8px for the duration of every lobby entry. Anything pinned inside a
           screen needs that screen to stay untransformed. */
        @keyframes jc-screen-in-fade { from { opacity:0; } to { opacity:1; } }
        #room-lobby.jc-enter { animation:jc-screen-in-fade .28s ease-out; }

        @media (prefers-reduced-motion: reduce) {
            .jc-timer { transition:none; }
            .jc-timer--urgent { animation:none; }
            .jc-roster--burst .jc-chip { animation:none; }
            .jc-enter { animation:none; }
            button, .btn { transition:none; }
        }
