/* ══════════════════════════════════════════════════════════
   Happy Light Arcade — warm-ink pixel arcade theme
   ══════════════════════════════════════════════════════════ */
:root{
  --bg:#0b0a0d;
  --bg-2:#16131a;
  --panel:rgba(29,25,34,.82);
  --panel-solid:#1d1922;
  --line:rgba(184,175,164,.20);
  --line-hot:rgba(245,158,11,.45);
  --ink:#f5f1ea;
  --ink-dim:#b8afa4;
  --ink-faint:#6b635a;
  --brand:#f59e0b;
  --brand-2:#a78bfa;
  --p1:#48c8dd;
  --p2:#e05b7a;
  --p3:#9ec95a;
  --p4:#e39a48;
  --ok:#4ee8a5;
  --warn:#fbbf24;
  /* hard edges: a pixel grid has no rounded corners and no soft falloff */
  --r-sm:0px; --r:0px; --r-lg:0px;
  --shadow:4px 4px 0 rgba(0,0,0,.45);
  /* Logo palette. The wordmark strokes are currentColor; only the bulb
     reads these, so it keeps its warm identity on either ground. */
  --logo-glow:1; --logo-glow-color:#f59e0b; --logo-bulb:#d9a441;
  --logo-glass-hi:#fffdf6; --logo-glass-lo:#f8e3b6; --logo-face:#8a6a3b;
  --font:"Geist","Geist Variable",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
  --display:"Silkscreen","VT323",ui-monospace,monospace;
  --mono:"Silkscreen","VT323",ui-monospace,monospace;
}

*{box-sizing:border-box}
/* several blocks below set an explicit display, which would otherwise
   beat the hidden attribute — keep [hidden] authoritative */
[hidden]{display:none !important}
html,body{height:100%}
/* iPhone: Safari enlarges text by itself when the phone is turned on its
   side, which pushed a landscape pad's buttons out of their boxes; and a
   quick second tap zooms the page wherever nothing says otherwise (the
   viewport tag's user-scalable=no is ignored there) */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%;touch-action:manipulation}
/* The live pad (controller.js, lockPage): the document itself must not
   scroll or rubber-band under a thumb. Hidden overflow is not enough on
   iOS, a pinned body is. */
html.pad-lock,html.pad-lock body{overflow:hidden;overscroll-behavior:none}
html.pad-lock body{position:fixed;top:0;left:0;right:0;bottom:0;width:100%}
body{
  margin:0;background:var(--bg);color:var(--ink);font-family:var(--font);
  -webkit-font-smoothing:antialiased;overflow-x:hidden;
  -webkit-tap-highlight-color:transparent;
}
button{font-family:inherit;color:inherit}

/* ── ambient background ── */
.bg-grid{
  position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.5;
  background-image:linear-gradient(rgba(222,215,204,.05) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(222,215,204,.05) 1px,transparent 1px);
  background-size:46px 46px;
  mask-image:radial-gradient(ellipse 90% 70% at 50% 30%,#000 30%,transparent 78%);
  -webkit-mask-image:radial-gradient(ellipse 90% 70% at 50% 30%,#000 30%,transparent 78%);
}
.bg-glow{
  position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(ellipse 70% 50% at 18% 0%,rgba(245,158,11,.10),transparent 70%),
    radial-gradient(ellipse 60% 45% at 85% 8%,rgba(167,139,250,.09),transparent 72%),
    radial-gradient(ellipse 80% 40% at 50% 100%,rgba(78,232,165,.045),transparent 75%);
}

/* ── view routing ── */
.view{display:none;position:relative;z-index:1}
.view.active{display:block}
#view-controller.active,#view-screen.active,#view-watch.active{display:flex;flex-direction:column;min-height:100vh;min-height:100dvh}
/* The room, the pad and the mirror are each one screen and never scroll as
   a page: what has more to show scrolls inside its own panel. A stray pixel
   of overflow must not put a scrollbar down the side of the stage. */
html:has(#view-controller.active,#view-screen.active,#view-watch.active),
html:has(#view-controller.active,#view-screen.active,#view-watch.active) body{overflow:hidden;overscroll-behavior:none}

/* ── wordmark ── */
/* Mark over title: the property name sits under the lockup, matching portal,
   pixel-editor and search so the suite reads as one brand. */
.wordmark{font-weight:700;letter-spacing:-.03em;font-size:clamp(2.4rem,7vw,4.2rem);line-height:1;
  display:inline-flex;flex-direction:column;align-items:center;gap:.34em}
/* The lockup is one SVG: bulb + HAPPY LiGHT, the bulb being the tittle of
   the "i". Its viewBox reserves headroom above the caps for the bulb, so the
   element is ~2.8x the cap height it draws — hence 2em, not 1em. */
.wordmark .hl-lockup{height:2em;width:auto;display:inline-block;
  vertical-align:-.34em;color:var(--ink);flex:none}
/* The label, not a second wordmark: letterspaced caps in a muted tone, the
   same treatment as the full lockup's DEVELOPMENT line. max() keeps it legible
   in the .small contexts, where .16em alone would round to a few pixels. */
.wordmark .wm-b{background:none;-webkit-background-clip:border-box;background-clip:border-box;
  color:var(--ink-dim);font-size:max(.62rem,.16em);font-weight:700;letter-spacing:.26em;
  text-indent:.26em;text-transform:uppercase;line-height:1}
.wordmark .wm-dot{color:var(--p2)}
.wordmark.small{font-size:1.25rem}

/* ══════════════ LANDING ══════════════ */
#view-landing{max-width:1120px;margin:0 auto;padding:clamp(2rem,6vw,5rem) clamp(1.1rem,4vw,2rem) 4rem}
.landing-head{text-align:center;margin-bottom:clamp(2.2rem,6vw,3.6rem)}
.tagline{margin:1.1rem auto 0;max-width:44ch;color:var(--ink-dim);font-size:clamp(1rem,2.2vw,1.2rem);line-height:1.6}
.tagline-lead{display:block;color:var(--ink);font-weight:600}

.choice-grid{display:grid;gap:1.15rem;grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.choice{
  position:relative;text-align:left;border:1px solid var(--line);border-radius:0;
  background:linear-gradient(160deg,rgba(43,37,51,.90),rgba(16,19,26,.82));
  padding:1.6rem 1.6rem 1.4rem;cursor:pointer;overflow:hidden;
  transition:transform .28s cubic-bezier(.2,.8,.2,1),border-color .28s,box-shadow .28s;
}
.choice:hover{transform:translateY(-6px);border-color:var(--line-hot);box-shadow:0 26px 50px -24px rgba(245,158,11,.7)}
.choice h2{margin:1.1rem 0 .45rem;font-size:1.5rem;letter-spacing:-.02em}
.choice p{margin:0;color:var(--ink-dim);line-height:1.55;font-size:.97rem}
.choice-cta{display:inline-block;margin-top:1.1rem;font-weight:700;font-size:.9rem;letter-spacing:.02em;color:var(--brand-2)}
.choice-pad .choice-cta{color:var(--p2)}

/* Device-recommended entry point: a solid accent frame + a badge (no glow —
   pixel theme leans on weight and definition). order:-1 floats it first, which
   only matters on the phone single-column layout; desktop keeps "Open the
   screen" first anyway. Non-recommended cards stay fully visible/clickable. */
.choice.recommended{order:-1;border-color:var(--brand);border-width:2px;
  background:linear-gradient(160deg,rgba(245,158,11,.14),rgba(16,19,26,.90))}
[data-theme="light"] .choice.recommended{
  background:linear-gradient(160deg,rgba(180,83,9,.16),rgba(246,241,232,.96))}
.reco-badge{position:absolute;top:0;right:0;
  background:linear-gradient(96deg,var(--brand),var(--brand-2));color:#0b0a0d;
  font-weight:700;font-size:.62rem;letter-spacing:.06em;text-transform:uppercase;
  padding:.28rem .55rem}
.choice-art{height:104px;display:grid;place-items:center}
.art-tv{position:relative;width:150px;height:92px;border:2px solid var(--brand-2);border-radius:0;background:rgba(167,139,250,.07);box-shadow:none}
.art-ball{position:absolute;width:12px;height:12px;border-radius:50%;background:#fff;box-shadow:none;top:40px;left:69px;animation:bounceX 2.2s ease-in-out infinite alternate}
.art-pad{position:absolute;width:6px;height:30px;border-radius:0;top:31px}
.art-pad.l{left:8px;background:var(--p1);box-shadow:none}
.art-pad.r{right:8px;background:var(--p2);box-shadow:none}
@keyframes bounceX{from{transform:translateX(-52px) translateY(-16px)}to{transform:translateX(52px) translateY(18px)}}
.art-phone{position:relative;width:62px;height:104px;border:2px solid var(--p2);border-radius:0;background:rgba(224,91,122,.07);box-shadow:none}
.art-stick{position:absolute;left:12px;bottom:16px;width:20px;height:20px;border-radius:50%;background:var(--p2);opacity:.85;animation:nudge 1.6s ease-in-out infinite alternate}
.art-btn{position:absolute;right:12px;bottom:18px;width:15px;height:15px;border-radius:50%;background:var(--brand-2)}
@keyframes nudge{from{transform:translateX(-4px)}to{transform:translateX(6px)}}

/* "Controller on this computer" — the keyboard join card. Accented green (p3)
   to read distinct from the phone (p2) and screen (brand-2) cards. */
.choice-pc .choice-cta{color:var(--p3)}
.art-kbd{position:relative;display:grid;grid-template-columns:repeat(4,1fr);gap:5px;
  width:132px;padding:10px;border:2px solid var(--p3);border-radius:0;background:rgba(158,201,90,.08)}
.art-kbd i{display:block;height:13px;background:var(--p3);opacity:.55}
.art-kbd i:nth-child(2){animation:keytap 1.7s ease-in-out infinite}
.art-kbd i:nth-child(7){animation:keytap 1.7s ease-in-out .6s infinite}
.art-kbd .art-space{grid-column:1 / -1;height:11px;background:var(--brand-2);opacity:.7;animation:keytap 1.7s ease-in-out .3s infinite}
@keyframes keytap{0%,100%{opacity:.4}45%{opacity:1}}

/* Keyboard-first join: a note on the join form + the live key hint promoted
   from a faint footnote to the headline control. Set on <body> in pcMode. */
.pad-kb-note{margin:0 0 .7rem;text-align:center;font-size:.9rem;color:var(--ink-dim);
  border:1px solid var(--p3);border-radius:0;padding:.5rem .7rem;background:rgba(158,201,90,.1)}
body.pc-ctrl #pad-keys{
  opacity:1;color:var(--brand);border:1px solid var(--brand);border-style:solid;
  font-size:.86rem;padding:.55rem .8rem;background:rgba(245,158,11,.14);
}
body.pc-ctrl #pad-hint{opacity:.75}

.game-preview{margin-top:3.4rem}
.game-preview h3{font-size:.82rem;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-faint);margin:0 0 1rem}
.preview-cards{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
.pv-card{border:1px solid var(--line);border-radius:0;padding:1.1rem 1.2rem;background:var(--panel);transition:border-color .25s,transform .25s}
.pv-card:hover{border-color:var(--line-hot);transform:translateY(-3px)}
.pv-ico{font-size:1.6rem;line-height:1}

/* ══════════════ SHARED CONTROLS ══════════════ */
.prime-btn{
  border:0;border-radius:0;padding:.85rem 1.7rem;font-weight:700;font-size:1rem;cursor:pointer;
  background:linear-gradient(96deg,var(--brand),var(--brand-2));color:#0b0a0d;
  box-shadow:0 14px 30px -12px rgba(245,158,11,.9);transition:transform .18s,filter .18s;
}
.prime-btn:hover{transform:translateY(-2px);filter:brightness(1.08)}
.prime-btn:active{transform:translateY(0) scale(.985)}
.prime-btn.full{width:100%;padding:1.05rem}
.ghost-btn{
  border:1px solid var(--line);background:rgba(255,255,255,.03);color:var(--ink-dim);
  border-radius:0;padding:.5rem .9rem;cursor:pointer;font-size:.9rem;transition:.2s;
}
.ghost-btn:hover{border-color:var(--line-hot);color:var(--ink)}
.ghost-btn.wide{padding:.65rem 1.2rem}
.link-btn{background:none;border:0;color:var(--ink-faint);cursor:pointer;font-size:.9rem;padding:.6rem}
.link-btn:hover{color:var(--ink)}

/* ══════════════ SCREEN ══════════════ */
#view-screen{height:100dvh;overflow:hidden}
.screen-shell{display:flex;flex-direction:column;height:100dvh}
.screen-bar{
  display:flex;align-items:center;gap:1rem;padding:.7rem clamp(.8rem,2.4vw,1.5rem);
  border-bottom:1px solid var(--line);background:rgba(8,11,28,.7);backdrop-filter:blur(12px);flex:0 0 auto;
}
.sb-left,.sb-right{display:flex;align-items:center;gap:.6rem}
.sb-players{display:flex;gap:.5rem;flex:1;justify-content:center;flex-wrap:wrap}
.net-pill{display:inline-flex;align-items:center;gap:.45rem;font-family:var(--mono);font-size:.72rem;color:var(--ink-faint);border:1px solid var(--line);border-radius:0;padding:.25rem .6rem}
.net-pill i{width:7px;height:7px;border-radius:50%;background:var(--warn);box-shadow:none}
.net-pill.live i{background:var(--ok)}
.net-pill.dead i{background:var(--p2)}
.chip{display:inline-flex;align-items:center;gap:.45rem;border:1px solid var(--line);border-radius:0;padding:.28rem .75rem;font-size:.85rem;font-weight:500}
.chip i{width:9px;height:9px;border-radius:50%;background:currentColor;box-shadow:none}
.chip.gone{opacity:.4}

.screen-body{flex:1;min-height:0;overflow:auto}

/* While the stage is up (briefing, round, result) the seat row is gone and
   the canvas has the whole screen. What is left of the row is one small
   button floating over the top right corner, faint until it is wanted. */
.screen-shell.playing .roster{display:none}
.stage-menu{
  display:none;position:fixed;z-index:8;
  top:calc(env(safe-area-inset-top,0px) + .35rem);right:calc(env(safe-area-inset-right,0px) + .35rem);
  width:40px;height:40px;padding:0;place-items:center;cursor:pointer;
  font-size:1.05rem;line-height:1;color:var(--ink);
  background:var(--panel-solid);border:1px solid var(--line);border-radius:0;
  opacity:.4;transition:opacity .2s,border-color .2s;
  -webkit-tap-highlight-color:transparent;touch-action:manipulation;
}
.screen-shell.playing .stage-menu{display:grid}
.stage-menu:hover,.stage-menu:focus-visible,.stage-menu.warn{opacity:1;border-color:var(--line-hot)}
.stage-menu:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
/* someone has dropped, or the room itself has lost its line */
.stage-menu i{display:none;position:absolute;top:-4px;right:-4px;width:10px;height:10px;border-radius:50%;background:var(--p2);border:2px solid var(--panel-solid)}
.stage-menu.warn i{display:block}
@media (min-width:1900px) and (min-height:1000px){.stage-menu{width:48px;height:48px;font-size:1.3rem}}
/* the room code and QR in the menu, for joining while a round is on */
.menu-join{display:flex;align-items:center;gap:.8rem;min-width:0}
.menu-join[hidden]{display:none}

/* fullscreen.js. The screen was reached without a click, so full screen has
   to wait for one: a quiet line at the bottom edge until then, clear of the
   toast rail and never in the way of a click. */
.fs-hint{
  position:fixed;z-index:19;left:50%;bottom:calc(env(safe-area-inset-bottom,0px) + .7rem);transform:translateX(-50%);
  margin:0;padding:.3rem .8rem;pointer-events:none;white-space:nowrap;
  font-size:.8rem;color:var(--ink);background:var(--panel-solid);border:1px solid var(--line);opacity:.8;
}
/* fullscreen.js. An iPhone in a browser tab: how to get the pad full screen.
   Laid over the wordmark at the top of the join form, which has no room to
   spare lower down; it is part of the form, so it leaves with it. */
.home-hint{
  position:fixed;z-index:5;top:calc(env(safe-area-inset-top,0px) + .5rem);left:.6rem;right:.6rem;
  display:flex;align-items:center;gap:.6rem;padding:.5rem .6rem .5rem .8rem;
  font-size:.84rem;line-height:1.4;text-align:left;color:var(--ink);
  background:var(--panel-solid);border:1px solid var(--line);
  user-select:none;-webkit-user-select:none;
}
.home-hint p{margin:0;flex:1}
.home-hint-share{display:inline-block;vertical-align:-.15em}
.home-hint-x{flex:0 0 auto;min-width:36px;min-height:36px;padding:0;touch-action:manipulation}
.menu-join .qr-box{margin:0}
.menu-join .qr-box img{width:84px}
.menu-join-text{min-width:0}
.menu-join-text p{margin:0}
.menu-join-url{font-family:var(--mono);font-size:.82rem;color:var(--brand-2);word-break:break-all}
.menu-join .room-code{font-size:1.9rem;line-height:1.15}
@media (max-height:480px){.menu-join .qr-box img{width:56px}.menu-join .room-code{font-size:1.3rem}}

/* lobby */
#lobby{display:grid;grid-template-columns:minmax(300px,380px) 1fr;gap:clamp(1rem,2.5vw,2rem);padding:clamp(1rem,2.6vw,2rem)}
.lobby-join{display:flex;flex-direction:column;gap:1rem;min-width:0}
.join-card{border:1px solid var(--line);border-radius:0;background:var(--panel);padding:1.4rem;text-align:center;backdrop-filter:blur(10px)}
.join-kicker{margin:0;color:var(--ink-faint);font-size:.78rem;letter-spacing:.16em;text-transform:uppercase}
.join-url{margin:.35rem 0 1rem;font-family:var(--mono);font-size:clamp(.95rem,1.6vw,1.15rem);color:var(--brand-2);word-break:break-all}
.room-code{
  font-family:var(--mono);font-weight:700;font-size:clamp(2.6rem,6vw,3.8rem);letter-spacing:.16em;
  margin:.3rem 0 1.1rem;background:linear-gradient(96deg,var(--brand),var(--brand-2));
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.qr-box{display:grid;place-items:center;background:#fff;border-radius:0;padding:.7rem;width:fit-content;margin:0 auto}
.qr-box img,.qr-box canvas{display:block;width:clamp(120px,15vw,168px);height:auto;image-rendering:pixelated}
.join-hint{margin:.75rem 0 0;color:var(--ink-faint);font-size:.8rem}

.join-card.gate{text-align:center}
.gate-h{margin:.5rem 0 .6rem;font-size:1.35rem;letter-spacing:-.02em}
.gate-p{margin:0 0 1.2rem;color:var(--ink-dim);font-size:.93rem;line-height:1.55}
.join-card.gate .prime-btn{width:100%}

.seat-list{display:grid;gap:.55rem}
.seat{display:flex;align-items:center;gap:.7rem;border:1px dashed var(--line);border-radius:0;padding:.7rem .9rem;color:var(--ink-faint);font-size:.92rem}
.seat.filled{border-style:solid;color:var(--ink);background:rgba(255,255,255,.03)}
.seat .dot{width:11px;height:11px;border-radius:50%;background:currentColor;box-shadow:none;flex:0 0 auto}

.lobby-games{display:flex;flex-direction:column;gap:1rem;min-width:0}
.game-grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(215px,1fr))}
.game-card{
  position:relative;text-align:left;border:1px solid var(--line);border-radius:0;
  background:linear-gradient(160deg,rgba(43,37,51,.85),rgba(16,19,26,.85));
  padding:1.2rem;cursor:pointer;transition:transform .25s,border-color .25s,box-shadow .25s;overflow:hidden;
}
.game-card:hover,.game-card.sel{transform:translateY(-5px);border-color:var(--line-hot);box-shadow:0 22px 46px -24px rgba(245,158,11,.8)}
.game-card.sel::after{content:"";position:absolute;inset:0;border-radius:inherit;border:2px solid var(--brand);pointer-events:none;animation:pulseBorder 1.4s ease-in-out infinite}
@keyframes pulseBorder{50%{opacity:.35}}
.gc-ico{font-size:2rem;line-height:1}
.game-card h3{margin:.65rem 0 .35rem;font-size:1.18rem}
.gc-meta{display:flex;gap:.5rem;margin-top:.85rem;flex-wrap:wrap}
.gc-meta span{font-family:var(--mono);font-size:.68rem;border:1px solid var(--line);border-radius:0;padding:.16rem .55rem;color:var(--ink-faint)}
.game-card.locked{opacity:.45;cursor:not-allowed;transform:none;box-shadow:none}

.seg{display:inline-flex;border:1px solid var(--line);border-radius:0;padding:3px;background:rgba(255,255,255,.03)}
.seg button{border:0;background:none;border-radius:0;padding:.4rem .8rem;font-size:.82rem;color:var(--ink-faint);cursor:pointer;transition:.18s}
.seg button:hover{color:var(--ink)}
.seg button.on{background:linear-gradient(96deg,var(--brand),var(--brand-2));color:#0b0a0d;font-weight:700}
.switch{display:inline-flex;align-items:center;gap:.6rem;cursor:pointer;font-size:.88rem;color:var(--ink-dim)}
.switch input{display:none}
.switch .track{width:42px;height:24px;border-radius:0;background:rgba(255,255,255,.08);border:1px solid var(--line);position:relative;transition:.22s}
.switch .track i{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:var(--ink-faint);transition:.22s}
.switch input:checked + .track{background:rgba(245,158,11,.35);border-color:var(--brand)}
.switch input:checked + .track i{left:20px;background:var(--brand-2)}

/* stage */
.stage-wrap{position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden}
#stage{display:block;width:100%;height:100%}
.stage-overlay{position:absolute;inset:0;display:grid;place-items:center;background:rgba(6,8,22,.86);backdrop-filter:blur(8px);z-index:5}
.ov-inner{text-align:center;padding:2rem;animation:popIn .35s cubic-bezier(.2,.9,.3,1.4)}
@keyframes popIn{from{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}
.ov-kicker{margin:0;letter-spacing:.24em;text-transform:uppercase;font-size:.78rem;color:var(--ink-faint)}
.ov-title{margin:.5rem 0;font-size:clamp(2.2rem,7vw,4.4rem);letter-spacing:-.03em}
.ov-sub{margin:0 0 1.6rem;color:var(--ink-dim)}
.ov-actions{display:flex;gap:.8rem;justify-content:center;flex-wrap:wrap}
/* the button a phone's START will press */
.ov-actions .sel{outline:2px solid var(--brand);outline-offset:3px}
.toast-rail{position:fixed;bottom:1rem;left:1rem;display:grid;gap:.5rem;z-index:20;pointer-events:none;justify-items:start}
.toast{
  font-family:var(--mono);font-size:.85rem;padding:.45rem 1rem;border-radius:0;
  background:rgba(10,14,34,.9);border:1px solid var(--line-hot);animation:toastIn .3s ease, fadeOut .4s ease 2.1s forwards;
}
@keyframes toastIn{from{opacity:0;transform:translateY(10px)}}
@keyframes fadeOut{to{opacity:0;transform:translateY(8px)}}

/* ══════════════ MIRROR LINK (on the screen) ══════════════ */
.viewer-chip{
  display:inline-flex;align-items:center;gap:.35rem;font-size:.8rem;color:var(--ink-dim);
  border:1px solid var(--line);border-radius:0;padding:.25rem .65rem;
}
.viewer-chip b{color:var(--brand-2);font-family:var(--mono)}

/* ══════════════ MIRROR VIEW ══════════════ */
#view-watch{height:100dvh;overflow:hidden}
.watch-badge{color:var(--brand-2)}
.watch-badge b{font-family:var(--mono);letter-spacing:.1em}
.watch-game{font-size:.85rem;color:var(--ink-dim)}
.watch-lobby{display:grid;place-items:center;padding:clamp(1rem,3vw,2rem)}
.watch-panel{
  width:min(420px,100%);text-align:center;border:1px solid var(--line);
  border-radius:0;background:var(--panel);padding:1.6rem;backdrop-filter:blur(10px);
}
.watch-panel .join-kicker{margin-top:1.1rem}
.watch-panel .join-kicker:first-child{margin-top:0}
.watch-panel .room-code{margin:.2rem 0 0}
.watch-sel{margin:.3rem 0 0;font-size:1.15rem;font-weight:700}
.watch-opts{margin:.3rem 0 0;font-family:var(--mono);font-size:.78rem;color:var(--ink-faint)}
.watch-panel .seat-list{margin-top:.5rem;text-align:left}
.watch-empty{margin:.4rem 0 0;color:var(--ink-faint);font-size:.88rem}
.watch-panel .join-hint{margin-top:1.1rem}

/* ══════════════ CONTROLLER ══════════════ */
#view-controller{height:100vh;height:100dvh;overflow:hidden;touch-action:none;user-select:none;-webkit-user-select:none;
  /* iOS: a held thumb otherwise brings up the magnifier/callout, and every
     tap flashes grey */
  -webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}
.pad-stage{flex:1;display:flex;flex-direction:column;padding:calc(env(safe-area-inset-top) + 1rem) 1.1rem calc(env(safe-area-inset-bottom) + 1rem);gap:.85rem;min-height:0}
/* a phone on its side has the notch at one end and nothing at the other:
   the outboard controls keep clear of it */
@supports (padding:max(0px)){
  .pad-stage{padding-left:max(1.1rem,env(safe-area-inset-left));padding-right:max(1.1rem,env(safe-area-inset-right))}
}
/* under reduced motion every property gets a .01ms transition (see the
   rule further down). The quarter turn of .pad-rot swaps position and
   transform together, and a transition on the transform alone showed the
   pad off-screen for a frame. */
#view-controller #pad-live{transition:none !important}
/* Safari before 15.4 has no dvh, and its 100vh runs under the toolbar;
   controller.js measures the visible height into --vvh there */
@supports not (height:100dvh){
  #view-controller,#view-controller.active{height:var(--vvh,100vh);min-height:0}
  html #view-controller.pad-rot #pad-live{width:var(--vvh,100vh)}
}
#pad-join{align-items:center;justify-content:center;text-align:center}
.pad-h{margin:.4rem 0 .6rem;font-size:1.6rem}
.field{display:block;width:min(340px,100%);text-align:left;margin-bottom:.2rem}
.field span{display:block;font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-faint);margin-bottom:.35rem}
.code-input,.name-input{
  width:100%;background:rgba(255,255,255,.04);border:1px solid var(--line);border-radius:0;
  color:var(--ink);padding:.9rem 1rem;font-size:1.1rem;font-family:var(--font);outline:none;transition:border-color .2s;
}
.code-input{font-family:var(--mono);font-size:2rem;letter-spacing:.34em;text-align:center;text-transform:uppercase;font-weight:700}
.code-input:focus,.name-input:focus{border-color:var(--brand)}
#btn-connect{width:min(340px,100%);margin-top:.5rem}
.pad-err{min-height:1.2rem;color:var(--p2);font-size:.86rem;margin:.3rem 0 0}
/* under "Your name" when the name is the account's rather than typed */
.field-note{width:min(340px,100%);margin:0;text-align:left;color:var(--ink-faint);font-size:.8rem;line-height:1.45}
.field-note .link-btn{padding:0;font-size:.74rem;color:var(--brand);text-decoration:underline;text-underline-offset:.2em}
.name-input[readonly]{color:var(--ink-dim);border-style:dashed;cursor:default}
/* the nickname setting: the QR scanner's dialog, with a form in it */
.nick-modal .sing-modal-card{text-align:left;border-color:var(--line-hot)}
.nick-modal .sing-modal-title{font-size:1.05rem}
.nick-modal .field{width:100%}
.nick-modal .sing-modal-text{font-size:.84rem}
.nick-modal .sing-modal-err{min-height:1.2rem}
.nick-modal .sing-modal-err:empty{display:block}

.pad-top{display:flex;align-items:center;gap:.7rem;flex:0 0 auto}
.pad-you.host::before{content:"\2605  ";color:var(--brand)}
.pad-you.host::after{content:" \00b7  HOST";color:var(--brand)}
.pad-you{font-family:var(--mono);font-weight:700;font-size:.9rem;border:1px solid currentColor;border-radius:0;padding:.22rem .7rem}
.pad-title{flex:1;text-align:center;font-size:.95rem;color:var(--ink-dim);text-transform:uppercase;letter-spacing:.12em}
.pad-hint{margin:0;text-align:center;color:var(--ink-faint);font-size:.9rem;flex:0 0 auto;min-height:1.2rem}
.pad-keys{
  margin:0;text-align:center;flex:0 0 auto;font-family:var(--mono);font-size:.72rem;
  letter-spacing:.02em;color:var(--ink-dim);opacity:.8;
  border:1px dashed var(--line);border-radius:0;padding:.3rem .5rem;
}
.power-chip{
  flex:0 0 auto;display:flex;align-items:center;justify-content:center;gap:.6rem;
  border:1px solid var(--brand);border-radius:0;padding:.45rem 1rem;background:rgba(245,158,11,.16);
  animation:glowPulse 1.5s ease-in-out infinite;
}
.power-chip b{font-family:var(--mono);letter-spacing:.1em}
.power-chip span{font-size:.75rem;color:var(--ink-dim)}
@keyframes glowPulse{50%{box-shadow:none}}

.pad-layout{flex:1;display:flex;flex-direction:column;gap:1rem;min-height:0}
.slider{
  flex:1;position:relative;border:1px solid var(--line);border-radius:0;
  background:linear-gradient(160deg,rgba(167,139,250,.09),rgba(245,158,11,.09));overflow:hidden;touch-action:none;
}
.slider-knob{
  position:absolute;border-radius:0;background:var(--knob,var(--p1));
  box-shadow:none;transition:none;
}
/* The knob is capped against the rail as well as in px. A fixed 74px knob
   inside a 55px landscape rail overflowed and left no travel at all —
   the paddle could not be moved. min() keeps portrait identical (30% of
   a tall rail exceeds 74px) while guaranteeing travel when short. */
.slider.v .slider-knob{left:12%;width:76%;height:min(74px,30%);top:0}
.slider.h .slider-knob{top:12%;height:76%;width:min(74px,30%);left:0}
/* parked before play (ui.lock): the paddle is not live yet, and looks it */
.slider.locked .slider-knob{opacity:.4}
.slider-tip{position:absolute;inset:auto 0 12px;text-align:center;color:var(--ink-faint);font-size:.78rem;letter-spacing:.16em;text-transform:uppercase;pointer-events:none}
.action-btn{
  flex:0 0 auto;height:88px;border-radius:0;border:1px solid var(--line-hot);
  background:linear-gradient(160deg,rgba(245,158,11,.3),rgba(245,158,11,.1));color:var(--ink);
  font-weight:700;letter-spacing:.18em;font-size:1.05rem;cursor:pointer;touch-action:none;transition:transform .09s,filter .09s;
}
.action-btn:active,.action-btn.hot{transform:scale(.97);filter:brightness(1.4)}
.action-btn.small{height:72px}
.action-btn[disabled]{opacity:.35}

.dpad{flex:1;display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:.55rem;place-items:stretch}
.dp{
  border:1px solid var(--line);border-radius:0;background:rgba(255,255,255,.045);
  font-size:1.5rem;color:var(--ink-dim);cursor:pointer;touch-action:none;transition:transform .08s,background .12s;
}
.dp:active,.dp.hot{background:rgba(245,158,11,.35);color:var(--ink);transform:scale(.96)}
.dp.up{grid-area:1/2} .dp.left{grid-area:2/1} .dp.right{grid-area:2/3} .dp.down{grid-area:3/2}

/* ══════════ gamepad frame: direction left, actions right ══════════
   Thumbs rest at the outer edges of a held phone, so the old
   full-width d-pad with a button underneath fought the hardware. */
.pad-layout.gamepad{flex-direction:row;align-items:stretch;gap:.7rem}
.gp-dir{flex:1 1 56%;min-width:0;display:flex;align-items:flex-end;justify-content:flex-start}
.gp-mid{flex:0 0 auto;display:none;min-width:0}
#gp-mid-drive .mini-wrap{width:100%;margin:0}
.gp-act{flex:1 1 44%;min-width:0;display:flex;flex-direction:column;justify-content:flex-end;gap:.6rem}
/* keep the cross square and in the thumb zone — stretched to column
   height it became four tall slabs rather than a d-pad */
.gp-dir .dpad{flex:0 0 auto;width:100%;max-width:250px;aspect-ratio:1}
.gp-dir .stick{flex:0 0 auto;width:100%;max-width:250px;aspect-ratio:1}
/* tetris pad: ◀ ▶ on the left, rotate/drop/hold on the right. Soft drop is
   a low bar on a row of its own under the arrows, a clear gap away: between
   them it was under the thumb on every trip from one arrow to the other. */
.tet-dir{flex-direction:column;justify-content:flex-end;align-items:stretch;gap:1.15rem;width:100%}
.tet-move-row{display:flex;gap:.5rem}
.tet-dir .tet-move{flex:1 1 0;min-width:0;height:88px;font-size:1.7rem}
.tet-dir .tet-soft{flex:0 0 auto;height:50px;font-size:1.15rem;border-style:dashed;color:var(--ink-faint)}
.tet-act{gap:.5rem}
.tet-act .action-btn.round{font-size:2rem}
.tet-act-row{display:flex;gap:.5rem}
.tet-act-row .action-btn.small{flex:1 1 0;min-width:0;height:58px;font-size:1.1rem;letter-spacing:.06em}
/* fight pad: move + jump on the left, hit + special on the right */
.fig-dir{flex-direction:column;gap:.6rem;width:100%;justify-content:flex-end}
.fig-move{display:flex;gap:.5rem;width:100%}
.fig-move .fig-mv{flex:1 1 0;min-width:0;height:104px;font-size:1.9rem}
.fig-jump{width:100%;height:74px}
.fig-act{gap:.6rem}
.fig-act .action-btn.round{font-size:1.5rem;max-width:150px}
.fig-special{font-size:.95rem;letter-spacing:.1em}
/* Lagoon Lobbers (id lob) on a phone: an aim square over FIRE and the wide
   button, which share the row under it. A thumb dragged in the square is the
   aim and the strength; the other fight-pad games never show it. The wide
   button opens the bag, which lies over the square: a grid of items to tap. */
.aim-cell{display:none}
#lay-fight.lob-sq .fig-act{display:grid;grid-template:"sq sq" minmax(0,1fr) "fire spec" auto / auto minmax(0,1fr);gap:.45rem;align-items:center}
#lay-fight.lob-sq .aim-cell{display:grid;place-items:end center;grid-area:sq;align-self:stretch;min-width:0;min-height:0;container-type:size}
.aim-sq{position:relative;width:min(100cqw,100cqh,184px);height:min(100cqw,100cqh,184px);border:1px solid var(--line-hot);
  background:rgba(0,0,0,.28);touch-action:none;cursor:crosshair;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
.aim-sq svg{display:block;width:100%;height:100%;pointer-events:none}
.aim-sq.hot{border-color:var(--brand);background:rgba(245,158,11,.10)}
.aim-sq.off{opacity:.4}
.aim-rim{fill:none;stroke:var(--line-hot);stroke-width:2}
.aim-half{fill:none;stroke:var(--line-hot);stroke-width:1;stroke-dasharray:3 6;opacity:.7}
.aim-dead{fill:rgba(253,251,247,.07)}
.aim-ray{stroke:var(--ink-faint);stroke-width:2;stroke-dasharray:2 7;stroke-linecap:round}
.aim-line{stroke:var(--brand);stroke-width:7;stroke-linecap:round}
.aim-hub{fill:var(--ink-dim)}
.aim-dot{fill:var(--knob,var(--brand));stroke:var(--ink);stroke-width:2}
.aim-sq.no-pow .aim-line{stroke:var(--ink-dim)}
.aim-num{position:absolute;left:0;right:0;bottom:3px;text-align:center;font-weight:700;font-size:.82rem;letter-spacing:.08em;
  color:var(--ink);font-variant-numeric:tabular-nums;pointer-events:none}
#lay-fight.lob-sq .aim-cell{position:relative}
.lob-bag{position:absolute;inset:0;z-index:3;display:grid;grid-template-columns:repeat(auto-fill,minmax(70px,1fr));grid-auto-rows:minmax(38px,auto);
  gap:.28rem;padding:.28rem;overflow-y:auto;align-content:start;background:rgba(12,10,18,.97);border:1px solid var(--brand);
  touch-action:pan-y;-webkit-overflow-scrolling:touch}
.lob-bag[hidden]{display:none}
.lob-bag button{display:flex;flex-direction:column;justify-content:center;gap:.1rem;min-width:0;padding:.2rem .3rem;border:1px solid var(--line-hot);
  background:rgba(253,251,247,.05);color:var(--ink);font:inherit;text-align:left;cursor:pointer;touch-action:manipulation}
.lob-bag button b{font-size:.56rem;letter-spacing:.03em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lob-bag button i{font-style:normal;color:var(--brand)}
.lob-bag button small{font-size:.48rem;color:var(--ink-dim);line-height:1.15;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.lob-bag button.tool{border-color:rgba(127,208,255,.45)}
.lob-bag button.tool b{color:#9fd8ff}
.lob-bag button.on{border-color:var(--brand);background:rgba(245,158,11,.2)}
.lob-bag button.no{opacity:.38}
.lob-shelf{grid-column:1/-1;align-self:end;font-size:.5rem;letter-spacing:.1em;color:var(--ink-dim);padding-top:.15rem}
#lay-fight.lob-sq #fig-attack{grid-area:fire;width:78px;max-width:none;margin:0;align-self:center;font-size:1rem;letter-spacing:.1em}
#lay-fight.lob-sq #fig-special{grid-area:spec;height:62px;min-width:0}
/* Riposte: THROW and JUMP over ATTACK, over PARRY and SLIDE; ATTACK and
   PARRY are the two the thumb lives on. THROW greys out while the hand is
   empty (the game says when). DUCK, held, lies under the two arrows, where
   the thumb that runs can drop onto it. */
.fen-act{flex-direction:column;align-items:stretch;justify-content:flex-end;gap:.45rem}
.fen-move .fig-mv{height:min(128px,29vh)}
.fen-duck{width:100%;height:min(62px,15vh);min-height:40px}
.fen-b{flex:1 1 0;width:100%;min-height:44px;max-height:72px;height:auto;font-size:.95rem;letter-spacing:.12em}
.fen-big{flex:1.7 1 0;max-height:124px;font-size:1.15rem}
.fen-b:disabled{opacity:.35}
.fen-row{display:flex;gap:.45rem;flex:1 1 0;min-height:44px;max-height:72px}
.fen-row.fen-big{flex:1.7 1 0;max-height:124px}
.fen-row .fen-b{max-height:none;height:100%;width:auto}
.fen-row.fen-big #fen-parry{flex:1.7 1 0;font-size:1.15rem}
/* Kick Off's wind-up on the held button: it fills, then pulses */
#fig-attack.charging{animation:fig-charge var(--chg-ms,750ms) linear forwards}
#fig-attack.charged{box-shadow:inset 0 0 0 999px rgba(251,191,36,.38),0 0 0 4px var(--warn);animation:fig-full .28s steps(2) infinite}
@keyframes fig-charge{from{box-shadow:inset 0 0 0 0 rgba(251,191,36,.30)}to{box-shadow:inset 0 0 0 80px rgba(251,191,36,.38)}}
@keyframes fig-full{50%{box-shadow:inset 0 0 0 999px rgba(253,251,247,.45),0 0 0 4px var(--warn)}}
@media (prefers-reduced-motion:reduce){#fig-attack.charged{animation:none}}
/* bubble pad: two wide move buttons on the left, one big FIRE on the right */
.bub-dir{display:flex;gap:.5rem;align-items:flex-end;width:100%}
.bub-dir .bub-mv{flex:1 1 0;min-width:0;height:120px;font-size:2rem}
.bub-act .action-btn.round{font-size:1.5rem;letter-spacing:.12em}
/* Ninja Dash: one rocker on the left (two halves with no gap, so a thumb
   slides from one to the other without lifting), one big JUMP on the right */
.nin-dir{display:flex;gap:0;align-items:flex-end;width:100%}
.nin-dir .nin-mv{flex:1 1 0;min-width:0;height:140px;font-size:2rem}
.nin-dir .nin-mv:first-child{border-right-width:0}
.nin-act .action-btn.round{font-size:1.5rem;letter-spacing:.12em}
/* Mothlight: one round cross on the left that takes two directions at once
   (a thumb up and to the right runs right and aims up); on the right JUMP
   and SLASH, the two the thumb lives on, under GLOW and DASH */
.mth-dir{align-items:flex-end;justify-content:flex-start}
.mth-cross{position:relative;flex:0 0 auto;width:min(100%,184px,46vh);aspect-ratio:1;border:1px solid var(--line-hot);border-radius:50%;
  background:rgba(0,0,0,.28);touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
.mth-a{position:absolute;display:grid;place-items:center;width:32%;height:32%;border-radius:50%;color:var(--ink-dim);font-size:1.25rem;pointer-events:none}
.mth-up{left:34%;top:3%} .mth-down{left:34%;bottom:3%} .mth-left{left:3%;top:34%} .mth-right{right:3%;top:34%}
.mth-a.hot{color:var(--ink);background:rgba(245,158,11,.35)}
.mth-act{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:min(58px,15vh) min(100px,26vh);gap:.45rem;align-content:end;align-self:flex-end;width:100%}
.mth-b{width:100%;height:100%;min-height:0;font-size:.92rem;letter-spacing:.1em}
.mth-big{font-size:1.1rem}
/* Lone Arrow: a stick on the left; DRAW (held) over ROLL on the right */
.bow-act{gap:.6rem}
.bow-act .action-btn.round{font-size:1.3rem;letter-spacing:.1em;max-width:150px}
.bow-act .action-btn.wide{font-size:.95rem;letter-spacing:.14em}
/* Cinder Vault: a stick on the left; on the right four small buttons over the attack pad, which is held and can be dragged to aim */
.cin-act{display:grid;grid-template-columns:repeat(6,1fr);grid-template-rows:min(46px,12vh) min(46px,12vh) minmax(0,1fr);gap:.4rem;align-content:end;align-self:flex-end;width:100%;max-height:100%}
.cin-b{grid-column:span 2;width:100%;height:100%;min-height:0;font-size:.74rem;letter-spacing:.08em;padding:0 .2rem}
#cin-roll{grid-column:span 4}
.cin-b b{font-weight:700;color:var(--brand)}
.cin-b.on{border-color:var(--brand);color:var(--ink);background:rgba(245,158,11,.2)}
.cin-b.off{opacity:.4}
.cin-atk{grid-column:1 / -1;position:relative;display:grid;place-items:center;height:min(150px,36vh);min-height:84px;border-radius:26px;font-size:1.25rem;letter-spacing:.12em;touch-action:none;user-select:none;-webkit-user-select:none}
.cin-atk.live{filter:brightness(1.4)}
.cin-nub{position:absolute;left:50%;top:50%;width:26px;height:26px;margin:-13px 0 0 -13px;border-radius:50%;background:var(--knob,var(--brand));opacity:0;pointer-events:none}
.cin-atk.aim .cin-nub{opacity:.85}
/* what a thing just taken did, for a moment; and the KIT sheet: how the class works, and what is carried */
#lay-cinder{position:relative}
.cin-got{position:absolute;left:50%;top:.4rem;transform:translateX(-50%);z-index:3;max-width:min(92%,30rem);padding:.45rem .7rem;border-radius:12px;background:rgba(13,11,18,.94);border:1px solid var(--brand);font-size:.8rem;line-height:1.3;text-align:center;pointer-events:none}
.cin-got b{display:block;color:var(--brand);letter-spacing:.08em}
.cin-sheet{position:absolute;inset:0;z-index:4;display:flex;flex-direction:column;background:#0d0b12;border-radius:14px;padding:.5rem .7rem;text-align:left}
.cin-sheet-head{display:flex;align-items:center;gap:.5rem;flex:none;padding-bottom:.4rem}
.cin-sheet-head b{flex:1;letter-spacing:.12em;color:var(--brand)}
.cin-sheet-b{width:auto;height:auto;min-height:0;padding:.4rem .7rem;font-size:.7rem;letter-spacing:.08em}
.cin-sheet-body{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch}
.cin-sheet-blurb{margin:0 0 .5rem;font-size:.8rem;color:var(--ink-dim)}
.cin-how,.cin-kit{list-style:none;margin:0 0 .6rem;padding:0}
.cin-how li{font-size:.78rem;color:var(--ink-dim);padding:.12rem 0 .12rem .8rem;text-indent:-.8rem}
.cin-how li::before{content:'· '}
.cin-sheet h4{margin:.2rem 0 .3rem;font-size:.72rem;letter-spacing:.14em;color:var(--ink-dim)}
.cin-kit li{font-size:.78rem;padding:.2rem 0;border-top:1px solid rgba(255,255,255,.07)}
.cin-kit b{margin-right:.4rem}
.cin-kit i{font-style:normal;color:var(--ink-dim);font-size:.68rem;letter-spacing:.08em;margin-right:.4rem}
.cin-kit span{color:var(--ink-dim)}

/* ══════════ Mic Drop: the phone is the microphone ══════════ */
#lay-sing{gap:.7rem}
.sing-head{display:flex;justify-content:space-between;align-items:baseline;gap:.6rem;min-height:1.4em}
.sing-song{color:var(--ink-dim);font-size:.85rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sing-score{display:flex;align-items:baseline;gap:.5rem;font-family:var(--mono)}
.sing-score b{font-size:1.4rem;color:var(--ink)}
.sing-score i{font-style:normal;font-size:.8rem;letter-spacing:.12em;font-weight:700}
.sing-score .r-perfect{color:var(--warn)}
.sing-score .r-great{color:var(--brand-2)}
.sing-score .r-good{color:var(--ok)}
.sing-score .r-ok,.sing-score .r-meh{color:var(--ink-dim)}
.sing-score .r-miss{color:var(--p2)}
.sing-mic{
  flex:1 1 auto;min-height:150px;display:grid;grid-template-columns:auto 1fr 12px;grid-template-rows:1fr auto;
  align-items:center;gap:.4rem 1rem;padding:1rem 1.2rem;cursor:pointer;
  border:1px solid var(--line-hot);border-radius:0;color:var(--ink);text-align:left;
  background:linear-gradient(160deg,color-mix(in srgb,var(--knob) 28%,transparent),rgba(255,255,255,.02));
}
.sing-mic:active{filter:brightness(1.25)}
.sing-glyph{grid-column:1;grid-row:1 / span 2;font-size:3.2rem;line-height:1}
.sing-state{grid-column:2;grid-row:1;align-self:end;font-weight:700;letter-spacing:.14em;font-size:1rem}
.sing-note{grid-column:2;grid-row:2;align-self:start;font-family:var(--mono);color:var(--ink-dim);font-size:.9rem}
.sing-meter{
  grid-column:3;grid-row:1 / span 2;align-self:stretch;width:12px;
  background:rgba(255,255,255,.08);display:flex;align-items:flex-end;overflow:hidden
}
.sing-meter i{display:block;width:100%;height:100%;background:var(--knob);transform:scaleY(0);transform-origin:bottom;transition:transform .05s linear}
.sing-mic.live{border-color:var(--knob);animation:sing-glow 2.4s ease-in-out infinite}
@keyframes sing-glow{50%{box-shadow:0 0 0 3px color-mix(in srgb,var(--knob) 30%,transparent)}}
#lay-sing[data-stage="sing"] .sing-mic{flex:0 0 auto;min-height:0;padding:.6rem 1rem}
#lay-sing[data-stage="sing"] .sing-glyph{font-size:2rem}
.sing-scope{flex:0 0 auto;width:100%;height:64px;background:rgba(255,255,255,.03);border:1px solid var(--line)}
.sing-lyric{flex:1 1 auto;display:flex;flex-direction:column;justify-content:center;text-align:center;min-height:0}
.sing-now{margin:0;font-size:1.35rem;line-height:1.3;color:var(--ink)}
.sing-next{margin:.5rem 0 0;font-size:.95rem;color:var(--ink-faint)}
.sing-setup{display:flex;flex-direction:column;gap:.6rem}
.sing-diff{display:flex;width:100%}
.sing-diff button{flex:1;padding:.7rem .4rem;font-size:.85rem;letter-spacing:.12em}
.sing-ready{height:74px}
.sing-ready.done{opacity:.6}
.sing-err{margin:0;color:var(--p2);font-size:.85rem;line-height:1.4;min-height:1.2em}
@media (orientation:landscape) and (max-height:520px){
  #lay-sing{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:auto 1fr auto;gap:.5rem .9rem}
  .sing-head{grid-column:1 / span 2}
  .sing-mic{min-height:0}
  .sing-scope{grid-column:1;height:48px}
  .sing-lyric,.sing-setup{grid-column:2;grid-row:2 / span 2}
  .sing-err{grid-column:1 / span 2}
}
/* the mic popup */
.sing-modal{
  position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;
  padding:16px;background:rgba(5,4,8,.78);backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);
  touch-action:manipulation
}
.sing-modal[hidden]{display:none}
/* QR scanner: a square viewfinder, cropped rather than letterboxed */
.scan-frame{position:relative;width:100%;aspect-ratio:1;max-height:55dvh;margin:0 auto;background:#000;border:1px solid var(--line);overflow:hidden}
.scan-frame video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.scan-frame::after{content:"";position:absolute;inset:14%;border:2px solid var(--brand);opacity:.75;pointer-events:none}
/* its zoom: − slider + and the level, one row a thumb reaches; two fingers
   on the viewfinder pinch (touch-action hands them to scan.js) */
.scan-frame{touch-action:none}
.scan-zoom{display:flex;align-items:center;gap:.6rem}
.scan-zoom[hidden]{display:none}
.scan-zoom .ghost-btn{flex:0 0 auto;min-width:44px;min-height:44px;padding:0;font-size:1.3rem;line-height:1}
.scan-zoom .ghost-btn[disabled]{opacity:.4}
.scan-zoom input[type=range]{flex:1 1 auto;min-width:0;min-height:44px;accent-color:var(--brand)}
.scan-zoom output{flex:0 0 auto;min-width:3.4em;text-align:right;font-family:var(--mono);font-size:.9rem;color:var(--ink)}
.sing-modal-card{
  width:100%;max-width:420px;max-height:100%;overflow-y:auto;display:flex;flex-direction:column;align-items:stretch;gap:.8rem;
  padding:1.4rem 1.2rem 1rem;text-align:center;background:var(--panel-solid, #16131a);
  border:1px solid color-mix(in srgb,var(--knob) 60%,var(--line));box-shadow:0 20px 60px rgba(0,0,0,.5)
}
.sing-modal-glyph{font-size:3rem;line-height:1}
.sing-modal-title{margin:0;font-size:1.3rem;color:var(--ink)}
.sing-modal-text{margin:0;color:var(--ink-dim);font-size:.92rem;line-height:1.45}
.sing-dev{display:flex;flex-direction:column;gap:.35rem;text-align:left;font-size:.78rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-faint)}
.sing-dev select{
  font:inherit;text-transform:none;letter-spacing:0;font-size:1rem;color:var(--ink);padding:.7rem .6rem;
  background:rgba(255,255,255,.05);border:1px solid var(--line);border-radius:0
}
.sing-allow{height:72px;touch-action:manipulation}
.sing-allow[disabled]{opacity:.5}
.sing-modal-err{margin:0;color:var(--p2);font-size:.88rem;line-height:1.45;min-height:0}
.sing-modal-err:empty{display:none}
.sing-later{align-self:center;touch-action:manipulation}
.sing-mic{touch-action:manipulation}

/* the screen's "add an UltraStar song" button, only while picking */
.kar-import{position:absolute;right:1rem;top:1rem;z-index:4;font-size:.8rem;opacity:.75}
.kar-import:hover{opacity:1}
.action-btn.armed{border-color:var(--brand);color:var(--ink);background:rgba(245,158,11,.2)}
.action-btn.wide{height:66px;letter-spacing:.18em}
.action-btn.round{
  border-radius:50%;aspect-ratio:1;height:auto;width:100%;max-width:190px;
  align-self:flex-end;margin-left:auto;font-size:1.05rem;
}

/* ── virtual joystick: continuous hold, for grid-free movement ── */
.stick{flex:1;display:grid;place-items:center;touch-action:none;position:relative}
.stick-base{
  position:relative;width:min(100%,240px);aspect-ratio:1;border-radius:50%;
  border:1px solid var(--line);background:radial-gradient(circle at 50% 50%,rgba(245,158,11,.14),rgba(167,139,250,.06));
  display:grid;place-items:center;
}
.stick-base::before{
  content:"";position:absolute;inset:22%;border-radius:50%;border:1px dashed rgba(140,160,255,.18);
}
.stick-knob{
  width:38%;height:38%;border-radius:50%;background:var(--knob,var(--p1));
  box-shadow:none;transition:transform .05s linear;will-change:transform;
}
.stick.live .stick-knob{transition:none}
/* a co-op maze pad: your quadrants tinted, the rest shaded out */
.stick-own{position:absolute;inset:0;border-radius:50%;pointer-events:none}
.dp.off{opacity:.18;pointer-events:none}
#lay-bomb.no-act .gp-act{display:none}
/* Snake: steering is all there is. No button, so the cross is as big as
   the phone allows and sits bottom-centre, under either thumb; the rest of
   the pad is there to be swiped on. */
#lay-dpad{position:relative}
#lay-dpad.no-act .gp-act{display:none}
#lay-dpad.no-act{justify-content:center}
#lay-dpad.no-act .gp-dir{flex:1 1 100%;justify-content:center;align-items:flex-end;container-type:size}
#lay-dpad.no-act .gp-dir .dpad,#lay-dpad.no-act .gp-dir .stick{width:min(100%,300px);max-width:none}
/* never taller than the room it has (a short window, a phone that is hosting) */
#lay-dpad.no-act .gp-dir .dpad,#lay-dpad.no-act .gp-dir .stick{width:min(100cqw,100cqh,300px)}
#lay-dpad.no-act .dp{font-size:2rem}
.swipe-tip{position:absolute;top:.2rem;left:0;right:0;margin:0;text-align:center;pointer-events:none;
  color:var(--ink-faint);font-size:.74rem;letter-spacing:.14em;text-transform:uppercase}
@media (orientation:landscape) and (max-height:560px){
  /* landscape: the cross at the left thumb, the board fills the rest */
  #lay-dpad.no-act{justify-content:flex-start}
  #lay-dpad.no-act .gp-dir{flex:0 0 34%;justify-content:flex-start}
  #lay-dpad.no-act .gp-mid{flex:1 1 auto}
  .swipe-tip{display:none}
}
/* A-Maze-ing: the only control is the stick, so it sits bottom-centre
   where either thumb reaches it */
#lay-bomb.no-act{justify-content:center}
#lay-bomb.no-act .gp-dir{flex:1 1 100%;justify-content:center;align-items:flex-end}
@media (orientation:landscape) and (max-height:560px){
  /* landscape keeps the mini-view, beside the stick rather than under it */
  #lay-bomb.no-act{display:grid;grid-template-columns:1fr auto 1fr;align-items:end}
  #lay-bomb.no-act .gp-dir{grid-column:2;grid-row:1;width:min(42vw,190px)}
  #lay-bomb.no-act .gp-mid{grid-column:3;grid-row:1;align-self:stretch}
}

/* ══════════ Sideways on the drive pad: a strip to steer, a pedal each ══════════ */
.drive-pedals{display:grid;grid-template-columns:1fr 1.5fr;gap:.5rem;width:100%}
.drive-pedals[hidden]{display:none}
.drive-pedals .action-btn{height:clamp(92px,22vh,170px);font-family:var(--mono);font-size:.8rem;letter-spacing:.1em}
.action-btn.brake{border-color:rgba(224,91,122,.5);background:linear-gradient(160deg,rgba(224,91,122,.32),rgba(224,91,122,.10))}
#lay-drive.pedals .steer-row{display:none}
#lay-drive.pedals .wheel{width:100%;height:clamp(92px,22vh,170px);flex:none}

/* ══════════ Redline: auto-gas, arrows steer, both = brake ══════════ */
.steer-row{flex:0 0 auto;display:grid;grid-template-columns:1fr auto 1fr;gap:.7rem;align-items:stretch}
.steer-arw{
  height:clamp(96px,20vh,160px);border-radius:0;border:1px solid var(--line-hot);
  background:linear-gradient(160deg,rgba(245,158,11,.26),rgba(245,158,11,.08));color:var(--ink);
  font-size:2rem;cursor:pointer;touch-action:none;transition:transform .08s,filter .08s,background .12s;
}
.steer-arw:active,.steer-arw.hot{transform:scale(.96);filter:brightness(1.45);background:rgba(245,158,11,.4)}
.steer-brake{
  align-self:center;font-family:var(--mono);font-size:.7rem;letter-spacing:.14em;
  color:var(--ink-faint);border:1px solid var(--line);border-radius:0;padding:.3rem .55rem;
  text-align:center;transition:.15s;white-space:nowrap;
}
.steer-row.braking .steer-arw{
  background:linear-gradient(160deg,rgba(224,91,122,.45),rgba(224,91,122,.2));
  border-color:var(--p2);box-shadow:none;
}
.steer-brake.on{color:#0b0a0d;background:var(--p2);border-color:var(--p2);box-shadow:none}
/* Redline & Vertigo now use the shared dir-left / act-right cluster model */
.drive-dir,.climb-dir{flex-direction:column;align-items:stretch;justify-content:flex-end;gap:.5rem}
.steer-row{width:100%}
.drive-acts .ghost-btn,.climb-acts .ghost-btn{width:100%;font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;padding:.6rem .5rem}
.drive-acts .ghost-btn.armed,.climb-acts .ghost-btn.armed{border-color:var(--brand);color:var(--ink);background:rgba(245,158,11,.2)}
.drive-acts .ghost-btn[disabled],.climb-acts .ghost-btn[disabled]{opacity:.4}
.drive-tip{margin:.25rem 0 0;text-align:center;color:var(--ink-faint);font-size:.72rem;letter-spacing:.06em}
#lay-climb .climb-strip{width:100%;height:clamp(96px,20vh,150px);max-height:none;flex:none}
/* the item is the one thing pressed in a hurry: a real button, not a chip */
#lay-climb #btn-climb-item{min-height:64px;font-size:.8rem}
#lay-climb #btn-climb-tilt{min-height:48px}
#lay-climb .climb-note{margin:.3rem 0 0}

/* ══════════ landscape: thumbs outboard, screen in the middle ══════════
   The mini-view is relocated into .gp-mid by controller.js so it sits
   between the thumbs rather than stealing height from them. */
@media (orientation:landscape) and (max-height:560px){
  .pad-stage{padding-top:calc(env(safe-area-inset-top) + .4rem);padding-bottom:calc(env(safe-area-inset-bottom) + .4rem);gap:.4rem}
  .pad-hint{display:none}
  .pad-top{padding-bottom:0}
  .pad-layout.gamepad{gap:.5rem}
  .gp-dir,.gp-act{flex:0 0 30%}
  .gp-mid{display:flex;flex:1 1 40%;align-items:center;justify-content:center}
  .gp-mid .mini-wrap{width:100%;margin:0}
  .stick-base{width:min(100%,190px)}
  .action-btn.round{max-width:150px;align-self:center;margin:0 0 0 auto}

  /* Redline uses the generic gamepad landscape (dir | view | acts) */
  #lay-drive .drive-tip{display:none}

  /* Pong. Which way the paddle travels decides the shape: a left/right
     paddle needs VERTICAL travel, so its strip stands full-height at one
     edge with the arena between the thumbs. A top/bottom paddle needs
     width, so it keeps a wide strip and the arena sits above it. */
  /* A hosting phone draws to #stage above the pad, so its middle slot is
     empty and the pad is just [strip][power] at the two thumbs. A JOINING
     phone has the board in the middle, so it becomes three columns. */
  /* One template either way: strip and button at the two thumbs, the board
     in the middle when this phone is a joiner (a host draws it above). */
  #lay-analog.an-v{display:grid;grid-template-columns:minmax(120px,24vw) 1fr;grid-template-rows:1fr;gap:.5rem;align-items:stretch}
  #lay-analog.an-v .slider{grid-column:1;grid-row:1;height:auto;flex:none}
  #lay-analog.an-v .gp-mid{display:none}
  #lay-analog.an-v.has-mid .gp-mid{display:flex;grid-column:2;grid-row:1;flex:1 1 auto;padding:.2rem}
  #lay-analog.an-v #btn-power{grid-column:3;grid-row:1;height:auto;flex:none}
  #lay-analog.an-h{display:grid;grid-template-columns:1fr;grid-template-rows:auto;gap:.5rem}
  #lay-analog.an-h.has-mid{grid-template-rows:1fr auto}
  #lay-analog.an-h .gp-mid{display:none}
  #lay-analog.an-h.has-mid .gp-mid{display:flex;grid-column:1;grid-row:1;flex:1 1 auto;min-height:0;padding:.2rem}
  #lay-analog.an-h .slider{grid-column:1;grid-row:1;height:64px;flex:none;align-self:end}
  #lay-analog.an-h.has-mid .slider{grid-row:2}
  #lay-analog.an-h #btn-power{grid-column:2;grid-row:1;height:64px;width:min(38vw,180px);flex:none;align-self:end}
  #lay-analog.an-h.has-mid #btn-power{grid-row:2}
  #lay-analog .slider-tip{display:none}

  /* Vertigo. The tower band is sized from panel HEIGHT, so a tall pad
     starved it to a ~100px sliver in an 844px viewport. One compact control
     row hands the height back to the climb. */
  /* Vertigo uses the generic gamepad landscape (strip | view | acts) */
  #lay-climb .climb-strip{height:clamp(84px,26vh,150px)}
  #lay-climb .climb-note{display:none}

  /* Pesten. The hand is pinned to a 120px floor so that on a TALL phone it
     sits by the thumb instead of marooned at the top of a big box. Landscape
     inverts that problem: the box is SHORT, so the floor became a ceiling and
     the cards shrank to ~30px slivers. Here it may take the room it has. */
  #lay-cards .cards-hand{flex:1 1 auto;min-height:0}
  #lay-cards .cards-top{padding:0}

  /* The buzzer is sized from the phone's width, which on its side is the
     long edge: it hung out under the bottom. Here the height decides. */
  #lay-buzzer .buzzer{flex:none;width:auto;height:100%;max-height:340px}
}


.buzzer{
  flex:1;border-radius:50%;aspect-ratio:1;margin:auto;width:min(78vw,340px);height:auto;
  border:3px solid var(--p2);background:radial-gradient(circle at 35% 30%,rgba(224,91,122,.55),rgba(224,91,122,.16));
  color:var(--ink);font-size:2.2rem;font-weight:700;letter-spacing:.16em;cursor:pointer;touch-action:none;
  box-shadow:none;transition:transform .07s,filter .07s;
}
.buzzer:active,.buzzer.hot{transform:scale(.94);filter:brightness(1.35)}

#lay-menu{justify-content:center}
.menu-pad{display:grid;grid-template-columns:1fr 1.6fr 1fr;grid-template-rows:1fr 1.25fr 1fr;gap:.6rem;align-items:stretch;height:min(50vh,330px)}
.menu-pad .dp.ok{grid-area:2/2}
.dp.ok{font-size:1rem;font-weight:700;letter-spacing:.16em;border-color:var(--brand)}
.menu-sel{text-align:center;font-family:var(--mono);color:var(--brand-2);font-size:1.05rem;margin:0}

/* ══════════ sideways pad on an upright phone ══════════
   controller.js sets .pad-rot for the gamepad-style games (Blast Radius,
   Stick Brawl, Bubble Bash) while the phone is in portrait. The whole
   live pad, top bar included, is turned a quarter so the phone is held
   like a gamepad: the bar runs along the long edge and the thumbs sit at
   the two ends. The rules below repeat the landscape gamepad frame, since
   the viewport itself is still portrait and those media queries are off. */
#view-controller.pad-rot #pad-live{
  position:fixed;top:0;left:100vw;z-index:2;
  width:100vh;width:100dvh;height:100vw;
  transform-origin:0 0;transform:rotate(90deg);
  /* the notch is now at the pad's left end, the home bar at its right */
  padding:.4rem calc(env(safe-area-inset-bottom) + .8rem) .4rem calc(env(safe-area-inset-top) + .8rem);
  gap:.4rem;
}
.pad-rot .pad-hint{display:none}
.pad-rot .pad-top{padding-bottom:0}
.pad-rot .pad-layout.gamepad{gap:.5rem}
.pad-rot .gp-dir,.pad-rot .gp-act{flex:0 0 30%}
.pad-rot .gp-mid{display:flex;flex:1 1 40%;align-items:center;justify-content:center}
.pad-rot .gp-mid .mini-wrap,
.pad-rot .gp-mid .mini-wrap.big{width:100%;margin:0;align-self:stretch;height:auto;min-height:0;max-height:100%}
.pad-rot .stick-base{width:min(100%,190px)}
.pad-rot .action-btn.round{max-width:150px;align-self:center;margin:0 0 0 auto}

/* ── room-controls sheet (phone) ──
   Sits above every pad layout, so a tap in here can never reach the
   slider/d-pad/buzzer underneath and fire a stray game input. */
.sheet{position:fixed;inset:0;z-index:50;display:flex;align-items:flex-end;background:rgba(6,8,22,.72);backdrop-filter:blur(6px);animation:sheetFade .18s ease}
@keyframes sheetFade{from{opacity:0}}
.sheet-card{
  width:100%;background:var(--panel-solid);border-top:1px solid var(--line-hot);
  border-radius:0;padding:1.1rem 1.1rem calc(env(safe-area-inset-bottom) + 1.1rem);
  box-shadow:var(--shadow);animation:sheetUp .24s cubic-bezier(.2,.9,.3,1.05);
  /* the sheet has grown section by section and now overflows a landscape
     phone — cap it and scroll inside so the close button is always reachable */
  max-height:100dvh;overflow-y:auto;-webkit-overflow-scrolling:touch;
}
/* keep the header (and its close button) pinned while the body scrolls */
.sheet-head{position:sticky;top:0;z-index:1;background:var(--panel-solid);padding-top:.2rem}
@keyframes sheetUp{from{transform:translateY(18%)}}
.sheet-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:.5rem}
.sheet-head h3{margin:0;font-size:1.05rem;letter-spacing:-.01em}
.sheet-sect{display:flex;flex-direction:column;gap:.5rem;margin-bottom:.5rem}
.sheet-label{margin:.5rem 0 0;font-size:.72rem;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-faint)}
.seg.wide{display:flex;width:100%}
.seg.wide button{flex:1;padding:.62rem .5rem;font-size:.85rem;text-align:center}
.sheet-btn{
  width:100%;border:1px solid var(--line);border-radius:0;background:rgba(255,255,255,.045);
  color:var(--ink);padding:.9rem 1rem;font-size:1rem;font-weight:500;cursor:pointer;transition:.18s;
}
a.sheet-btn{display:block;text-align:center;text-decoration:none}
.sheet-btn:active{transform:scale(.99)}
.sheet-btn.danger{border-color:rgba(224,91,122,.55);color:var(--p2)}
.sheet-btn.danger.armed{background:var(--p2);border-color:var(--p2);color:#0b0a0d;font-weight:700}
.sheet-note{margin:.3rem 0 0;text-align:center;font-size:.75rem;color:var(--ink-faint)}
/* Volume for the ambient bed. Sits directly under its toggle, so it
   borrows the button's metrics rather than inventing a second rhythm. */
.sheet-range{display:flex;align-items:center;gap:.7rem;margin:.4rem 0 0;padding:.55rem .8rem;
  border:1px solid var(--line);border-radius:.6rem;color:var(--ink-dim);font-size:.82rem}
.sheet-range span{flex:0 0 auto}
.sheet-range input[type=range]{flex:1 1 auto;min-width:0;accent-color:var(--brand)}
.sheet-credit{opacity:.72;font-size:.72rem}

/* Now-playing row. A library needs to answer "what is this?" in a way a
   single looping bed never did. The title is UI text, not arcade chrome,
   so it stays in the body face — see the account-menu note below. */
.music-now{display:flex;align-items:center;gap:.5rem;margin:.1rem 0 .35rem}
.music-now[hidden]{display:none}
.music-track{
  margin:0;flex:1 1 auto;min-width:0;font-size:.78rem;color:var(--ink-dim);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.music-skip{flex:none;width:auto;padding:.3rem .7rem;font-size:.75rem}
/* the safe exit people actually reach for — it should read as the obvious one */
.sheet-btn.safe{border-color:var(--line-hot);background:rgba(245,158,11,.16);font-weight:700}

/* The phone sheet slides up from the bottom, which is right for a thumb.
   On a TV or laptop that reads as an accident, so centre it there. */
@media (min-width:861px){
  .room-sheet{align-items:center;justify-content:center}
  .room-sheet .sheet-card{
    max-width:760px;border-radius:0;border:1px solid var(--line-hot);
    padding-bottom:1.1rem;max-height:96dvh;animation:popIn .2s cubic-bezier(.2,.9,.3,1.2);
  }
}
/* The room menu never scrolls. It shows one section at a time behind
   three tabs, and inside a section the groups sit side by side whenever
   the card is wide enough — so a short landscape phone spends its width
   instead of its height. A big screen has room for everything and drops
   the tabs (see the min-width/min-height query below). */
.room-sheet .sheet-card{
  display:flex;flex-direction:column;gap:.5rem;
  scrollbar-width:thin;scrollbar-color:var(--line) transparent;overscroll-behavior:contain;
}
.room-sheet .sheet-card > *{flex:0 0 auto;margin:0}
/* sticky measures from inside the card's padding, so pull the pinned
   header back out over it or it sits a padding's depth too low */
.room-sheet .sheet-head{top:-1.1rem;margin:-1.1rem -1.1rem 0;padding:.5rem 1.1rem;border-bottom:1px solid var(--line)}
.room-sheet .sheet-label,.room-sheet .sheet-note,.room-sheet .sheet-fine{margin:0}
.room-sheet .sheet-note,.room-sheet .sheet-fine{text-align:left}
.room-sheet .sheet-btn{padding:.6rem 1rem;font-size:.95rem;text-align:center;min-width:0}
.room-sheet a.sheet-btn{text-decoration:none;display:block}
.sheet-pair{display:grid;grid-template-columns:repeat(auto-fit,minmax(0,1fr));gap:.5rem}
.rs-panel{display:none;gap:.8rem 1rem;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));align-items:start}
.rs-panel.on{display:grid}
.rs-group{display:flex;flex-direction:column;gap:.5rem;min-width:0}
.rs-span{grid-column:1/-1}
.menu-players{display:grid;gap:.4rem;grid-template-columns:repeat(auto-fill,minmax(280px,1fr))}
.mp-row{min-width:0;white-space:nowrap}
/* the name takes the slack, so the buttons line up down the right edge */
.mp-row b{overflow:hidden;text-overflow:ellipsis;min-width:0;flex:1 1 auto}
/* a short screen keeps the controls and drops the small print */
@media (max-height:700px){
  .room-sheet .sheet-note,.room-sheet .sheet-fine:not(#room-who){display:none}
  .room-sheet .sheet-btn{padding:.42rem .8rem;font-size:.9rem}
  .room-sheet .mp-row{padding:.3rem .6rem;font-size:.85rem}
  .room-sheet .sheet-card{gap:.4rem;padding-bottom:.7rem}
  .rs-panel{gap:.5rem 1rem}
  .rs-group,.menu-players{gap:.35rem}
}
/* a narrow, short phone cannot stack twelve players: two to a row */
@media (max-width:520px){.room-sheet .mp-tag{display:none}}
@media (max-width:520px) and (max-height:700px){
  .menu-players{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .room-sheet .mp-row{gap:.35rem;padding:.3rem .4rem}
  .room-sheet .mp-kick,.room-sheet .mp-keys{padding:.15rem .3rem;font-size:.62rem}
}
/* A phone on its side has almost no height. The tabs move up beside the
   close button, the section labels go (the tab already names them), and
   everything packs three across. */
@media (max-height:480px){
  .room-sheet .sheet-card{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.35rem .5rem;padding:.5rem .7rem .6rem;align-content:start}
  .room-sheet .sheet-card > *{grid-column:1/-1}
  .room-sheet .sheet-head{position:static;grid-column:2;grid-row:1;margin:0;padding:0;border:0}
  .room-sheet .sheet-head h3{display:none}
  .room-sheet .rs-tabs{grid-column:1;grid-row:1}
  .room-sheet .rs-tabs button{padding:.35rem .4rem}
  .room-sheet .sheet-label{display:none}
  .rs-panel{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:.4rem .6rem}
  .menu-players{grid-template-columns:repeat(auto-fill,minmax(165px,1fr))}
  .room-sheet .mp-tag{display:none}
  .room-sheet .mp-row{gap:.35rem;padding:.2rem .4rem;font-size:.8rem}
  .room-sheet .mp-kick,.room-sheet .mp-keys{padding:.12rem .3rem;font-size:.62rem}
  .room-sheet .sheet-btn{padding:.35rem .5rem;font-size:.85rem}
  .room-sheet .sheet-range{margin:0;padding:.3rem .6rem}
  .room-sheet .music-now{margin:0}
  .room-sheet .seg.wide button{padding:.4rem .3rem}
}
/* A big screen has room for everything at once: the tabs go, and the
   three sections stack with their groups side by side. */
@media (min-width:1100px) and (min-height:940px){
  .room-sheet .sheet-card{max-width:1060px}
  .room-sheet .rs-tabs{display:none}
  .rs-panels{display:flex;flex-direction:column;gap:1rem}
  .rs-panel,.rs-panel.on{display:grid}
}
.mp-row{
  display:flex;align-items:center;gap:.6rem;border:1px solid var(--line);
  border-radius:0;padding:.55rem .7rem;font-size:.92rem;
}
/* the host star: filled on the host, an outline to click on anyone else */
.mp-host{flex:0 0 auto;border:0;background:none;color:inherit;font-family:system-ui,sans-serif;font-size:1.1rem;line-height:1;padding:0 .1rem;cursor:pointer;opacity:.5}
.mp-host:hover:not([disabled]){opacity:1}
.mp-host.on{opacity:1;cursor:default}
.mp-host[disabled]:not(.on){opacity:.2;cursor:not-allowed}
/* the pixel face draws a star a few pixels wide; borrow the system one */
/* The host's seat dot grows to hold a star, centred, in the page colour so
   it reads on any seat colour. The pixel face draws a star a few pixels
   wide, so this borrows the system one. */
.seat .dot.host{width:17px;height:17px;display:inline-flex;align-items:center;justify-content:center}
.seat .dot.host i{font-style:normal;font-family:system-ui,sans-serif;font-size:12px;line-height:1;color:var(--bg);transform:translateY(-.5px)}
.mp-row .dot{width:10px;height:10px;border-radius:50%;background:currentColor;box-shadow:none;flex:0 0 auto}
.mp-row b{color:var(--ink)}
.mp-tag{margin-left:auto;font-family:var(--mono);font-size:.68rem;color:var(--ink-faint)}
.mp-row.lost{border-color:rgba(227,154,72,.5)}
.mp-row.lost .mp-tag{color:var(--p4)}
.mp-row.empty{border-style:dashed;color:var(--ink-faint)}
.mp-kick{
  border:1px solid rgba(224,91,122,.5);background:none;color:var(--p2);
  border-radius:0;padding:.2rem .6rem;font-size:.72rem;cursor:pointer;flex:0 0 auto;
}
/* keyboard players change their keys here, now the seat no longer shows them */
.mp-keys{
  border:1px solid var(--line);background:none;color:var(--ink-dim);
  border-radius:0;padding:.2rem .6rem;font-size:.72rem;cursor:pointer;flex:0 0 auto;
}
.mp-keys:hover{border-color:var(--line-hot);color:var(--ink)}
.mp-kick:hover:not([disabled]){background:var(--p2);color:#0b0a0d}
.mp-kick[disabled]{opacity:.35;cursor:not-allowed}
.mp-kick.armed{background:var(--p2);color:#0b0a0d;font-weight:700}

@media (max-width:860px){
  #lobby{grid-template-columns:1fr}
  .sb-players{order:3;flex-basis:100%;justify-content:flex-start}
}
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms !important;transition-duration:.01ms !important}
}

/* ══════════════ party pack pads ══════════════ */
.vote-list{flex:1;display:flex;flex-direction:column;gap:.6rem;overflow-y:auto;padding:.1rem;
  /* a list longer than the phone scrolls under the thumb (the pad itself is touch-action:none) */
  touch-action:pan-y;overscroll-behavior:contain;min-height:0}
.vote-list .vote-opt{touch-action:pan-y;flex-shrink:0}

/* ── Crypt pad: hero sheet, info card, choices, d-pad — always upright ── */
#lay-crown{gap:.55rem}
/* The whole pad is in the pixel face, like the game on the big screen. It
   is a wide face, so sizes run smaller than the body face would need. */
#lay-crown,#lay-crown button,#lay-crown b,#lay-crown p,#lay-crown small{font-family:var(--mono);letter-spacing:0}
/* A drawn pixel arrow, seven pixels by five, in the text's own colour and
   centred on the line: the pixel face has no arrow of its own. */
.px-arrow{display:inline-block;width:1em;height:.72em;margin:0 .4em;vertical-align:middle;position:relative;top:-.08em;background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 7' shape-rendering='crispEdges'%3E%3Cpath d='M0 3h7v1H0zM5 1h1v5H5zM6 2h1v3H6zM7 3h2v1H7z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 7' shape-rendering='crispEdges'%3E%3Cpath d='M0 3h7v1H0zM5 1h1v5H5zM6 2h1v3H6zM7 3h2v1H7z'/%3E%3C/svg%3E") center/contain no-repeat}
/* No top bar on this pad: the hero sheet already says who you are. Only
   the menu button stays, small, floating at the top centre above the pad —
   in the gap between the name and the class, so it covers nothing. */
#pad-live.is-crown{position:relative;gap:.4rem;padding-top:calc(env(safe-area-inset-top) + .5rem)}
#pad-live.is-crown .pad-top{position:absolute;top:calc(env(safe-area-inset-top) + .2rem);left:50%;transform:translateX(-50%);z-index:6;padding:0;gap:0}
#pad-live.is-crown .pad-you,#pad-live.is-crown .pad-title,#pad-live.is-crown .pad-hint,#pad-live.is-crown .pad-keys{display:none}
#pad-live.is-crown #btn-pad-menu{min-height:0;padding:.1rem .8rem;font-size:.85rem;line-height:1.2;opacity:.8;background:var(--panel-solid)}
#pad-live.is-crown .cr-hero{padding-top:1.45rem}
/* before a class is picked there is no hero sheet: the panel is on top */
#pad-live.is-crown .cr-hero[hidden] + .cr-info{padding-top:1.5rem}
/* In a fight: no d-pad, the card takes what room there is, and the choices
   sit at the bottom in rows of one fixed height. */
#lay-crown[data-mode="battle"] .cr-pad{display:none}
/* ...until the hero may walk: then the fight pad sits between the panel and
   the choices, which keep the bottom of the screen and their squares. Four
   arrows, each lying along a tile edge as the big screen draws it (a tile is
   twice as wide as it is tall, so they lean 26.57 degrees), with the steps
   left as pips between them. */
#lay-crown[data-mode="battle"][data-fpad] .cr-pad{display:flex;order:1;padding:0}
#lay-crown[data-fpad] .cr-acts{order:2}
#lay-crown[data-fpad] .cr-dpad{display:none}
.cr-fpad{flex:none;display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);grid-template-rows:1fr 1fr;gap:.35rem;width:min(100%,20rem);height:clamp(5.4rem,17vh,7.4rem)}
.cr-fpad[hidden]{display:none}
@media (max-height:600px){.cr-fpad{height:4.8rem;gap:.25rem}}
.cr-fpad .fp{min-height:0;min-width:0;padding:0;display:flex;align-items:center;justify-content:center}
.cr-fpad .nw{grid-area:1/1}.cr-fpad .ne{grid-area:1/3}.cr-fpad .sw{grid-area:2/1}.cr-fpad .se{grid-area:2/3}
.cr-fpad .fp.shut{opacity:.4}
.fp-arrow{display:block;width:3rem;height:1.9rem;background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11 7' shape-rendering='crispEdges'%3E%3Cpath d='M0 3h5v1H0zM5 0h1v7H5zM6 1h2v5H6zM8 2h2v3H8zM10 3h1v1h-1z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11 7' shape-rendering='crispEdges'%3E%3Cpath d='M0 3h5v1H0zM5 0h1v7H5zM6 1h2v5H6zM8 2h2v3H8zM10 3h1v1h-1z'/%3E%3C/svg%3E") center/contain no-repeat}
.cr-fpad .ne .fp-arrow{transform:rotate(-26.57deg)}
.cr-fpad .se .fp-arrow{transform:rotate(26.57deg)}
.cr-fpad .sw .fp-arrow{transform:rotate(153.43deg)}
.cr-fpad .nw .fp-arrow{transform:rotate(206.57deg)}
.fp-mid{grid-area:1/2/3/3;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.3rem;min-width:4.2rem;padding:0 .2rem}
.fp-mid small{font-size:.5rem;color:var(--ink-dim);white-space:nowrap}
/* a tile, as the screen draws one: so the arrows are read as its four edges */
.fp-tile{display:block;width:2.4rem;height:1.2rem;background:var(--knob);opacity:.85;clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%)}
.fp-pips{display:flex;gap:.22rem;justify-content:center;flex-wrap:wrap;max-width:4.2rem}
.fp-pips i{display:block;width:.5rem;height:.5rem;border:1px solid var(--line);background:transparent}
.fp-pips i.on{background:var(--knob);border-color:var(--knob)}
@keyframes fpNo{0%,100%{transform:none}25%{transform:translateX(-.25rem)}75%{transform:translateX(.25rem)}}
.cr-fpad .fp.no{animation:fpNo .18s linear;border-color:#f2645a}
/* Going into a fight and coming out, eased like the screen's wipe: the
   d-pad's box closes up or opens again (--pad-h is its height, measured by
   controller.js), and the panel and the choices rise into place.
   controller.js puts the class on for the half second it lasts. */
@keyframes crRise{from{opacity:0;transform:translateY(.8rem)}}
@keyframes crPadShut{from{max-height:var(--pad-h)}to{max-height:0;padding-top:0;padding-bottom:0;opacity:0}}
@keyframes crPadOpen{from{max-height:0;padding-top:0;padding-bottom:0;opacity:0}to{max-height:var(--pad-h)}}
@media (prefers-reduced-motion:no-preference){
  #lay-crown.to-battle .cr-info,#lay-crown.to-battle .cr-acts,#lay-crown.to-map .cr-info,#lay-crown.to-map .cr-acts{animation:crRise .48s cubic-bezier(.45,0,.25,1) both}
  #lay-crown.to-battle .cr-pad{display:flex;pointer-events:none;overflow:hidden;animation:crPadShut .48s cubic-bezier(.45,0,.25,1) both}
  #lay-crown.to-map .cr-pad{overflow:hidden;animation:crPadOpen .48s cubic-bezier(.45,0,.25,1) both}
}
/* In every phase, not only a fight:
   The panel is only as tall as what it says; the choices get the rest.
   Each choice is a square card carrying its own odds, two to a row, and
   when they do not all fit the list scrolls, with no scrollbar and a
   fade at the edge it continues past. */
#lay-crown .cr-info{flex:0 0 auto;min-height:0}
#lay-crown .cr-acts{
  /* one side length for width and height, so the cards are true squares:
     half the phone's width, within sensible bounds on anything bigger */
  --sq:clamp(7.4rem,calc((100vw - 2.95rem) / 2),10rem);
  grid-template-columns:repeat(auto-fill,var(--sq));grid-auto-rows:var(--sq);justify-content:center;
  flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;align-content:start;
  scrollbar-width:none;-ms-overflow-style:none;padding-bottom:.6rem;
  -webkit-mask-image:linear-gradient(to bottom,#000 0,#000 calc(100% - 14px),transparent 100%);
  mask-image:linear-gradient(to bottom,#000 0,#000 calc(100% - 14px),transparent 100%);
}
#lay-crown .cr-acts::-webkit-scrollbar{display:none}
#lay-crown .cr-act{height:100%;min-height:0;justify-content:flex-start;gap:.25rem;padding:.55rem .55rem;overflow:hidden}
#lay-crown .cr-act.wide{grid-column:auto}
.cr-odds{display:block;font-style:normal;font-size:.56rem;line-height:1.35;color:var(--ink-dim);overflow-wrap:anywhere}
.cr-odds:first-of-type{margin-top:.2rem}
/* A hero card on the class pick: the name, the four stats two by two, the
   best two, then the skills one under the other. Labels are bold, and the
   type is sized off the card, so all of it fits on a small phone too. */
#lay-crown .cr-act.hero{gap:0;--ht:calc(var(--sq) * .066)}
#lay-crown .cr-act.hero .cr-label{font-size:.8rem;margin-bottom:.15rem}
#lay-crown .cr-act.hero small{font-size:var(--ht);line-height:1.3}
.cr-cstats{width:100%;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.1rem .3rem;margin:.2rem 0}
.cr-cstat,.cr-cline{display:block;font-size:var(--ht);line-height:1.4;color:var(--ink);max-width:100%;overflow-wrap:anywhere}
.cr-cstat{font-size:calc(var(--ht) * 1.25);line-height:1.3;white-space:nowrap}
#lay-crown .cr-cstat b,#lay-crown .cr-cline b{font-size:inherit;line-height:inherit;font-weight:700}
/* the weapon a class sets out with: its symbol, small, then its name, on one line */
.cr-cline.cr-gear{white-space:nowrap;color:var(--ink-dim)}
.cr-cline.cr-gear .cr-ico.cr-inline{--u:1px;vertical-align:-1px;margin-right:.35rem}
/* The screen's colours for the class and for each stat, sent by the game.
   A selected card keeps its own border; on the light theme they are
   darkened to stay readable on the pale card. */
.cr-act.cr-tint:not(.sel){border-color:var(--c)}
.cr-act.cr-tint .cr-label,.cr-act.cr-tint .cr-ico,.cr-act.hero .cr-cstat b.cr-tint,.cr-act.hero .cr-cline span.cr-tint{color:var(--c)}
[data-theme="light"] .cr-act.cr-tint .cr-label,[data-theme="light"] .cr-act.cr-tint .cr-ico,[data-theme="light"] .cr-act.hero .cr-cstat b.cr-tint,[data-theme="light"] .cr-act.hero .cr-cline span.cr-tint{color:color-mix(in srgb,var(--c) 55%,#000)}
/* A stat named in a card's lines, in the info card or on the hero's sheet
   takes the game's colour for it: the name only, the numbers stay as they were. */
.cr-act small .cr-tint,.cr-odds .cr-tint,.cr-info p .cr-tint,.cr-stat em.cr-tint{color:var(--c)}
[data-theme="light"] .cr-act small .cr-tint,[data-theme="light"] .cr-odds .cr-tint,[data-theme="light"] .cr-info p .cr-tint,[data-theme="light"] .cr-stat em.cr-tint{color:color-mix(in srgb,var(--c) 55%,#000)}
/* Every card's symbol: a seven by seven pixel glyph in the top right corner,
   inside the card's own padding. --px is its pixels as box-shadows. */
.cr-act{position:relative}
.cr-ico{position:absolute;top:4px;right:4px;--u:2px;width:calc(var(--u) * 7);height:calc(var(--u) * 7);color:var(--ink-dim);pointer-events:none}
.cr-ico::before{content:"";position:absolute;left:calc(var(--u) * -1);top:0;width:var(--u);height:var(--u);box-shadow:var(--px)}
/* the same glyph set in a line of text, before a name: the skull of the fallen */
.cr-ico.cr-inline{position:relative;top:auto;right:auto;display:inline-block;vertical-align:-3px;margin-right:.45rem;color:inherit}
/* the skull: nine pixels square before a name, thirteen by fourteen on the fallen hero's card */
.cr-ico.cr-skull{--u:2px;width:calc(var(--u) * 9);height:calc(var(--u) * 9);vertical-align:middle;margin-top:-6px;margin-bottom:-6px}
.cr-ico.cr-skull.cr-big{--u:4px;position:relative;top:auto;right:auto;display:block;width:calc(var(--u) * 13);height:calc(var(--u) * 14);margin:.1rem 0 .45rem}
/* every row as tall as the tallest button needs, so all are one size and
   nothing in any of them is ever cut off */
.cr-acts{grid-auto-rows:1fr}
/* the monsters, filling the card in a fight */
.cr-foes{display:flex;flex-direction:column;gap:.35rem;padding-top:.4rem}
.cr-foe{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.15rem .5rem;align-items:center;font-size:.7rem;color:var(--ink)}
.cr-foe-n{overflow-wrap:anywhere}
.cr-foe-hp{color:var(--ink-dim);font-size:.66rem}
.cr-foe-bar{grid-column:1/-1;height:.5rem;background:rgba(0,0,0,.45);border:1px solid var(--line)}
.cr-foe-bar i{display:block;height:100%;background:#e0485a}
.cr-foe.dead{opacity:.4}
/* a monster that joined the fight from another pack: a word after its name, on the same line */
.cr-tag{font-style:normal;font-size:.56rem;margin-left:.4em;padding:0 .25em;border:1px solid currentColor;color:var(--p2,#fb7185);vertical-align:.08em;white-space:nowrap}
/* The row the selected card is aimed at: boxed in your colour, with a
   pixel arrow pointing at the name. An ally's bar is green. */
.cr-foe.tgt{outline:2px solid var(--knob);outline-offset:3px;color:var(--knob)}
.cr-foe.tgt .cr-foe-n::before{content:"";display:inline-block;width:1em;height:.72em;margin-right:.4em;vertical-align:middle;position:relative;top:-.08em;background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 7' shape-rendering='crispEdges'%3E%3Cpath d='M0 3h7v1H0zM5 1h1v5H5zM6 2h1v3H6zM7 3h2v1H7z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 7' shape-rendering='crispEdges'%3E%3Cpath d='M0 3h7v1H0zM5 1h1v5H5zM6 2h1v3H6zM7 3h2v1H7z'/%3E%3C/svg%3E") center/contain no-repeat}
.cr-foe.ally .cr-foe-bar i{background:#5fd38a}
/* A card that is a target wears the same bar, the card's width, under its
   name and health: red for a monster, green for an ally. */
.cr-act .cr-foe-bar{display:block;flex:0 0 auto;width:100%;height:.4rem}
.cr-act .cr-foe-bar.ally i{background:#5fd38a}
.cr-hero{flex:0 0 auto;display:flex;flex-direction:column;gap:.35rem;padding:.55rem .7rem;border:1px solid var(--line);background:rgba(255,255,255,.03)}
.cr-top{display:flex;align-items:baseline;justify-content:space-between;gap:.6rem;min-width:0}
.cr-name{color:var(--knob);font-size:.85rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cr-cls{font-family:var(--mono);font-size:.62rem;color:var(--ink-dim);white-space:nowrap}
.cr-hp{position:relative;height:1.15rem;background:rgba(0,0,0,.4);border:1px solid var(--line)}
/* Experience sits flush under the health bar, thin and violet, so the
   sheet grows by a few pixels only; the health bar gives most of them back. */
.cr-bars{display:flex;flex-direction:column}
.cr-bars .cr-hp{height:1.05rem;border-bottom:0}
.cr-bars .cr-hp span{line-height:1rem}
.cr-xp{height:.3rem;background:rgba(0,0,0,.55);border:1px solid var(--line);border-top:0}
.cr-xp i{display:block;height:100%;background:var(--brand-2)}
.cr-hp i{position:absolute;left:0;top:0;bottom:0}
.cr-hp span{position:relative;display:block;text-align:center;font-family:var(--mono);font-size:.72rem;line-height:1.1rem;color:#fff;text-shadow:0 1px 2px #000}
.cr-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.3rem}
.cr-stat{font-family:var(--mono);font-size:.85rem;color:var(--ink);text-align:center}
.cr-stat em{font-style:normal;font-size:.68rem;margin-right:.3rem}
/* the hero's own purse, under the stats: a coin and a flask in pixels, each with its number */
.cr-purse{display:flex;align-items:center;gap:1.1rem;min-width:0}
.cr-coin{display:inline-flex;align-items:center;font-family:var(--mono);font-size:.8rem;color:var(--ink);white-space:nowrap}
.cr-coin .cr-ico.cr-inline{vertical-align:0;margin-right:.5rem}
.cr-coin .cr-ico.cr-tint{color:var(--c)}
[data-theme="light"] .cr-coin .cr-ico.cr-tint{color:color-mix(in srgb,var(--c) 60%,#000)}
/* Tags: a row of small symbols, each in its own colour, some with a word:
   a damage type, the mark for weak to or resists. On a card they sit
   under its first line; beside a monster's name they follow it. */
.cr-tags{display:flex;flex-wrap:wrap;align-items:center;gap:.1rem .4rem;max-width:100%;font-size:.56rem;line-height:1.3;color:var(--ink-dim)}
.cr-tg{display:inline-flex;align-items:center;gap:.25rem;white-space:nowrap}
.cr-tg.cr-tint{color:var(--c)}
[data-theme="light"] .cr-tg.cr-tint{color:color-mix(in srgb,var(--c) 45%,#000)}
.cr-tg .cr-ico.cr-inline{--u:1px;vertical-align:0;margin-right:0;color:inherit}
.cr-foe-n .cr-tags{display:inline-flex;margin-left:.5em;vertical-align:middle}
/* on the hero's own sheet they are a size up: this is what is happening to you */
.cr-tags.cr-ail{font-size:.7rem;gap:.2rem .8rem}
.cr-ail .cr-tg .cr-ico.cr-inline{--u:2px}
/* The Character button, at the end of the purse row: always in reach while
   exploring, and it covers nothing. It turns into Close while its panel is open. */
#lay-crown .cr-charbtn{margin-left:auto;display:inline-flex;align-items:center;gap:.45rem;min-height:0;padding:.32rem .6rem;border:1px solid var(--knob);
  background:rgba(255,255,255,.05);color:var(--knob);font-size:.6rem;font-weight:700;line-height:1.2;white-space:nowrap;cursor:pointer;touch-action:manipulation}
#lay-crown .cr-charbtn.on{background:var(--knob);color:#0b0a0d}
.cr-charbtn .cr-ico.cr-inline{vertical-align:0;margin-right:0}
/* The panel's tabs, three across the top of the info card. */
.cr-tabs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.3rem;margin-bottom:.4rem}
#lay-crown .cr-tab{min-height:0;min-width:0;padding:.45rem .1rem;border:1px solid var(--line);background:transparent;color:var(--ink-dim);
  font-size:.5rem;font-weight:700;line-height:1.2;white-space:nowrap;cursor:pointer;touch-action:manipulation}
#lay-crown .cr-tab.on{border-color:var(--knob);color:var(--knob);background:rgba(245,158,11,.12)}
/* while it is open there is nowhere to walk: the cards get the d-pad's room */
#lay-crown[data-char] .cr-pad{display:none}
/* Dividing the loot: a card you have taken is filled in your colour's
   frame; one somebody else took is theirs to look at, in their colour. */
.cr-act.hold{box-shadow:inset 0 0 0 3px var(--c,var(--knob));background:rgba(255,255,255,.1)}
.cr-acts .cr-act.hold,.cr-acts.sent .cr-act.hold{opacity:1}
.s-str em{color:#e0675a}.s-agi em{color:#7fc35a}.s-vit em{color:#e3a048}.s-int em{color:#6ea8f0}
/* Every bar on the pad is two layers: the fill, and a ghost behind it. On a
   loss the fill drops at once and the pale ghost lingers, then drains to
   meet it; on a gain the ghost runs ahead, bright, and the fill catches
   up. controller.js starts each from where the last one stood. */
.cr-m{position:relative}
.cr-m>u{position:absolute;left:0;top:0;bottom:0;text-decoration:none;background:#f4ecd8;opacity:.85;transition:width .38s ease-in .15s}
.cr-m>i{transition:width .16s ease-out}
.cr-xp.cr-m>i,.cr-foe-bar.cr-m>i{position:relative}
.cr-m.up>u{background:#fff;opacity:.6;transition:width .16s ease-out}
.cr-m.up>i{transition:width .38s ease-out .12s}
.cr-m.wrap>i,.cr-m.wrap>u{transition:width .26s ease-in}
.cr-m.snap>i,.cr-m.snap>u{transition:none}
@media (prefers-reduced-motion:reduce){.cr-m>i,.cr-m>u{transition:none !important}}
.cr-info{flex:0 0 auto;display:flex;flex-direction:column;gap:.15rem;padding:.55rem .75rem;border:1px solid var(--line);min-height:5.2rem}
.cr-info b{font-size:.8rem;line-height:1.3;color:var(--ink);font-weight:700}
.cr-info p{margin:0;font-size:.68rem;line-height:1.45;color:var(--ink-dim)}
.cr-info.picked{border-color:var(--knob)}
.cr-info .cr-confirm{margin-top:.15rem;color:var(--knob);font-weight:700}
.cr-acts{flex:1 1 auto;min-height:4.1rem;overflow-y:auto;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.5rem;align-content:start;padding:.1rem}
.cr-act{min-height:3.1rem;height:100%;display:flex;flex-direction:column;justify-content:center;gap:.2rem;padding:.4rem .5rem;text-align:left;
  border:1px solid var(--line);background:rgba(255,255,255,.05);color:var(--ink);cursor:pointer;touch-action:manipulation}
.cr-act.wide{grid-column:1/-1}
/* Short words, small type, and free to wrap: a button's text is never
   clipped, the row just grows (and its neighbours with it). */
.cr-label{font-size:.74rem;font-weight:700;line-height:1.2;max-width:100%;overflow-wrap:anywhere}
.cr-act small{font-family:var(--mono);font-size:.6rem;color:var(--ink-dim);line-height:1.3;max-width:100%;overflow-wrap:anywhere}
.cr-act.sel{border-color:var(--knob);box-shadow:inset 0 0 0 2px var(--knob);background:rgba(245,158,11,.12)}
/* A card is tapped on release, so a finger can scroll the list from on top
   of one: it lights while it is held, and goes out if the touch turns into
   a scroll. Both ways of scrolling stay with the browser. */
.cr-acts,.cr-act{touch-action:pan-x pan-y}
.cr-act.press{filter:brightness(1.35)}
[data-theme="light"] .cr-act.press{filter:brightness(.92)}
.cr-acts.sent .cr-act:not(.sel){opacity:.35}
.cr-pad{flex:0 0 auto;display:flex;justify-content:center;padding:.15rem 0 .35rem}
/* sized by the height too, so a short phone keeps room for the buttons */
.cr-dpad{flex:none;width:min(58vw,15.5rem,30vh);aspect-ratio:1;gap:.45rem}
.cr-dpad .dp{font-size:1.5rem;min-height:0}
/* Off (a fight, someone else's turn) it stays where it is but shrinks,
   handing the height to the choices and their odds. */
.cr-dpad{transition:width .2s ease}
.cr-dpad.off{opacity:.22;pointer-events:none;width:min(36vw,8.5rem,18vh)}
[data-theme="light"] .cr-act{background:rgba(43,37,54,.05)}
[data-theme="light"] .cr-hero{background:rgba(43,37,54,.04)}
/* a short upright phone in a fight: the sheet keeps the name and the health bar, and the room the
   stats and the purse took goes to the cards, which carry their own odds. Two of four fitted before. */
@media (orientation:portrait) and (max-height:700px){
  #lay-crown[data-mode="battle"] .cr-stats,#lay-crown[data-mode="battle"] .cr-purse{display:none}
  #lay-crown[data-mode="battle"] .cr-info{min-height:0}
}
/* a phone on its side: sheet, card and choices on the left, the d-pad on the right */
@media (orientation:landscape) and (max-height:560px){
  #lay-crown{display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-rows:auto auto minmax(0,1fr);gap:.45rem .8rem}
  #lay-crown[hidden]{display:none}
  .cr-hero,.cr-info,.cr-acts{grid-column:1}
  .cr-info{min-height:0}
  .cr-pad{grid-column:2;grid-row:1/4;align-items:center}
  #lay-crown[data-mode="battle"]{grid-template-columns:minmax(0,1fr)}
  #lay-crown[data-mode="battle"][data-fpad]{grid-template-columns:minmax(0,1fr) auto}
  .cr-fpad{width:min(42vw,16rem)}
  .cr-dpad{width:min(62vh,13rem)}
  /* switched off it shrinks by height; 18vh of a phone on its side is too
     narrow for three arrows, and the right one hung over the edge */
  .cr-dpad.off{width:min(40vh,8.5rem)}
}
.vote-opt{
  flex:1;min-height:64px;border:1px solid var(--line);border-radius:0;
  background:linear-gradient(160deg,rgba(255,255,255,.055),rgba(255,255,255,.02));
  color:var(--ink);font-family:inherit;font-weight:700;font-size:1.05rem;cursor:pointer;
  touch-action:none;transition:transform .09s,filter .12s,border-color .12s;
  display:flex;align-items:center;justify-content:center;text-align:center;padding:.5rem .8rem;
}
.vote-opt:active,.vote-opt.hot{transform:scale(.98);filter:brightness(1.3)}
.vote-opt.picked{border-color:currentColor;box-shadow:none}
.vote-list.done .vote-opt:not(.picked){opacity:.35}

.hold-btn{
  flex:1;border-radius:0;border:2px solid var(--brand);
  background:radial-gradient(circle at 40% 32%,rgba(245,158,11,.5),rgba(245,158,11,.14));
  color:var(--ink);font-family:inherit;font-weight:700;font-size:2rem;letter-spacing:.2em;
  cursor:pointer;touch-action:none;transition:transform .08s,filter .08s;
  box-shadow:none;
}
.hold-btn.down{transform:scale(.97);filter:brightness(1.5);border-color:var(--p3)}

/* ── babel pad (Topple Town): one DROP button the size of the pad; three scrambled words while confused ── */
.bb-pad{flex:1;display:flex;flex-direction:column;gap:.5rem;min-height:0;touch-action:none}
.bb-top{display:flex;justify-content:space-between;align-items:center;font-family:var(--mono);color:var(--ink-dim);font-size:.95rem;letter-spacing:.1em}
.bb-floors{color:var(--ink);font-size:1.5rem;font-weight:700}
.bb-floors::after{content:' floors';font-size:.8rem;font-weight:400;color:var(--ink-dim)}
.bb-combo{color:var(--warn)}
.bb-lives{display:flex;gap:.3rem}
.bb-lives i{display:block;width:.9rem;height:.9rem;border:1px solid var(--line);background:transparent}
.bb-lives i.on{background:var(--p2);border-color:var(--p2)}
.bb-btns{flex:1;min-height:0;display:flex;flex-direction:column;gap:.5rem}
.bb-btn{flex:1;min-height:3.2rem;display:none;align-items:center;justify-content:center;border:2px solid var(--knob);
  background:var(--panel-solid);color:var(--ink);font-family:var(--mono);touch-action:none;user-select:none;-webkit-user-select:none}
.bb-btn *{pointer-events:none}
.bb-btn b{font-size:2.4rem;font-weight:700;letter-spacing:.2em}
.bb-btn.bb-main{display:flex}
.bb-btn.hot{transform:scale(.98);filter:brightness(1.5)}
.bb-pad.conf .bb-btn{display:flex;border-color:var(--p3)}
.bb-pad.conf .bb-btn b{font-size:1.9rem}
.bb-pad.out .bb-btn{border-color:var(--line);color:var(--ink-faint)}
@media (orientation:landscape) and (max-height:560px){
  .bb-btns{flex-direction:row}
}

/* ── hunt pad (Fowl Play): a trackpad on top, RELOAD and FIRE under it ── */
.hunt-pad{flex:1;display:flex;flex-direction:column;gap:.6rem;min-height:0;touch-action:none}
.hunt-top{display:flex;justify-content:space-between;align-items:center;gap:.6rem;font-family:var(--mono);color:var(--ink-dim)}
.hunt-ammo{display:flex;gap:.3rem}
.hunt-ammo i{display:block;width:.7rem;height:1.3rem;border:1px solid var(--line);background:transparent}
.hunt-ammo i.on{background:var(--brand);border-color:var(--brand)}
.hunt-pad.loading .hunt-ammo i{border-style:dashed}
.hunt-score{flex:1;text-align:center;font-size:1.3rem;font-weight:700;letter-spacing:.1em;color:var(--ink)}
.hunt-mode{font:inherit;font-size:.8rem;letter-spacing:.1em;padding:.45rem .7rem;border:1px solid var(--line);background:var(--panel-solid);color:var(--ink-dim);min-height:2.4rem}
.hunt-mode.armed{border-color:var(--ok);color:var(--ink)}
.hunt-track{flex:1.7;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.4rem;
  border:2px solid var(--knob,var(--line));background:linear-gradient(160deg,rgba(255,255,255,.06),rgba(255,255,255,.02));touch-action:none}
.hunt-track *{pointer-events:none}
.hunt-track.down{filter:brightness(1.3)}
.hunt-word{font-size:1.5rem;font-weight:700;letter-spacing:.14em;color:var(--ink-dim)}
.hunt-tip{color:var(--ink-faint);font-size:.8rem;letter-spacing:.12em;text-transform:uppercase}
.hunt-pad.empty .hunt-word{color:var(--warn)}
.hunt-btns{flex:1;min-height:5rem;display:flex;gap:.6rem}
.hunt-btn{font:inherit;font-weight:700;letter-spacing:.16em;border:2px solid var(--line);background:var(--panel-solid);color:var(--ink);touch-action:none}
.hunt-btn:active,.hunt-btn.down{filter:brightness(1.4)}
.hunt-reload,.hunt-centre{flex:1;font-size:.95rem}
.hunt-fire{flex:2;font-size:1.7rem;border-color:var(--p2);background:linear-gradient(160deg,rgba(251,113,133,.3),rgba(245,158,11,.14))}
.hunt-pad.empty .hunt-reload{border-color:var(--warn);animation:duel-ask 1s steps(2) infinite}
@media (orientation:landscape) and (max-height:520px){
  .hunt-pad{flex-direction:row;flex-wrap:wrap}
  .hunt-top{flex:0 0 100%}
  .hunt-track{flex:1.6}
  .hunt-btns{flex:1;flex-direction:column-reverse;min-height:0}
  .hunt-tip{display:none}
}
@media (prefers-reduced-motion:reduce){.hunt-pad.empty .hunt-reload{animation:none}}

/* ── shoot pad (Gridfire): the move stick over JUMP on the left, the aim panel over RELOAD and FIRE on the right ── */
.sht-dir{flex-direction:column;justify-content:flex-end;align-items:stretch;gap:.6rem}
.sht-top{display:flex;align-items:center;gap:.5rem;font-family:var(--mono);font-size:.8rem;color:var(--ink-dim)}
.sht-hp{flex:1;height:.5rem;border:1px solid var(--line);background:transparent}
.sht-hp i{display:block;height:100%;width:100%;background:var(--ok)}
.sht-hp i.low{background:var(--p2)}
.sht-ammo{color:var(--ink);font-size:1rem}
.sht-stick{flex:0 0 auto;width:100%;max-width:220px;aspect-ratio:1;align-self:center}
.sht-act{gap:.6rem}
.sht-aim{flex:1;min-height:5rem;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.3rem;
  border:2px solid var(--knob,var(--line));background:linear-gradient(160deg,rgba(255,255,255,.06),rgba(255,255,255,.02));touch-action:none}
.sht-aim *{pointer-events:none}
.sht-aim.down{filter:brightness(1.3)}
.sht-word{font-size:1.1rem;font-weight:700;letter-spacing:.14em;color:var(--ink-dim)}
.sht-tip{color:var(--ink-faint);font-size:.75rem;letter-spacing:.12em;text-transform:uppercase}
.sht-btns{display:grid;grid-template-columns:1fr 1fr 1.3fr;grid-template-rows:auto auto;gap:.5rem;align-items:stretch}
.sht-btns .action-btn{min-width:0;margin:0}
.sht-reload{grid-column:1;grid-row:1;height:46px;font-size:.85rem;font-family:var(--mono)}
.sht-knife{grid-column:2;grid-row:1;height:46px;font-size:.85rem;font-family:var(--mono);border-color:var(--p2,#fb7185)}
.sht-knife.down{background:rgba(251,113,133,.28)}
/* JUMP, and beside it the flashlight's switch (lit while the light is on) */
.sht-low{display:flex;gap:.5rem;align-items:stretch}
.sht-low .action-btn{min-width:0;margin:0}
.sht-low .sht-jump{flex:1.5 1 0}
.sht-light{flex:1 1 0;font-size:.85rem;font-family:var(--mono);letter-spacing:.08em;border-color:var(--warn)}
.sht-light.down,#lay-shoot.lit .sht-light{background:rgba(250,204,21,.24);box-shadow:0 0 0 2px var(--warn) inset}
.sht-ads{grid-column:2;grid-row:2;height:46px;font-size:.85rem;font-family:var(--mono);border-color:var(--p3,#a78bfa)}
.sht-ads.down{background:rgba(167,139,250,.28);border-color:#c4b5fd}
.sht-duck{grid-column:1;grid-row:2;height:46px;font-size:.85rem;font-family:var(--mono);border-color:var(--p1,#48c8dd)}
.sht-duck.down,#lay-shoot.ducked .sht-duck{background:rgba(72,200,221,.28);border-color:#8fe3f0}
#lay-shoot.sprint .stick-base{box-shadow:0 0 0 2px var(--warn) inset}
.sht-fire{grid-column:3;grid-row:1 / 3;align-self:center;justify-self:center;width:100%;max-width:120px;border-color:var(--p2);background:linear-gradient(160deg,rgba(251,113,133,.3),rgba(245,158,11,.14));font-size:1.3rem}
#lay-shoot.empty .sht-reload{border-color:var(--warn);animation:duel-ask 1s steps(2) infinite}
#lay-shoot.empty .sht-ammo{color:var(--warn)}
#lay-shoot.dead .sht-word{color:var(--p2)}
@media (orientation:landscape) and (max-height:560px){
  .sht-stick{max-width:170px}
  .sht-jump,.sht-light{height:52px}
  .sht-aim{min-height:0}
  .sht-reload{height:44px}
  .sht-knife{height:44px}
  .sht-ads{height:44px}
  .sht-duck{height:44px}
}
@media (prefers-reduced-motion:reduce){#lay-shoot.empty .sht-reload{animation:none}}
/* the sights are on: AIM stays lit and the look panel says so */
#lay-shoot.ads .sht-aim{border-color:#c4b5fd;background:linear-gradient(160deg,rgba(167,139,250,.16),rgba(167,139,250,.05))}
#lay-shoot.ads .sht-word{color:#c4b5fd}
#lay-shoot.ads .sht-ads{box-shadow:0 0 0 2px #c4b5fd inset}
/* Gridfire has no small view between the thumbs: the look panel takes that room */
@media (orientation:landscape) and (max-height:560px){
  #lay-shoot .gp-mid{display:none}
  #lay-shoot .gp-dir{flex:0 0 32%}
  #lay-shoot .gp-act{flex:1 1 auto}
  /* the stick no taller than leaves room for the numbers above it and JUMP under it (it used to push the numbers up into the top bar) */
  #lay-shoot .sht-stick{max-width:min(190px,calc(100vh - 200px))}
}
.pad-rot #lay-shoot .gp-mid{display:none}
.pad-rot #lay-shoot .gp-dir{flex:0 0 32%}
.pad-rot #lay-shoot .gp-act{flex:1 1 auto}
.pad-rot #lay-shoot .sht-stick{max-width:min(190px,calc(100vw - 200px))}
.pad-rot .sht-jump,.pad-rot .sht-light{height:52px}
.pad-rot .sht-aim{min-height:0}
.pad-rot .sht-reload,.pad-rot .sht-knife,.pad-rot .sht-ads,.pad-rot .sht-duck{height:44px}

/* ── Gridfire: your controls (gridpad.js). Over the left of the pad; the look panel on the right stays free ── */
#btn-pad-opts{flex:0 0 auto;min-width:44px;min-height:44px;padding:0 .6rem;font-size:1.25rem;line-height:1}
#btn-pad-opts.on{border-color:var(--brand);color:var(--brand)}
#pad-live{--gfo-w:min(54%,27rem)}
.gfo{position:fixed;z-index:8;left:0;top:0;bottom:0;width:var(--gfo-w);display:flex;flex-direction:column;gap:.4rem;
  background:var(--panel-solid);border-right:2px solid var(--knob,var(--line));touch-action:none;
  padding:calc(env(safe-area-inset-top,0px) + .45rem) .6rem calc(env(safe-area-inset-bottom,0px) + .45rem) max(.6rem,env(safe-area-inset-left,0px))}
/* on a pad drawn turned, the pad's left end is the top of the phone (the notch) and its long sides are the phone's sides */
.pad-rot .gfo{padding:.45rem .6rem .45rem calc(env(safe-area-inset-top,0px) + .6rem)}
.gfo[hidden]{display:none}
.gfo-bar{display:flex;gap:.35rem;align-items:stretch;flex:0 0 auto}
.gfo-gap{flex:1}
.gfo-tab{font:inherit;font-family:var(--mono);font-size:.78rem;letter-spacing:.1em;min-height:44px;min-width:52px;padding:0 .6rem;
  border:1px solid var(--line);background:transparent;color:var(--ink-dim);touch-action:none}
.gfo-tab.on{border-color:var(--knob,var(--brand));color:var(--ink);background:rgba(255,255,255,.07)}
.gfo-tab:disabled{opacity:.35}
.gfo-done{border-color:var(--brand);color:var(--ink)}
.gfo-page{flex:1 1 auto;min-height:0;display:grid;grid-template-columns:1fr 1fr;grid-auto-rows:minmax(44px,auto);gap:.35rem;align-content:start}
.gfo-page[hidden]{display:none}
.gfo-row{grid-column:1 / -1;display:flex;align-items:stretch;gap:.35rem;min-width:0}
.gfo-row[hidden]{display:none}
.gfo-sw-row{grid-column:auto}
#gfo-look .gfo-sw-row{grid-column:1 / -1}
.gfo-lab{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center;gap:.1rem;padding:0 .2rem}
.gfo-lab b{font-size:.82rem;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gfo-lab i{font-style:normal;font-family:var(--mono);font-size:.74rem;color:var(--brand-2,var(--ink-dim));white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gfo-step{font:inherit;font-size:1.4rem;line-height:1;flex:0 0 56px;min-height:44px;border:1px solid var(--line);background:rgba(255,255,255,.05);color:var(--ink);touch-action:none}
.gfo-step:active{filter:brightness(1.5)}
.gfo-step:disabled{opacity:.3}
.gfo-sw{font:inherit;font-size:.78rem;flex:1;min-width:0;min-height:44px;padding:.2rem .45rem;text-align:left;border:1px solid var(--line);background:transparent;color:var(--ink-dim);touch-action:none}
.gfo-sw.on{border-color:var(--ok);color:var(--ink);background:rgba(78,232,165,.1)}
.gfo-meter-row{align-items:center;gap:.5rem}
.gfo-meter{position:relative;flex:1;height:1.1rem;border:1px solid var(--line);background:rgba(0,0,0,.3);overflow:hidden}
.gfo-meter i{position:absolute;left:0;top:0;bottom:0;width:0;background:var(--ink-dim)}
.gfo-meter.hit i{background:var(--p2)}
.gfo-meter u{position:absolute;top:0;bottom:0;width:2px;margin-left:-1px;text-decoration:none}
.gfo-meter u.rest{background:var(--ok)}
.gfo-meter u.on{background:var(--warn)}
.gfo-meter-tip{flex:0 0 auto;font-size:.68rem;color:var(--ink-faint);letter-spacing:.06em}
/* while it is open the pad is the look panel and its buttons, moved over beside it: what is set is tried at once */
#pad-live.gfo-open #lay-shoot .sht-dir{display:none}
#pad-live.gfo-open .pad-title,#pad-live.gfo-open .pad-you{visibility:hidden}
#pad-live.gfo-open #lay-shoot{margin-left:calc(var(--gfo-w) + .5rem)}

/* "turn your phone": upright words over a pad that is drawn turned (it is the phone that has to turn, not the reader's head) */
.land-hint{position:fixed;z-index:9;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;padding:.45rem .9rem;pointer-events:none;
  font-family:var(--mono);font-size:.82rem;letter-spacing:.06em;white-space:nowrap;color:var(--ink);background:rgba(8,8,12,.86);border:1px solid var(--brand);
  animation:land-hint 6s linear forwards}
.land-hint[hidden]{display:none}
.land-hint b{display:inline-block;margin-right:.35rem;color:var(--brand);font-size:1.1rem}
@keyframes land-hint{0%,80%{opacity:1}100%{opacity:0}}
@media (prefers-reduced-motion:reduce){.land-hint{animation:none}}
/* the room menu's sliders (a game's own settings: screen.js renderGameSettings) */
.rs-range{display:flex;flex-direction:column;gap:.25rem;font-size:.85rem;color:var(--ink)}
.rs-range input{width:100%;min-height:28px;accent-color:var(--brand)}
/* a picture among a game's settings (Gridfire: the chosen map from above) */
.rs-picture{display:block;width:100%;max-width:320px;height:auto;margin:.2rem auto;border:1px solid var(--line);image-rendering:pixelated}

/* ── duel pad (Sundown Draw): the holster at the bottom, the rest is for aiming ── */
.duel-pad{flex:1;display:flex;flex-direction:column;gap:.6rem;min-height:0;touch-action:none;position:relative}
.duel-pad *{pointer-events:none}
.duel-top{display:flex;justify-content:space-between;align-items:center;font-family:var(--mono);color:var(--ink-dim);font-size:.95rem;letter-spacing:.1em}
.duel-hp::before{content:'\2665  ';color:var(--knob)}
.duel-ammo{display:flex;gap:.3rem}
.duel-ammo i{display:block;width:.55rem;height:1rem;border:1px solid var(--line);background:transparent}
.duel-ammo i.on{background:var(--brand);border-color:var(--brand)}
.duel-aim{flex:1.6;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;
  border:1px dashed var(--line);background:linear-gradient(160deg,rgba(167,139,250,.06),rgba(245,158,11,.06));text-align:center;padding:.6rem}
.duel-word{font-size:2.4rem;font-weight:700;letter-spacing:.14em;color:var(--ink-dim)}
.duel-tip{color:var(--ink-faint);font-size:.82rem;letter-spacing:.12em;text-transform:uppercase}
.duel-holster{flex:1;min-height:5.5rem;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.3rem;
  border:2px solid var(--line);background:rgba(255,255,255,.03);color:var(--ink-faint);transition:filter .08s,border-color .12s}
.duel-holster b{font-size:1.5rem;letter-spacing:.2em}
.duel-holster span{font-size:.75rem;letter-spacing:.14em;text-transform:uppercase}
.duel-pad[data-stage="holster"] .duel-holster{border-color:var(--brand);color:var(--ink);
  background:radial-gradient(circle at 50% 40%,rgba(245,158,11,.42),rgba(245,158,11,.12));animation:duel-ask 1s steps(2) infinite}
@keyframes duel-ask{50%{border-color:var(--line)}}
.duel-pad[data-stage="held"] .duel-holster,.duel-pad[data-stage="steady"] .duel-holster{border-color:var(--ok);color:var(--ink);
  background:radial-gradient(circle at 50% 40%,rgba(78,232,165,.3),rgba(78,232,165,.08))}
.duel-holster.down{filter:brightness(1.35)}
.duel-pad[data-stage="steady"] .duel-word{color:var(--ink)}
.duel-pad[data-stage="fire"] .duel-aim{border:2px solid var(--p2);background:linear-gradient(160deg,rgba(251,113,133,.22),rgba(245,158,11,.1))}
.duel-pad[data-stage="fire"] .duel-word{color:var(--p2);font-size:3.2rem}
/* once the gun is out the holster is only more room to aim in */
.duel-pad.foul .duel-word{color:var(--warn);font-size:2rem}
.duel-pad.foul .duel-tip{color:var(--ink);font-size:.95rem;text-align:center;padding:0 .6rem}
.duel-pad.nogun .duel-word{color:var(--warn)}
.duel-pad.nogun .duel-aim{border-style:dashed}
.rs-game-opts{display:flex;flex-direction:column;gap:.5rem}
.duel-pad.drawn .duel-holster{border-style:dashed;border-color:var(--line);background:transparent;color:transparent}
.duel-pad.aiming .duel-aim{filter:brightness(1.25)}
@media (orientation:landscape) and (max-height:560px){
  .duel-word{font-size:1.5rem}
  .duel-pad[data-stage="fire"] .duel-word{font-size:2rem}
  .duel-tip{display:none}
  .duel-holster{min-height:3.6rem}
  .duel-holster span{display:none}
}
@media (prefers-reduced-motion:reduce){.duel-pad[data-stage="holster"] .duel-holster{animation:none}}

.shake-pad{
  flex:1;border-radius:0;border:1px solid var(--line-hot);
  background:linear-gradient(160deg,rgba(227,154,72,.22),rgba(245,158,11,.12));
  color:var(--ink);font-family:inherit;cursor:pointer;touch-action:none;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;
  transition:transform .08s,filter .08s;
}
.shake-pad:active,.shake-pad.hot{transform:scale(.98);filter:brightness(1.35)}
.shake-glyph{font-size:2.6rem;line-height:1;animation:wiggle 1.1s ease-in-out infinite}
@keyframes wiggle{0%,100%{transform:rotate(-12deg)}50%{transform:rotate(12deg)}}
.shake-word{font-weight:700;font-size:1.7rem;letter-spacing:.22em}
.shake-sub{font-size:.76rem;color:var(--ink-faint);max-width:80%;text-align:center;line-height:1.4}
/* ── drive pad: steering strip + pedals ── */
.drive-top{display:flex;gap:.5rem;flex:0 0 auto}
.drive-top .ghost-btn{flex:1;font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;padding:.55rem .5rem}
.drive-top .ghost-btn.armed{border-color:var(--brand);color:var(--ink);background:rgba(245,158,11,.2)}
.drive-top .ghost-btn[disabled]{opacity:.4}
.wheel{
  position:relative;flex:1;border:1px solid var(--line);border-radius:0;overflow:hidden;
  background:linear-gradient(160deg,rgba(167,139,250,.08),rgba(245,158,11,.08));touch-action:none;
}
.wheel::before{
  content:"";position:absolute;left:50%;top:8%;bottom:8%;width:1px;
  background:rgba(140,160,255,.30);
}
.wheel-fill{position:absolute;top:0;bottom:0;background:var(--knob,var(--p1));opacity:.16;pointer-events:none}
.wheel-knob{
  position:absolute;top:10%;height:80%;width:78px;border-radius:0;
  background:var(--knob,var(--p1));box-shadow:none;
}
.wheel.tilting{opacity:.55}
.wheel.tilting .wheel-tip{color:var(--brand-2)}
.wheel-tip{
  position:absolute;inset:auto 0 10px;text-align:center;color:var(--ink-faint);
  font-size:.74rem;letter-spacing:.16em;text-transform:uppercase;pointer-events:none;
}
.drive-row{display:grid;grid-template-columns:1fr 1.7fr;gap:.6rem;flex:0 0 auto}
.action-btn.gas{
  border-color:rgba(52,211,153,.5);
  background:linear-gradient(160deg,rgba(52,211,153,.32),rgba(52,211,153,.10));
}
.action-btn.brake{
  border-color:rgba(224,91,122,.5);
  background:linear-gradient(160deg,rgba(224,91,122,.30),rgba(224,91,122,.10));
}

/* ══════════════ seats ══════════════ */
/* Every seat is one line: dot, name, and ✕. A keyboard player's name is
   also the rename control; its keys are changed from the ☰ menu. */
.seat-name-btn{cursor:pointer}
.seat-name-btn:hover,.seat-name-btn:focus-visible{text-decoration:underline dotted;outline:none}
.seat-tools{display:flex;gap:.4rem;order:4;margin-left:auto;flex:0 0 auto}
.seat .mini-btn.danger{padding:.15rem .4rem;line-height:1}
/* the inline rename box matches the name's height, so the seat stays put */
.roster .seat-rename{height:1.3rem;padding:0 .3rem;font-size:.78rem;width:5.8rem;min-width:0;flex:0 1 5.8rem}
.seat-rename{
  font-family:var(--mono);font-weight:700;font-size:.9rem;color:var(--ink);
  background:rgba(255,255,255,.06);border:1px solid var(--brand);border-radius:0;
  padding:.1rem .35rem;width:6.2rem;text-transform:uppercase;letter-spacing:.03em;
}
.seat-rename:focus{outline:none;border-color:var(--line-hot);background:rgba(245,158,11,.16)}
[data-theme="light"] .seat-rename{background:rgba(43,37,54,.06)}
.kcap{
  display:inline-block;font-family:var(--mono);font-size:.7rem;line-height:1;
  padding:.28rem .45rem;border:1px solid var(--line);border-bottom-width:2px;
  border-radius:0;background:rgba(255,255,255,.05);color:var(--ink-dim);min-width:1.7rem;text-align:center;
}
.kcap.dim{opacity:.5;border-style:dashed}
.kcap.edited{border-color:var(--brand);color:var(--brand-2)}
.kcap.big{font-size:.9rem;padding:.5rem .8rem;cursor:pointer;color:var(--ink);transition:.15s}
.kcap.big:hover{border-color:var(--line-hot)}
.kcap.big.capturing{border-color:var(--brand);background:rgba(245,158,11,.2);animation:glowPulse 1.2s ease-in-out infinite}
.mini-btn{
  border:1px solid var(--line);background:rgba(255,255,255,.03);color:var(--ink-faint);
  border-radius:0;padding:.22rem .6rem;font-size:.72rem;cursor:pointer;transition:.18s;
}
.mini-btn:hover:not([disabled]){border-color:var(--line-hot);color:var(--ink)}
.mini-btn.danger:hover:not([disabled]){border-color:var(--p2);color:var(--p2)}
.mini-btn[disabled]{opacity:.35;cursor:not-allowed}

.kb-legend{
  /* right-hand side: the toast rail owns bottom-left */
  position:absolute;right:1rem;bottom:1rem;z-index:3;display:grid;gap:.35rem;
  justify-items:end;pointer-events:none;opacity:.42;transition:opacity .25s;
}
.stage-wrap:hover .kb-legend{opacity:.95}
.kl-row{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap;font-size:.72rem}
.kl-name{font-weight:700;font-size:.75rem;margin-right:.2rem}
.kl-act{color:var(--ink-faint);margin-right:.5rem}
.kl-sel{font-family:var(--mono);color:var(--brand-2);margin-left:.3rem}

.rebind{position:fixed;inset:0;z-index:40;display:grid;place-items:center}
.rebind-backdrop{position:absolute;inset:0;background:rgba(5,7,20,.8);backdrop-filter:blur(6px)}
.rebind-card{
  position:relative;width:min(560px,92vw);max-height:86vh;overflow:auto;
  background:var(--panel-solid);border:1px solid var(--line-hot);border-radius:0;
  padding:1.4rem;box-shadow:var(--shadow);
}
.rb-head{display:flex;align-items:center;justify-content:space-between;gap:1rem}
.rb-head h2{margin:0;font-size:1.25rem}
.rb-sub{margin:.4rem 0 1rem;color:var(--ink-faint);font-size:.85rem}
.seg-tabs{display:flex;gap:.4rem;flex-wrap:wrap;margin-bottom:1rem}
.seg-tab{
  border:1px solid var(--line);background:none;color:var(--ink-faint);border-radius:0;
  padding:.3rem .7rem;font-size:.76rem;cursor:pointer;transition:.18s;
}
.seg-tab:hover{color:var(--ink)}
.seg-tab.on{background:linear-gradient(96deg,var(--brand),var(--brand-2));color:#0b0a0d;font-weight:700;border-color:transparent}
.rb-list{display:grid;gap:.5rem}
.rb-row{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  border:1px solid var(--line);border-radius:0;padding:.5rem .8rem}
.rb-label{font-size:.9rem}
.rb-none{color:var(--ink-faint);font-size:.88rem;margin:.4rem 0}
.rb-msg{min-height:1.3rem;margin:.8rem 0 0;font-size:.82rem;color:var(--ok)}
.rb-msg.err{color:var(--p2)}
.rb-foot{margin-top:.6rem;display:flex;justify-content:flex-end}

/* ══════════════ phone mini view ══════════════
   A glanceable thumbnail of the big screen. It takes a FIXED slice of
   the phone and lets the pad flex into what is left — the controls are
   what you actually play with, so they never give up space to it. */
.mini-wrap{
  flex:0 0 auto;position:relative;
  height:min(24vh,170px);
  border:1px solid var(--line);border-radius:0;
  background:radial-gradient(ellipse at 50% 40%,rgba(61,53,71,.50),rgba(11,10,13,.75));
  overflow:hidden;
}
/* SnapView writes explicit px width/height onto the canvas, measured from
   this box. As a normal flow child the canvas then DEFINES the box height,
   so each resize ratchets it larger — in landscape it reached 447px inside a
   390px viewport and the board was clipped top and bottom. Taking the canvas
   out of flow breaks the feedback loop; the box keeps its own height. */
.mini-wrap canvas{position:absolute;inset:0;display:block;width:100%;height:100%}
.mini-idle{
  position:absolute;inset:0;margin:0;display:grid;place-items:center;
  color:var(--ink-faint);font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;
  pointer-events:none;
}
.mini-wrap.live .mini-idle{display:none}
.sheet-fine{margin:.5rem 0 0;color:var(--ink-faint);font-size:.76rem;line-height:1.45}

/* Short phones and landscape: shrink the view, never the pad. */
@media (max-height:620px){ .mini-wrap{height:min(20vh,120px)} }
@media (max-height:460px){ .mini-wrap{height:88px} }

/* ── climb pad: one steer axis, thumb or tilt ──
   The strip inherits .wheel's flex:1 so it fills the pad: a full-height
   target is easier to thumb, and a short one put the knob on top of the
   hint text. */
.climb-strip{flex:none}
.climb-note{margin:.35rem 0 0;text-align:center;color:var(--ink-faint);font-size:.8rem;flex:0 0 auto}
/* pushed to the bottom: this pad is held one-handed and thumbed */


/* ══════════════ cards pad (Pesten) ══════════════ */
#lay-cards{display:flex;flex-direction:column;gap:.6rem;position:relative}
.cards-top{display:flex;align-items:center;gap:.6rem;flex:0 0 auto}
.cards-state{flex:1;font-family:var(--mono);font-size:.8rem;color:var(--ink-dim);letter-spacing:.04em}
.cards-rules-btn{padding:.3rem .7rem;font-size:.78rem}
/* the top of the pile and the called suit, spelled out on the phone */
.cards-pile{display:block;margin-top:.2rem;font-size:.95rem;color:var(--ink-dim);letter-spacing:.02em}
.cards-pile b{font-family:system-ui,sans-serif;font-size:1.35rem;font-weight:700;color:var(--ink);vertical-align:-.08em;letter-spacing:0}
/* a plain system face: the pixel one muddles 4 and 5 at this size and outlines its heart */
.cards-pile b.suit{font-variant-emoji:text}
.cards-hand{
  /* Capped and pushed down: on a tall phone an uncapped flex:1 made the hand
     a small row marooned at the top of a huge empty box. Your cards belong
     near your thumb, just above the actions. */
  flex:0 0 auto;min-height:120px;margin-top:auto;
  position:relative;border:1px solid var(--line);border-radius:0;
  background:linear-gradient(160deg,rgba(167,139,250,.06),rgba(245,158,11,.08));overflow:hidden;touch-action:none;
}
.cards-hand canvas{display:block;width:100%;height:100%}
/* Rummikub tiles pad: the board (table sets + your rack) takes all the room
   between the header and the action row, since manipulating IS the play. */
#lay-tiles{display:flex;flex-direction:column;gap:.6rem}
.tiles-board{
  flex:1 1 auto;min-height:200px;position:relative;
  border:1px solid var(--line);border-radius:0;overflow:hidden;touch-action:none;
  background:linear-gradient(160deg,rgba(167,139,250,.05),rgba(245,158,11,.06));
}
.tiles-board canvas{display:block;width:100%;height:100%}
.tiles-actions .action-btn{flex:1;height:60px}
.tiles-actions .action-btn.ready{border-color:var(--lime,#a3e635);background:linear-gradient(160deg,rgba(163,230,53,.30),rgba(163,230,53,.10))}
.tiles-actions .action-btn:disabled{opacity:.4}
.cards-actions{display:flex;gap:.6rem;flex:0 0 auto}
.cards-actions .action-btn{flex:1;height:64px}
.action-btn.claim{border-color:#fbbf24;background:linear-gradient(160deg,rgba(251,191,36,.28),rgba(251,191,36,.10))}

.suit-pick,.rules-panel{
  position:absolute;inset:auto .5rem .5rem;z-index:6;background:rgba(12,16,38,.97);
  border:1px solid var(--line-hot);border-radius:0;padding:.9rem;backdrop-filter:blur(10px);
}
.suit-pick p{margin:0 0 .6rem;text-align:center;color:var(--ink-dim);font-size:.85rem}
.suit-row{display:grid;grid-template-columns:repeat(4,1fr);gap:.5rem}
.suit-row button{
  border:1px solid var(--line);background:rgba(255,255,255,.04);border-radius:0;
  font-size:2rem;padding:.5rem 0;cursor:pointer;touch-action:none;
}
.suit-row button:active{background:rgba(245,158,11,.3)}
.rules-panel{top:.5rem;overflow:auto}
.rules-panel header{display:flex;align-items:center;justify-content:space-between;margin-bottom:.6rem}
.rules-panel dl{margin:0;display:grid;grid-template-columns:auto 1fr;gap:.35rem .8rem;font-size:.85rem}
.rules-panel dt{font-family:var(--mono);color:var(--brand-2);white-space:nowrap}
.rules-panel dd{margin:0;color:var(--ink-dim)}

@media (orientation:landscape) and (max-height:520px){
  .cards-actions .action-btn{height:52px}
  /* The header used to be hidden here to claw back vertical space, which
     also hid the only way into the rules — they were unreachable in
     landscape. Now that the felt hugs the hand there is room; shrink it
     instead of removing it. */
  .cards-top{font-size:.9em}
  .cards-pile{display:inline;margin:0 0 0 .8rem}
  .cards-rules-btn{padding:.22rem .6rem;font-size:.72rem}
}

/* ══════════════ doodle pad (Doodle Light) ══════════════
   The board is always the warm near-black, in either theme: the whole
   game is one bright line on a dark ground. --dd-ink is the artist's
   ink, set per round by the controller. */
#lay-doodle{display:flex;flex-direction:column;gap:.6rem;position:relative;
  /* iPhone: no long-press callout, magnifier text selection or grey tap
     flash anywhere on this pad — it is drawn on, not read */
  -webkit-touch-callout:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}
.dd-clock{font-family:var(--mono);font-size:.9rem;color:var(--ink);letter-spacing:.06em;min-width:3ch;text-align:right}
.dd-clock.low{color:var(--danger,#fb7185)}
.dd-main{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:.6rem}
/* The wrap's size comes from the flex layout alone; the canvas sits out of
   flow and is fitted to it in JS. In flow, the canvas's own height fed back
   into the box it was measured from and pushed over the header. It hangs
   from the top so a tall phone keeps the board under the thumb-free header
   instead of floating mid-screen, and --dd-bh (its height, set in JS) lets
   the overlay text find it. */
.dd-wrap{flex:1 1 0;min-height:140px;position:relative;touch-action:none}
.dd-canvas{
  position:absolute;left:50%;top:0;transform:translateX(-50%);
  display:block;background:#0b0a0d;touch-action:none;-webkit-touch-callout:none;
  border:1px solid var(--line);
  border:1px solid color-mix(in srgb,var(--dd-ink,var(--line)) 55%,transparent);
  image-rendering:-webkit-optimize-contrast;image-rendering:pixelated;image-rendering:crisp-edges;
}
.dd-wrap.live .dd-canvas{border-color:var(--dd-ink,var(--brand));cursor:crosshair}
.dd-cover{
  position:absolute;left:50%;top:calc(var(--dd-bh,100%) / 2);transform:translate(-50%,-50%);margin:0;
  padding:.5rem .9rem;background:rgba(11,10,13,.88);border:1px solid var(--line);
  color:#f5f1ea;font-family:var(--mono);font-size:.85rem;letter-spacing:.05em;
  text-align:center;white-space:nowrap;pointer-events:none;max-width:92%;overflow:hidden;text-overflow:ellipsis;
}
.dd-cover.low{top:calc(var(--dd-bh,100%) - .5rem);transform:translate(-50%,-100%);color:var(--dd-ink,#f5f1ea)}
.dd-side{flex:0 0 auto;display:flex;flex-direction:column;gap:.5rem}

/* tools: one row of actions, one row of brush sizes */
.dd-tools{display:grid;grid-template-columns:repeat(4,1fr);gap:.45rem}
.dd-tool,.dd-sizes button{
  height:50px;border:1px solid var(--line);border-radius:0;background:rgba(255,255,255,.04);
  color:var(--ink-dim);font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;
  cursor:pointer;touch-action:none;display:flex;align-items:center;justify-content:center;gap:.4rem;
}
.dd-tool.on,.dd-sizes button.on{border-color:var(--dd-ink,var(--brand));color:var(--ink);background:rgba(245,158,11,.16);background:color-mix(in srgb,var(--dd-ink,var(--brand)) 18%,transparent)}
.dd-tool:active,.dd-sizes button:active{transform:scale(.97)}
.dd-tool.armed{border-color:var(--danger,#fb7185);color:var(--danger,#fb7185)}
.dd-ink{width:10px;height:10px;background:var(--dd-ink,var(--brand));display:inline-block}
/* sizes sit after UNDO/CLEAR in the grid, so the four actions share a row */
.dd-sizes{grid-column:1/-1;order:1;display:grid;grid-template-columns:repeat(3,1fr);gap:.45rem}
.dd-sizes button{height:40px}
.dd-sizes i{display:block;border-radius:50%;background:var(--dd-ink,var(--ink))}

/* guessing: the blanks, then the rack */
.dd-guess{display:flex;flex-direction:column;gap:.55rem}
.dd-slots{display:grid;grid-template-columns:repeat(var(--n,5),minmax(0,40px));gap:.3rem;justify-content:center}
.dd-slot{
  aspect-ratio:1;border:0;border-bottom:2px solid var(--line);border-radius:0;
  background:rgba(255,255,255,.04);color:var(--ink);font-family:var(--mono);font-size:1.1rem;padding:0;cursor:pointer;
}
.dd-slot.full{border-bottom-color:var(--dd-ink,var(--brand))}
.dd-slot.lock{color:var(--brand);border-bottom-color:var(--brand);background:rgba(245,158,11,.12);cursor:default}
.dd-slots.nope{animation:ddNope .4s}
.dd-slots.nope .dd-slot.full:not(.lock){border-bottom-color:var(--danger,#fb7185);color:var(--danger,#fb7185)}
@keyframes ddNope{20%,60%{transform:translateX(-7px)}40%,80%{transform:translateX(7px)}}
.dd-tiles{display:grid;grid-template-columns:repeat(6,1fr);gap:.35rem}
.dd-tile{
  height:46px;border:1px solid var(--line-hot);border-radius:0;
  background:linear-gradient(160deg,rgba(245,158,11,.22),rgba(245,158,11,.06));
  color:var(--ink);font-family:var(--mono);font-size:1.15rem;cursor:pointer;touch-action:none;padding:0;
}
.dd-tile:active{transform:scale(.95)}
.dd-tile[disabled]{opacity:.15}

/* picking: three stacked words, easy to hard */
.dd-pick{flex:1 1 auto;display:flex;flex-direction:column;justify-content:center;gap:.6rem}
.dd-pick p{margin:0;text-align:center;color:var(--ink-dim);font-size:.9rem}
.dd-words{display:flex;flex-direction:column;gap:.6rem}
.dd-word{
  min-height:72px;border:1px solid var(--line);border-radius:0;background:rgba(255,255,255,.04);
  color:var(--ink);cursor:pointer;touch-action:none;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.25rem;
}
.dd-word b{font-family:var(--mono);font-size:1.25rem;letter-spacing:.08em}
.dd-word span{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;color:var(--ink-faint)}
.dd-word.d0{border-color:var(--ok);border-color:color-mix(in srgb,var(--ok) 55%,transparent)}
.dd-word.d1{border-color:var(--brand);border-color:color-mix(in srgb,var(--brand) 60%,transparent)}
.dd-word.d2{border-color:#fb7185;border-color:color-mix(in srgb,var(--danger,#fb7185) 60%,transparent)}
.dd-word:active{transform:scale(.98);background:rgba(245,158,11,.18)}
/* keyboard highlight, then the word you chose — lit at once, before the
   host answers, so a tap never looks like it did nothing */
.dd-word.sel{outline:2px solid var(--knob,var(--brand));outline-offset:-2px}
.dd-word.chosen{border-color:var(--knob,var(--brand));background:rgba(245,158,11,.2);background:color-mix(in srgb,var(--knob,var(--brand)) 22%,transparent)}
.dd-word.dim{opacity:.35}

/* A landscape phone puts the tools or the rack beside the board, so the
   drawing keeps the full height. */
@media (orientation:landscape) and (max-height:520px){
  .dd-main{flex-direction:row}
  .dd-side{width:42%;justify-content:center}
  .dd-tools{grid-template-columns:repeat(2,1fr)}
  .dd-tool{height:42px}
  .dd-sizes button{height:34px}
  .dd-tile{height:38px}
  .dd-words{flex-direction:row}
  .dd-word{flex:1}
}

/* Secondary pad buttons inherit the header's compact ghost size, which is
   fine under a mouse and 31px under a thumb. In a pad they are touch
   targets, so give them a floor. */
.pad-stage .ghost-btn { min-height: 40px; }
/* A 120px floor under the hand suits a tall phone, but on a short one the
   slot cannot hold hand + actions and TAKE was pushed off the bottom of the
   screen (measured: button bottom 668 in a 640px viewport). Below that
   height the hand yields instead — the actions must stay reachable. */
@media (max-height: 700px) {
  #lay-cards .cards-hand { min-height: 0; flex: 1 1 auto; }
}

/* Big view — the mini screen blown up to fill the pad, a player's own
   choice from the ☰ sheet. It takes what is left after the pad has had
   its share, so the controls still come first. */
.mini-wrap.big{ flex:1 1 auto; height:auto; min-height:26vh; max-height:100%; }
@media (max-height:620px){ .mini-wrap.big{ min-height:24vh; } }
@media (orientation:landscape){ .mini-wrap.big{ min-height:30vh; } }

/* The gamepad frame centres its middle column, which is right for a small
   glance-view but leaves the BIG view sitting at its minimum height between
   the thumbs. Let it stretch to the full height of the frame instead. */
@media (orientation: landscape) {
  .gp-mid .mini-wrap.big { align-self: stretch; height: auto; min-height: 0; max-height: 100%; }
}

/* Portrait big view: an even split left a dead band between the board and
   the thumb cluster, which sits low by design. Weight the split toward the
   board — the pad still gets what its controls need, and the assertion that
   the pad keeps >=120px guards the floor. */
@media (orientation: portrait) {
  .mini-wrap.big { flex: 2 1 auto; }
}

/* ══════════════════════════════════════════════════════════
   LIGHT THEME
   Written purely as overrides so the dark rules above are
   untouched — dark mode is byte-identical to what shipped.
   [data-theme] is stamped on <html> by theme.js, from the OS
   preference or an explicit choice.
   Canvas colours are the other half, and live in theme.js.
   ══════════════════════════════════════════════════════════ */
:root[data-theme="light"]{
  --bg:#ece5d8;
  --bg-2:#ded5c4;
  --panel:rgba(246,241,232,.90);
  --panel-solid:#f6f1e8;
  --line:rgba(90,74,54,.24);
  --line-hot:rgba(180,83,9,.45);
  --ink:#2b2536;
  --ink-dim:#5b5266;
  --ink-faint:#7b7188;
  --brand:#b45309;
  --brand-2:#7c3aed;
  /* measured on this paper ground: 4.69 / 4.95 / 4.91 / 5.04 : 1,
     min pairwise deltaE76 45.1, hue within 5.7deg of the originals */
  --p1:#0f6f7a;
  --p2:#b03050;
  --p3:#4a6b1e;
  --p4:#8c5210;
  --ok:#047857;
  --warn:#8c6510;
  --shadow:4px 4px 0 rgba(90,74,54,.30);
  /* On paper the bloom turns to haze and the pale glass loses its edge, so
     the glow is dropped and the bulb runs a stop deeper. */
  --logo-glow:0; --logo-bulb:#a86f18;
  --logo-glass-hi:#fffaec; --logo-glass-lo:#f2d79c; --logo-face:#6b4d22;
}

/* ── dark surfaces that must become light ── */
[data-theme="light"] .choice{background:linear-gradient(160deg,rgba(246,241,232,.96),rgba(228,219,202,.94))}
[data-theme="light"] .game-card{background:linear-gradient(160deg,rgba(246,241,232,.96),rgba(225,216,199,.94))}
[data-theme="light"] .screen-bar{background:rgba(236,229,216,.90)}
[data-theme="light"] .toast{background:rgba(246,241,232,.96)}
[data-theme="light"] .suit-pick,
[data-theme="light"] .rules-panel{background:rgba(246,241,232,.98)}
[data-theme="light"] .mini-wrap{background:linear-gradient(180deg,rgba(222,213,196,.75),rgba(236,229,216,.85))}
/* the overlay carries --ink text, which is dark in light mode, so a dark
   veil here would hide the result of the round */
[data-theme="light"] .stage-overlay{background:rgba(236,229,216,.92)}

/* ── a scrim darkens what is behind it in BOTH themes: a white scrim over
      a light page bleaches the room into unreadable mush ── */
[data-theme="light"] .sheet{background:rgba(43,37,54,.50)}
[data-theme="light"] .rebind-backdrop{background:rgba(43,37,54,.50)}

/* ── white tints only lift a surface on a dark ground; invert them ── */
[data-theme="light"] .ghost-btn,
[data-theme="light"] .seat.filled,
[data-theme="light"] .seg,
[data-theme="light"] .mini-btn{background:rgba(43,37,54,.07)}
[data-theme="light"] .code-input,
[data-theme="light"] .name-input,
[data-theme="light"] .suit-row button{background:rgba(43,37,54,.07)}
[data-theme="light"] .dp,
[data-theme="light"] .sheet-btn,
[data-theme="light"] .kcap,
[data-theme="light"] .switch .track{background:rgba(16,20,44,.07)}
[data-theme="light"] .vote-opt{background:linear-gradient(160deg,rgba(16,20,44,.07),rgba(16,20,44,.03))}

/* ── faint structure lines need more weight on a light ground ── */
[data-theme="light"] .bg-grid{
  background-image:linear-gradient(rgba(90,74,54,.09) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(90,74,54,.09) 1px,transparent 1px);
}
[data-theme="light"] .bg-glow{
  background:
    radial-gradient(ellipse 70% 50% at 18% 0%,rgba(180,83,9,.07),transparent 70%),
    radial-gradient(ellipse 60% 45% at 85% 8%,rgba(124,58,237,.06),transparent 72%),
    radial-gradient(ellipse 80% 40% at 50% 100%,rgba(4,120,87,.035),transparent 75%);
}
[data-theme="light"] .stick-base::before{border-color:rgba(90,74,54,.28)}
[data-theme="light"] .wheel::before{background:rgba(90,74,54,.35)}

/* ── accents tuned for a dark field that lose urgency on white ── */
[data-theme="light"] .action-btn.claim{border-color:var(--warn)}
[data-theme="light"] .sheet-btn.danger{background:rgba(190,18,60,.07);border-color:rgba(190,18,60,.55)}
[data-theme="light"] .mp-row.lost{border-color:rgba(180,83,9,.55)}

/* the decorative pong ball on the landing card: white vanishes on white */
[data-theme="light"] .art-ball{background:var(--ink);box-shadow:none}
/* .qr-box stays #fff in both themes on purpose — a QR needs a light ground
   and dark modules to scan reliably */

/* ── Neon bloom is how the dark theme carries depth; on a light ground the
      same shadow reads as a smudge. Trade glow for weight and definition.
      (The canvas half of this is PAL.glow, which collapses to 0.) ── */
[data-theme="light"] .slider-knob{box-shadow:0 6px 16px -6px rgba(43,37,54,.50)}
[data-theme="light"] .steer-brake.on{box-shadow:0 4px 14px -6px rgba(190,18,60,.6)}
[data-theme="light"] .buzzer{box-shadow:0 8px 24px -10px rgba(190,18,60,.5)}
[data-theme="light"] .net-pill i,
[data-theme="light"] .chip i,
[data-theme="light"] .seat .dot,
[data-theme="light"] .mp-row .dot{box-shadow:none}
[data-theme="light"] .power-chip{animation:none;box-shadow:0 4px 14px -8px rgba(124,58,237,.5)}
[data-theme="light"] .art-tv{box-shadow:none}
[data-theme="light"] .art-phone{box-shadow:none}
[data-theme="light"] .art-pad.l,
[data-theme="light"] .art-pad.r{box-shadow:none}

/* ══════════════════════════════════════════════════════════
   LO-FI PIXEL LAYER

   Everything above still describes the layout; this section
   changes only how it LOOKS. Kept last and self-contained so the
   aesthetic can be lifted back out in one piece if it is not
   wanted — the tokens revert, the structure never moved.

   Three rules carry most of it: hard edges instead of radii,
   offset blocks instead of blurred shadows, and stepped fills
   instead of smooth gradients.
   ══════════════════════════════════════════════════════════ */

/* ── type ──
   A pixel display face for anything short and structural, and a
   normal face kept for running prose: full blockiness is charming
   on a heading and punishing in a paragraph. */
.wordmark,h1,h2,h3,h4,
.prime-btn,.ghost-btn,.link-btn,.seg button,.mini-btn,
.room-code,.join-kicker,.seat,.chip,.pad-you,.pad-title,
.gc-meta span,.net-pill,.ov-kicker,.ov-title,.toast,
.sheet-item,.sheet-head,.mp-row,.switch b,
.action-btn,.dp,.buzzer,.menu-sel,.field span{
  font-family:var(--display);
  letter-spacing:.01em;
}
/* Silkscreen has no lowercase to speak of; keep it for the codes and
   tags where it is set in caps anyway. */
.room-code,.net-pill,.gc-meta span,.join-url{font-family:var(--mono)}

body{-webkit-font-smoothing:auto;font-smooth:never}

/* ── hard edges ── */
.chip,.gc-meta span,.net-pill,.pad-you,.seg,.seg button,.switch .track{border-radius:0}
/* dots stay round: a 9px square reads as a glitch, not a token */

/* ── blocks, not blur ──
   Every soft shadow becomes a solid offset block. The 2px border is
   what makes a control feel stamped rather than floated. */
.choice,.game-card,.join-card,.seat,.pv-card,.toast,
.sheet-panel,.rules-panel,.suit-pick,.stage-overlay .ov-inner{
  border-width:2px;box-shadow:var(--shadow);
}
.prime-btn,.ghost-btn,.action-btn,.dp,.mini-btn,.seg,.buzzer,
.code-input,.name-input{
  border-width:2px;border-style:solid;box-shadow:var(--shadow);
}
.prime-btn:hover,.ghost-btn:hover,.choice:hover,.game-card:hover,.pv-card:hover{
  transform:translate(-2px,-2px);box-shadow:6px 6px 0 rgba(0,0,0,.45);
}
[data-theme="light"] .prime-btn:hover,[data-theme="light"] .ghost-btn:hover,
[data-theme="light"] .choice:hover,[data-theme="light"] .game-card:hover,
[data-theme="light"] .pv-card:hover{box-shadow:6px 6px 0 rgba(90,74,54,.30)}
/* pressed controls sink into their own shadow */
.prime-btn:active,.ghost-btn:active,.action-btn:active,.dp:active,
.buzzer:active,.mini-btn:active,.action-btn.hot,.dp.hot,.buzzer.hot{
  transform:translate(2px,2px);box-shadow:0 0 0 rgba(0,0,0,0);
}

/* ── ambient wash ──
   Cards stay FLAT (below): on a limited palette a gradient fill dithers
   badly, so panels are single values. The page-level wash is the one
   exception — Happy Light's identity is ambient glow, and these run at
   4-10% alpha over a full-colour backdrop, far too faint to band. The
   earlier hard-stop version read as three stripes, not a glow. */
.bg-glow{
  background:
    radial-gradient(ellipse 70% 50% at 18% 0%,rgba(245,158,11,.10),transparent 70%),
    radial-gradient(ellipse 60% 45% at 85% 8%,rgba(167,139,250,.09),transparent 72%),
    radial-gradient(ellipse 80% 40% at 50% 100%,rgba(78,232,165,.045),transparent 75%);
}
[data-theme="light"] .bg-glow{
  background:
    radial-gradient(ellipse 70% 50% at 18% 0%,rgba(180,83,9,.07),transparent 70%),
    radial-gradient(ellipse 60% 45% at 85% 8%,rgba(124,58,237,.06),transparent 72%),
    radial-gradient(ellipse 80% 40% at 50% 100%,rgba(4,120,87,.035),transparent 75%);
}
.choice,.game-card{background-image:none;background-color:var(--panel-solid)}
[data-theme="light"] .choice,[data-theme="light"] .game-card{background-image:none;background-color:var(--panel-solid)}

/* ── scanlines ──
   The nostalgia cue, kept faint enough to read through. Off for
   anyone who asked for reduced motion, since fine stripes shimmer. */
.bg-grid::after{
  content:"";position:fixed;inset:0;pointer-events:none;
  background:repeating-linear-gradient(180deg,rgba(0,0,0,.16) 0 1px,transparent 1px 3px);
  opacity:.5;
}
[data-theme="light"] .bg-grid::after{
  background:repeating-linear-gradient(180deg,rgba(90,74,54,.10) 0 1px,transparent 1px 3px);
}
@media (prefers-reduced-motion:reduce){.bg-grid::after{display:none}}

/* ── the backdrop while a stage is up ──
   Fixed, the grid and the glow are each their own compositor layer (the
   grid a masked surface with the scanlines inside it), and all of them
   are blended again under the stage canvas on every frame a game draws.
   Measured at 3840x2160 on an integrated GPU that held every game to
   about 45 fps while its own drawing took under a millisecond. The
   screen and the mirror never scroll while a stage is showing, so the
   same boxes positioned absolutely look identical, are painted once into
   the page's own layer, and a 4K stage runs at 60. */
body:has(#view-screen.active .stage-wrap:not([hidden]),#view-watch.active .stage-wrap:not([hidden])) .bg-grid,
body:has(#view-screen.active .stage-wrap:not([hidden]),#view-watch.active .stage-wrap:not([hidden])) .bg-grid::after,
body:has(#view-screen.active .stage-wrap:not([hidden]),#view-watch.active .stage-wrap:not([hidden])) .bg-glow{position:absolute}

/* ── canvases keep their pixels square ── */
canvas,#stage,.mini-canvas,.cards-canvas{image-rendering:pixelated;image-rendering:crisp-edges}

/* the QR needs smooth square modules, not resampled ones */
.qr-box img,.qr-box canvas{image-rendering:pixelated}

/* ── the room code is the hero number on the lobby ── */
.room-code{letter-spacing:.22em;text-shadow:3px 3px 0 rgba(0,0,0,.35)}
[data-theme="light"] .room-code{text-shadow:3px 3px 0 rgba(90,74,54,.25)}

/* ── the pixel face has a floor ──
   Silkscreen is a bitmap design: its C keeps a one-pixel aperture that
   the rasteriser closes at some sizes (10px and 14px both turn CARDS
   into OARDS, while 11-13 and 15+ are clean). Rather than gamble on a
   per-element size surviving every device and pixel ratio, anything
   genuinely small drops back to the readable face. The headings, the
   room code and the in-game text carry the aesthetic; a 10px metadata
   tag was never going to. Legibility first. */
.gc-meta span,.net-pill,.sheet-note,.mp-tag,
.join-hint,.pad-hint{
  font-family:var(--font);
  letter-spacing:.02em;
}
/* short caps labels sit just above the floor — nudge them onto a size
   that rasterises cleanly rather than dropping the pixel face */
.join-kicker,.ov-kicker{font-size:.82rem}
.switch b{font-size:.95rem}

/* A short landscape slider puts its own hint label straight through the
   knob. The analog pad already hid it here; the climb pad has its own
   slider and was missed, and the solid pixel knob makes the collision
   plain. Same condition, same fix. */
@media (orientation:landscape) and (max-height:560px){
  #lay-climb .slider-tip{display:none}
}

/* ── one weight only ──
   Silkscreen ships 400 and 700, and the bold fills in the notch that
   tells M from H: at 700 the wordmark reads HP.GAHES and "MAM"/"HAH"
   are the same shape. Regular is already heavy, so the pixel face is
   pinned to it everywhere and hierarchy comes from size instead. */
.wordmark,h1,h2,h3,h4,
.prime-btn,.ghost-btn,.link-btn,.seg button,.mini-btn,
.room-code,.join-kicker,.seat,.chip,.pad-you,.pad-title,
.ov-kicker,.ov-title,.toast,.sheet-item,.sheet-head,.mp-row,
.switch b,.action-btn,.dp,.buzzer,.menu-sel,
.field span,.join-url,.kcap{font-weight:400}

/* ══════════════ persistent roster ══════════════
   Pinned under the bar so the room can always see who is connected and
   act on them. The header chips said the same thing less usefully, so
   they stand down rather than repeat it two lines above. */
.screen-bar .sb-players{display:none}
.roster{
  flex:0 0 auto;border-bottom:1px solid var(--line);
  padding:.6rem clamp(.8rem,2.4vw,1.5rem);
  background:var(--panel);backdrop-filter:blur(8px);
  max-height:34vh;overflow-y:auto;
}
.roster .seat-list{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
/* Once a round is running the roster gets out of the way: one line of
   chips, no key legends, no room for it to cover the play area. */
.roster.compact{max-height:none;overflow:visible;padding:.35rem clamp(.8rem,2.4vw,1.5rem)}
.roster.compact .seat-list{display:flex;flex-wrap:wrap;gap:.4rem}
.roster.compact .seat{
  padding:.22rem .6rem;font-size:.8rem;gap:.4rem;border-radius:0;flex:0 0 auto;
}
.roster.compact .seat-tools{margin-left:.4rem}
.roster.compact .mini-btn{padding:.1rem .4rem;font-size:.65rem}
.roster.compact .seat:not(.filled){display:none}
.seat.has-tools .seat-tools{order:4}
.seat.lost{border-style:dashed}

/* ══════════════ games in their own scroll frame ══════════════ */
.games-frame{
  position:relative;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;
  padding:2px;margin:-2px;
  scrollbar-width:thin;overscroll-behavior:contain;
}

/* ══════════════ ready gate ══════════════ */
/* The strip above the card is the room's own: its menu button sits there,
   and so does a toast while the card is up (below), so neither can lie
   over the card. The card never grows past what is left: its head and its
   row of buttons keep their place and the body between them gives. */
.gate-overlay{
  position:absolute;inset:0;z-index:6;display:grid;place-items:center;
  grid-template:minmax(0,1fr)/minmax(0,1fr);
  background:rgba(8,8,18,.86);backdrop-filter:blur(6px);padding:3rem 1rem 1rem;
}
[data-theme="light"] .gate-overlay{background:rgba(40,36,30,.72)}
.gate-card{
  text-align:center;max-width:min(560px,92vw);max-height:100%;min-height:0;box-sizing:border-box;
  display:flex;flex-direction:column;
  border:2px solid var(--line-hot);background:var(--panel-solid);
  padding:clamp(.7rem,2.6vmin,2rem);
}
.gate-head,.gate-foot{flex:0 0 auto}
.gate-kicker{margin:0;letter-spacing:.2em;text-transform:uppercase;font-size:.72rem;color:var(--ink-faint)}
.gate-title{margin:.35rem 0 .5rem;font-size:clamp(1.25rem,min(5vw,6.5vh),2.6rem);line-height:1.05}
.gate-sub{margin:0 0 1rem;color:var(--ink-dim);font-size:.95rem;line-height:1.5}
/* The body. It scrolls only when it cannot be made to fit (screen.js sizes
   it first), and then as the leaderboard's list does: no scrollbar, a fade
   at the edge that has more beyond it (two strips laid over it, not a mask:
   a mask repaints the whole body on every frame of a scroll), momentum under
   a finger. howto.js (H.scroller) sets .sc-on / .sc-top / .sc-end. */
.gate-view{flex:1 1 auto;min-height:0;position:relative;display:flex}
.gate-body{flex:1 1 auto;min-width:0;min-height:0;overflow-y:auto;overflow-x:hidden;
  -webkit-overflow-scrolling:touch;overscroll-behavior:contain;scrollbar-width:none;-ms-overflow-style:none}
.gate-body::-webkit-scrollbar,.ready-body::-webkit-scrollbar{display:none;width:0;height:0}
.gate-view::before,.gate-view::after,.ready-view::before,.ready-view::after{content:"";position:absolute;left:0;right:0;height:2.4em;z-index:1;
  pointer-events:none;opacity:0;transition:opacity .2s linear}
.gate-view::before{top:0;background:linear-gradient(to bottom,var(--panel-solid),transparent)}
.gate-view::after{bottom:0;background:linear-gradient(to top,var(--panel-solid),transparent)}
.sc-on:not(.sc-top)::before,.sc-on:not(.sc-end)::after{opacity:1}
.gate-players{display:flex;flex-wrap:wrap;gap:.5rem;justify-content:center;margin:.9rem 0}
/* a full room: the chips shrink to a dot and a name, lit when ready */
.gate-players.many{gap:.3rem;margin:.6rem 0}
.gate-players.many .gate-who{padding:.16rem .45rem;font-size:.72rem;gap:.3rem}
.gate-players.many .gate-who span{display:none}
.gate-players.many .gate-who.ok{background:rgba(255,255,255,.08)}
.gate-players.many .gate-who.ok i{box-shadow:0 0 0 2px var(--ok)}
.gate-who{
  display:inline-flex;align-items:center;gap:.4rem;
  border:1px solid currentColor;padding:.3rem .7rem;font-size:.82rem;opacity:.55;
}
.gate-who i{width:8px;height:8px;border-radius:50%;background:currentColor;flex:0 0 auto}
.gate-who b{color:var(--ink)}
.gate-who span{font-family:var(--mono);font-size:.68rem;color:var(--ink-faint)}
.gate-who.ok{opacity:1}
.gate-who.ok span{color:var(--ok)}
.gate-who.none{opacity:.5;color:var(--ink-faint);border-style:dashed}
.gate-actions{display:flex;align-items:center;justify-content:center;gap:.8rem;flex-wrap:wrap}
/* as wide as the longest thing it says, so the buttons beside it never shift */
.gate-count{font-family:var(--mono);font-size:.8rem;color:var(--ink-faint);min-width:6.5em;text-align:left;white-space:nowrap}
.gate-count.more{color:var(--brand)}
@media (max-height:520px),(max-width:480px){
  .gate-actions{gap:.5rem}
  .gate-actions .prime-btn{padding:.6rem 1.1rem;font-size:.9rem}
  .gate-actions .ghost-btn.wide{padding:.5rem .8rem;font-size:.82rem}
  .gate-kicker{display:none}
  .gate-title{margin:0 0 .35rem}
  .gate-players{margin:.5rem 0;gap:.3rem}
  .gate-players .gate-who{padding:.16rem .45rem;font-size:.72rem;gap:.3rem}
  .gate-players .gate-who span{display:none}
  .gate-players .gate-who.ok{background:rgba(255,255,255,.08)}
  .gate-players .gate-who.ok i{box-shadow:0 0 0 2px var(--ok)}
}
/* no height to spare (a phone on its side): who is ready sits beside the buttons */
@media (max-height:520px) and (min-width:600px){
  .gate-foot{display:flex;align-items:center;justify-content:center;gap:.4rem 1.2rem;flex-wrap:wrap;margin-top:.5rem}
  .gate-foot .gate-players{margin:0}
}
@media (max-width:480px){
  .gate-actions .prime-btn{padding:.6rem .7rem}
  .gate-actions .ghost-btn.wide{padding:.5rem .6rem}
  .gate-count{min-width:0}
}
/* A toast while the card is up goes in the strip above it, on one line,
   unless the hint bar has room for it in its own strip (further down). */
html:not(.nav-on) #gate:not([hidden]) ~ .toast-rail,html.nav-tight #gate:not([hidden]) ~ .toast-rail{
  top:.55rem;bottom:auto;left:.8rem;right:4rem;display:flex;flex-direction:row-reverse;justify-content:flex-end;gap:.5rem;max-width:none;overflow:hidden}
html:not(.nav-on) #gate:not([hidden]) ~ .toast-rail .toast,html.nav-tight #gate:not([hidden]) ~ .toast-rail .toast{
  flex:0 1 auto;min-width:0;padding:.3rem .8rem;font-size:.76rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* a game's own pre-game screen: the way back out, in the corner of the stage */
.stage-cancel{position:absolute;left:.8rem;top:.8rem;z-index:4;background:rgba(8,8,18,.72);color:var(--ink);
  border-color:var(--line-hot);font-size:.85rem;padding:.45rem .8rem;min-height:40px}
.stage-cancel:hover{background:rgba(8,8,18,.9)}

/* ══════════════ ready pad ══════════════ */
/* Portrait: the briefing fills the space and NEXT sits at the BOTTOM,
   where a thumb actually rests. Centring it left a dead band underneath. */
#lay-ready{display:flex;flex-direction:column;gap:.8rem}
.ready-info{
  flex:1;min-height:0;display:flex;flex-direction:column;justify-content:center;
  text-align:center;padding:0 .4rem;
}
/* under the title: sized to fit by controller.js, and scrolled like the
   big screen's briefing only when it still does not (see .gate-body) */
.ready-view{flex:0 1 auto;min-height:0;position:relative;display:flex}
.ready-body{flex:1 1 auto;min-width:0;min-height:0;overflow-y:auto;overflow-x:hidden;touch-action:pan-y;
  -webkit-overflow-scrolling:touch;overscroll-behavior:contain;scrollbar-width:none;-ms-overflow-style:none}
.ready-view::before{top:0;background:linear-gradient(to bottom,var(--bg),transparent)}
.ready-view::after{bottom:0;background:linear-gradient(to top,var(--bg),transparent)}
/* Cancel, while a game is starting: in the top bar, as far from NEXT as the
   pad goes, and it takes a second tap */
.pad-cancel{flex:0 0 auto;font-size:.78rem;padding:.3rem .65rem;white-space:nowrap;color:var(--ink)}
.pad-cancel.armed{border-color:var(--brand);color:var(--brand)}
.ready-kicker{margin:0;letter-spacing:.18em;text-transform:uppercase;font-size:.68rem;color:var(--ink-faint)}
.ready-title{margin:.25rem 0 .4rem;font-size:1.35rem;line-height:1.15}
.ready-sub{margin:0;color:var(--ink-dim);font-size:.88rem;line-height:1.45}
.ready-btn{flex:0 0 auto;min-height:clamp(84px,18vh,140px);font-size:1.3rem}
.ready-btn.done{opacity:.5}
.ready-wait{margin:0;text-align:center;color:var(--ink-faint);font-size:.8rem;min-height:1.1em}

/* landscape: the briefing text and the button sit side by side so NEXT
   stays under a thumb instead of below the fold */
@media (orientation:landscape) and (max-height:520px){
  #lay-ready{flex-direction:row;align-items:center;gap:1rem}
  .ready-info{flex:1;text-align:left;justify-content:center}
  .ready-btn{flex:0 0 40%;min-height:clamp(70px,40vh,120px)}
  .ready-wait{display:none}
  .roster{max-height:26vh}
}
@media (max-width:860px){
  .roster .seat-list{grid-template-columns:1fr}
  .roster{max-height:30vh}
}

/* ══════════════ one screen, no scrolling ══════════════
   The lobby and the landing page each fit the viewport. Nothing on them
   scrolls, so the game a controller highlights is always in view. */

/* ── top bar: brand left; seat controls, then ☰, at the far right ── */
.sb-adders{display:flex;align-items:center;gap:.5rem .9rem;margin-left:auto;min-width:0}
.sb-adders .seat-add{white-space:nowrap;padding:.35rem .7rem;font-size:.8rem}
.sb-adders .switch{min-width:0}
.sb-adders .switch b{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media (max-width:700px){
  .sb-adders{gap:.6rem}
  .sb-adders .seat-add{font-size:.7rem;padding:.3rem .5rem}
  .sb-adders .switch{gap:.4rem}
  .sb-adders .switch b{font-size:.78rem}
  .sb-adders .switch .track{width:36px;height:20px}
  .sb-adders .switch .track i{width:14px;height:14px}
  .sb-adders .switch input:checked + .track i{left:18px}
}
.sb-right{margin-left:auto;flex:0 0 auto}
.sb-adders:not([hidden]) + .sb-right{margin-left:0}
/* the status pill gives way first, so ☰ always stays at the right edge */
.sb-left{min-width:0;flex:0 1 auto}
.sb-left .wordmark{flex:0 0 auto}
.net-pill{min-width:0;overflow:hidden}
#net-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ── roster: the seats on one row, two once a room passes six (--seat-cols) ── */
.roster{max-height:none;overflow:visible;padding:.45rem clamp(.7rem,1.8vw,1.5rem)}
/* Seats on the left, then the room's controls at the far right — the row
   that used to be a separate top bar. The seats take whatever is left. */
.roster{display:flex;align-items:center;gap:.6rem 1rem;flex-wrap:wrap}
.roster .seat-list{flex:1 1 520px;min-width:0}
.roster-tools{display:flex;align-items:center;gap:.6rem;flex:0 0 auto;margin-left:auto;min-width:0}
.roster-tools .sb-adders{margin-left:0}
/* the connection pill shrinks to its dot while all is well; trouble gets words */
.roster-tools .net-pill.live{padding:.25rem;border-color:transparent}
.roster-tools .net-pill.live span{display:none}
.roster.compact .roster-tools .ghost-btn{padding:.2rem .5rem}
.roster .seat-list{grid-template-columns:repeat(var(--seat-cols,4),minmax(0,1fr));gap:.45rem}
.roster .seat{padding:.4rem .6rem;font-size:.82rem;gap:.4rem;min-width:0;white-space:nowrap;overflow:hidden}
.roster .seat b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}

/* ── lobby: join card left, games fill the rest ── */
#lobby{
  overflow:hidden;grid-template-columns:clamp(210px,21vw,400px) minmax(0,1fr);
  grid-template-rows:minmax(0,1fr);gap:clamp(.7rem,1.8vw,1.8rem);padding:clamp(.7rem,1.8vw,1.8rem);
}
#lobby .lobby-join{min-height:0;overflow:hidden;container:joincol / size}
#lobby .join-card{
  flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(.5rem,2vh,1.2rem);padding:clamp(.7rem,1.6vw,1.4rem);
}
.join-text{min-width:0}
#lobby .join-url{margin:.2rem 0 .5rem;font-size:clamp(.8rem,1.3vw,1.3rem)}
#lobby .room-code{margin:.1rem 0 0;font-size:clamp(2rem,min(4.6vw,9vh),5rem)}
/* The join QR takes the column height the text and mirror card leave. */
#lobby .qr-box img,#lobby .qr-box canvas{width:clamp(48px,min(76cqi,100cqh - var(--join-rest,316px)),300px)}
/* Mirror card: a small second QR and the watch link, under the join card.
   It gives up its hint, then its QR, as space runs out; the copy button
   always stays. */
.mirror-card{
  flex:0 0 auto;display:flex;align-items:center;gap:.8rem;min-width:0;
  border:2px solid var(--line);background:var(--panel);box-shadow:var(--shadow);
  padding:clamp(.5rem,1vw,.9rem);
}
.mirror-text{min-width:0;display:flex;flex-direction:column;align-items:flex-start;gap:.25rem}
.mirror-url{
  margin:0;max-width:100%;font-family:var(--mono);font-size:.72rem;color:var(--brand-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.mirror-hint{margin:0;color:var(--ink-faint);font-size:.75rem;line-height:1.35}
.mirror-card .ghost-btn{padding:.3rem .6rem;font-size:.78rem}
.qr-box.small{padding:.3rem;flex:0 0 auto}
#lobby .qr-box.small img,#lobby .qr-box.small canvas{width:clamp(52px,min(5.5vw,10vh),110px)}
@media (max-height:760px){.mirror-hint{display:none}}

/* the pass gate is text; keep it inside the column on a short screen */
#lobby .join-card.gate{justify-content:center;overflow:hidden}
#lobby .gate-p{margin-bottom:.8rem}

#lobby .lobby-games{min-height:0;gap:0}

/* Fixed-size cards in a frame that scrolls. A card is the same size on
   every screen and always shows the same things: icon, name, how many
   can play, what it needs, and its play count (the description is in the
   line under the frame and in the briefing, not on the card). A
   screen that cannot hold every card scrolls — with no scrollbar: the
   frame follows the selected card (S.revealSel), and a wheel or a finger
   still moves it. The edges fade so a row sliding out reads as "more". */
.games-frame{
  flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:16px 6px;margin:-6px;
  scrollbar-width:none;-ms-overflow-style:none;overscroll-behavior:contain;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 16px,#000 calc(100% - 16px),transparent 100%);
  mask-image:linear-gradient(to bottom,transparent 0,#000 16px,#000 calc(100% - 16px),transparent 100%);
}
.games-frame::-webkit-scrollbar{display:none}
.game-grid{
  --gc-w:16rem;--gc-h:9.5rem;
  display:grid;gap:1rem;justify-content:center;align-content:start;
  grid-template-columns:repeat(auto-fill,var(--gc-w));grid-auto-rows:var(--gc-h);
}
.game-card{
  width:var(--gc-w);height:var(--gc-h);min-height:0;padding:.9rem 1rem;
  display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);
  align-items:center;gap:.6rem .75rem;text-align:left;
}
/* the icon beside the name: one header line, up to two lines of name */
.gc-ico{font-size:2.2rem;line-height:1}
.game-card h3{margin:0;font-size:1.05rem;line-height:1.2;overflow-wrap:anywhere;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* the facts sit on the card's floor, however many of them there are */
.gc-foot{grid-column:1/-1;align-self:end;display:flex;flex-direction:column;align-items:flex-start}
.gc-meta{margin-top:0;flex-wrap:wrap;gap:.35rem}
/* the spacing rides on the chip, so a game with no plays yet leaves no gap */
.gc-meta-plays .gc-plays{margin-bottom:.35rem}
.gc-meta span{white-space:nowrap}
.gc-meta .gc-plays{color:var(--brand-2);border-color:var(--brand-2)}
/* The players and needs chips are quiet by default: dark grey, like the
   rest of the card's small print. One the room already satisfies gets a
   golden border and brighter text, nothing more — no tick, no fill. The
   difference is the whole message. */
.gc-meta .gc-players,.gc-meta .gc-needs{color:var(--ink-faint);border-color:var(--line);transition:border-color .2s,color .2s}
.gc-meta .gc-players.met,.gc-meta .gc-needs.met{color:var(--ink);border-color:var(--brand)}
/* The kind of controller a game is BEST played with (needs.js: `best`),
   wherever what it is played on is listed: bold, in the arcade's cyan
   rather than the ink around it, and with a small square before it so
   the meaning does not hang on colour alone. The square is drawn, not a
   glyph, so the pixel face cannot lose it. */
.fit-best{font-weight:700;color:var(--fit);white-space:nowrap}
.fit-best::before{content:"";display:inline-block;width:.42em;height:.42em;margin-right:.3em;background:currentColor;vertical-align:.12em}
:root{--fit:#5fd8ec}
[data-theme="light"]{--fit:#00657a}
/* a quiet chip keeps its best word readable; a lit one keeps it cyan */
.gc-meta .gc-needs .fit-best{color:var(--fit)}
.fit-legend{margin:.35rem 0 0;text-align:center;font-family:var(--mono);font-size:.6rem;line-height:1.3;color:var(--ink-dim)}
@media (max-height:520px){.fit-legend{display:none}}
/* two cards to a row on a phone: no room for the square, the bold stays */
@media (max-width:600px){.gc-meta .gc-needs{padding-left:.5rem;padding-right:.5rem}.gc-meta .gc-needs .fit-best::before{display:none}}
.howto-fit{margin:0;font-family:var(--mono);font-size:.62rem;line-height:1.5;color:var(--ink-dim)}
.gate-howto .howto-fit{font-size:clamp(10.5px,.56em,21px)}
/* The highlight is colour and border only: no lift, so no card moves. */
.game-card:hover,.game-card.sel{transform:none}
/* a short screen (a phone on its side): smaller cards */
@media (max-height:520px){
  .game-grid{--gc-w:13.5rem;--gc-h:8.25rem;gap:.6rem}
  .game-card{padding:.7rem;gap:.45rem .6rem}
  .gc-ico{font-size:1.7rem}
  .game-card h3{font-size:.85rem}
}
/* a narrow screen (a phone upright): two cards to a row, still one size */
@media (max-width:600px){
  .game-grid{--gc-h:9rem;grid-template-columns:repeat(2,minmax(0,1fr));gap:.6rem}
  .game-card{width:auto;padding:.75rem;gap:.5rem .55rem}
  .gc-ico{font-size:1.7rem}
  .game-card h3{font-size:.85rem}
}

/* Phone or portrait host (an upright tablet included): everything stacks,
   still without scrolling. */
/* phone: the seat controls take their own row under the brand and ☰ */
@media (max-width:600px){
  .screen-bar{flex-wrap:wrap;row-gap:.4rem}
  .sb-left{flex:1 1 0}
  .sb-adders{order:3;flex-basis:100%;margin-left:0}
  .sb-adders + .sb-right{margin-left:auto}
}
@media (max-width:600px){.roster .seat-list{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:860px),(orientation:portrait){
  /* Stacked, the join card can leave the games almost no height, and a
     fixed-size card cannot shrink to fit. Guarantee a card's height and
     let the lobby itself scroll, again without a scrollbar. */
  #lobby{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(18.5rem,1fr);
    overflow-y:auto;scrollbar-width:none;-ms-overflow-style:none}
  #lobby::-webkit-scrollbar{display:none}
  #lobby .join-card{flex-direction:row;justify-content:space-around;text-align:left;padding:.6rem .9rem}
  #lobby .join-card.gate{flex-direction:column;text-align:center}
  #lobby .room-code{font-size:clamp(1.8rem,9vw,3rem)}
  #lobby .qr-box{margin:0}
  #lobby .qr-box img,#lobby .qr-box canvas{width:clamp(76px,22vw,130px)}
  /* upright tablet: the mirror card sits beside the join card. The column
     is content-sized here, so it cannot be a size container. */
  #lobby .lobby-join{flex-direction:row;gap:.7rem;container-type:normal}
  #lobby .join-card{flex:2 1 0}
  .mirror-card{flex:1 1 0}
  #lobby .qr-box.small img,#lobby .qr-box.small canvas{width:clamp(56px,11vw,110px)}
}
/* phone: too narrow for two cards, so the mirror becomes a slim strip */
@media (max-width:600px){
  #lobby .lobby-join{flex-direction:column;gap:.5rem}
  #lobby .join-card,.mirror-card{flex:0 0 auto}
  .mirror-card{padding:.4rem .6rem}
  .mirror-text{flex-direction:row;flex-wrap:wrap;align-items:center;gap:.2rem .6rem;flex:1}
  .mirror-url{flex:1 1 100%}
  .mirror-hint{display:none}
  #lobby .qr-box.small img,#lobby .qr-box.small canvas{width:44px}
}
@media (max-width:600px) and (max-height:700px){.mirror-card .qr-box{display:none}}
/* Short screens: a slimmer top bar leaves the height to the games. */
@media (max-height:500px){
  .screen-bar{padding:.3rem .8rem}
  .screen-bar .wordmark.small{font-size:.85rem}
}
/* Landscape phone: too short to stack, so keep the two columns. */
@media (max-width:860px) and (min-aspect-ratio:4/3){
  .roster .seat-list{grid-template-columns:repeat(var(--seat-cols,4),minmax(0,1fr))}
  .roster .seat{padding:.25rem .5rem;font-size:.72rem}
  #lobby{grid-template-columns:clamp(130px,24vw,220px) minmax(0,1fr);grid-template-rows:minmax(0,1fr);padding:.5rem;gap:.6rem}
  #lobby .join-card{flex-direction:column;justify-content:center;text-align:center;gap:.3rem;padding:.35rem}
  #lobby .room-code{font-size:clamp(1.4rem,5vw,2.2rem)}
  #lobby .join-text .join-kicker{display:none}
  #lobby .join-url{margin:.1rem 0 .2rem;font-size:.7rem}
  #lobby .qr-box{padding:.35rem}
  #lobby .lobby-join{container-type:size}
  #lobby .qr-box img,#lobby .qr-box canvas{width:clamp(40px,min(80cqi,100cqh - var(--join-rest)),120px)}
  #lobby{--join-rest:180px}
  #lobby .lobby-join{flex-direction:column;gap:.4rem}
  .mirror-card{padding:.3rem;justify-content:center}
  .mirror-card .qr-box,.mirror-url,.mirror-hint{display:none}
  .mirror-text{align-items:center}
  .mirror-card .join-kicker{display:none}
  #btn-mirror-copy::after{content:" · mirror"}
}
/* Short screens drop the pass-gate small print before anything clips. */
@media (max-height:620px){
  #lobby .join-card.gate .join-hint{display:none}
}
@media (max-height:500px){
  #lobby .gate-p{display:none}
  #lobby .gate-h{font-size:1rem;margin:.3rem 0 .6rem}
  #lobby .join-card.gate .prime-btn{padding:.6rem .8rem;font-size:.85rem}
}

/* ── landing: fits one viewport ── */
/* At least the viewport, never held to it: where it does not fit (a short
   window) the page itself scrolls, at the window's edge. It used to be a
   box of exactly 100dvh that scrolled itself, and as the cabinet grew its
   scrollbar turned up in the middle of every wide screen it overran. */
#view-landing.active{
  display:flex;flex-direction:column;justify-content:safe center;
  min-height:100dvh;gap:clamp(.7rem,2.6vh,1.8rem);
  padding:max(clamp(.7rem,2.4vh,1.6rem),env(safe-area-inset-top,0px)) max(clamp(1rem,4vw,2rem),env(safe-area-inset-right,0px))
          max(clamp(.7rem,2.4vh,1.6rem),env(safe-area-inset-bottom,0px)) max(clamp(1rem,4vw,2rem),env(safe-area-inset-left,0px));
}
/* The header is a grid of the account bar (Leaderboards, Controllers, Sign in
   or the account chip), the logo and the tagline. The bar used to be laid
   over the top right corner, which put it on the logo, or off the left edge,
   whenever the two did not happen to miss each other: its width follows the
   sign-in state and the player's name. Now it is in the flow: a row of its
   own above the logo, right-aligned, wrapping if it must. */
.landing-head{margin:0;display:grid;grid-template-columns:minmax(0,1fr);justify-items:center;column-gap:1rem}
#view-landing .landing-account{justify-self:end;flex-wrap:wrap;min-width:0;max-width:100%;margin:0 0 .5rem;padding:0}
/* Wide but not tall (a laptop, a 720p TV): no height to spare for that row,
   so the bar takes the column to the right of the logo and wraps inside it.
   The side columns are equal, which keeps the logo centred; 20rem is the
   bar's widest row, and a column that has to be held to it moves the logo
   left a little instead of letting the bar onto it. */
@media (min-width:70em) and (max-height:62.49em){
  .landing-head{grid-template-columns:minmax(0,1fr) auto minmax(20rem,1fr)}
  .landing-head .wordmark{grid-column:2;grid-row:1}
  #view-landing .landing-account{grid-column:3;grid-row:1;align-self:start;margin:0}
  .landing-head .tagline{grid-column:1 / -1}
}
/* Wide and tall (a desktop, a 1080p TV): the corner, where it has always
   been there, and the header keeps clear of the strip it sits in. */
@media (min-width:70em) and (min-height:62.5em){
  #view-landing .landing-account{position:absolute;top:clamp(.7rem,2.4vh,1.6rem);right:clamp(1rem,4vw,2rem);flex-wrap:nowrap;margin:0}
  .landing-head{padding-top:3.25rem}
}
#view-landing .wordmark{font-size:clamp(1.6rem,min(6vw,6.5vh),4rem)}
.tagline{margin:.6rem auto 0;font-size:clamp(.9rem,1.9vh,1.15rem)}
.choice-grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:clamp(.6rem,1.6vw,1.15rem)}
.choice{padding:clamp(.8rem,2.2vh,1.6rem) clamp(.9rem,1.8vw,1.6rem)}
.choice-art{height:clamp(56px,11vh,104px)}
.choice h2{margin:clamp(.5rem,1.6vh,1.1rem) 0 .35rem;font-size:clamp(1rem,2.3vh,1.5rem)}
.choice p{font-size:clamp(.82rem,1.7vh,.97rem)}
.choice-cta{margin-top:clamp(.5rem,1.4vh,1.1rem)}
.random-cta{margin:0}
.game-preview{margin:0;text-align:center}
.game-preview h3{margin:0 0 .6rem}
.preview-cards{display:flex;flex-wrap:wrap;justify-content:center;gap:.45rem}
.pv-card{display:inline-flex;align-items:center;gap:.45rem;padding:.35rem .7rem;font-size:.85rem;color:var(--ink-dim)}
/* how often the game has been played; hidden until the API has a number */
.pv-plays{font-family:var(--mono);font-size:.62rem;letter-spacing:.06em;color:var(--ink-faint);border-left:1px solid var(--line);padding-left:.45rem}
.pv-ico{font-size:1.05rem}
/* zoom, not transform: the drawings must shrink their layout box too */
@media (max-height:800px){.choice-art{height:70px}.choice-art > *{zoom:.68}}
@media (max-height:680px){.choice-art{height:52px}.choice-art > *{zoom:.5}.choice p{display:none}}
/* The cabinet wraps to four rows and more now; under 1000px of height, or
   in a narrow window, its chips are a size smaller so the page still fits.
   (A phone has its own, below.) */
@media (min-width:43.76em) and (max-height:62.49em),(min-width:43.76em) and (max-width:69.99em){
  .game-preview h3{margin-bottom:.4rem}
  .preview-cards{gap:.3rem}
  .pv-card{padding:.2rem .55rem;font-size:.78rem;gap:.35rem}
  .pv-ico{font-size:.95rem}
}
/* A window between a phone and a desktop (a tablet, a browser tiled to half
   the screen): three narrow cards whose words wrap, under the account bar's
   own row. Their drawings shrink first, then their blurbs go. */
@media (min-width:43.76em) and (max-width:69.99em) and (max-height:66em){.choice-art{height:52px}.choice-art > *{zoom:.5}}
@media (min-width:43.76em) and (max-width:69.99em) and (max-height:47.99em),
       (min-width:43.76em) and (max-width:51.99em) and (max-height:54em),
       (min-width:43.76em) and (max-width:62em) and (max-height:50em){.choice p{display:none}}

/* Phone portrait: the three ways in become rows, the cabinet shows icons. */
@media (max-width:700px){
  #view-landing.active{gap:clamp(.6rem,2vh,1.2rem)}
  .choice-grid{grid-template-columns:minmax(0,1fr)}
  .choice{display:grid;grid-template-columns:64px minmax(0,1fr);column-gap:.9rem;align-items:center;padding:.7rem .9rem}
  .choice-art{height:48px;grid-row:1 / span 3}
  .choice-art > *{zoom:.42}
  .choice h2{margin:0;font-size:1rem}
  .choice p{display:none}
  .choice-cta{margin-top:.3rem}
  .choice.recommended{padding-top:1.4rem}
  .pv-card{padding:.35rem .45rem}
  .pv-name{display:none}
}
/* Landscape phone: no room for decoration at all. */
@media (max-height:500px){
  #view-landing .landing-account{margin-bottom:.3rem}
  .tagline,.game-preview,.choice-art,.choice p{display:none}
  .choice-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
  .choice{display:block}
}

/* ── sign-in: landing chip + sheet account row (non-gating) ── */
.landing-account{
  display:flex; align-items:center; gap:.5rem; justify-content:flex-end;
  max-width:1100px; margin:0 auto .4rem; padding:0 .2rem;
}
.landing-account a.ghost-btn{text-decoration:none;display:inline-block}
/* Three of them have to fit a 320px phone in the pixel face, which is wide:
   the labels and their padding give a little below 424px, and no further. */
.landing-account .ghost-btn{flex:0 0 auto;font-size:clamp(.68rem,3.4vw,.9rem);padding-inline:clamp(.45rem,2.2vw,.9rem);white-space:nowrap}
.landing-account .acct{flex:0 1 auto;min-width:0;max-width:100%}
.landing-account .acct-summary{max-width:100%}
/* under a thumb: 44px at least, the chip too */
@media (pointer:coarse),(max-width:43.75em){
  .landing-account .ghost-btn{min-height:2.75rem}
  .landing-account .acct-summary{min-width:2.75rem;min-height:2.75rem;justify-content:center}
}
.landing-account a.ghost-btn:visited{color:var(--ink-dim)}
.landing-account .acct-who{
  max-width:min(40vw,10rem); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* Same reasoning as .acct-who — an address is UI text, not chrome. */
#room-who{ color:var(--ink); }
.landing-account a.sheet-btn{text-decoration:none}

/* Account menu — a <details> disclosure, matching portal's treatment so the
   two properties read as one product. <details> is used rather than a custom
   widget because it is keyboard-operable with no script; the JS below only
   adds close-on-outside-click and Escape, which are conveniences, not the
   mechanism. */
.acct{position:relative}
.acct[hidden]{display:none}
/* The trigger is the shared Happy Light signed-in chip (happylight.io's
   .nav__me, the apps' .hl-me): round, mint-edged, ghost avatar with a live
   dot, "Signed in" over the name. It stays round on purpose, against the
   arcade's square corners, so "you" looks the same on every site. Up to
   640px only the avatar shows (wide enough for a phone on its side, where
   the name would push the bar onto a second row); the aria-label carries
   the words. */
.acct-summary{
  display:inline-flex; align-items:center; gap:.55rem; cursor:pointer;
  list-style:none; padding:.2rem .9rem .2rem .2rem; border-radius:999px;
  border:1px solid color-mix(in srgb,var(--ok) 38%,var(--line));
  background:color-mix(in srgb,var(--ok) 9%,transparent);
  font-family:var(--font); color:var(--ink); white-space:nowrap; user-select:none;
  transition:background-color .2s,border-color .2s;
}
.acct-summary::-webkit-details-marker{display:none}
.acct-summary:hover,.acct[open] > .acct-summary{
  background:color-mix(in srgb,var(--ok) 16%,transparent);
  border-color:color-mix(in srgb,var(--ok) 60%,transparent)}
.acct-summary:focus-visible{outline:2px solid var(--brand); outline-offset:2px}
.hl-me-avatar{position:relative; display:grid; place-items:center; flex:none;
  width:2rem; height:2rem; border-radius:50%;
  background:color-mix(in srgb,var(--ink) 10%,transparent); color:var(--ink)}
.hl-me-ghost{width:1rem; height:1rem; fill:currentColor; animation:hl-me-bob 2.4s steps(4) infinite}
.hl-me-dot{position:absolute; right:-1px; bottom:-1px; width:.65rem; height:.65rem;
  border-radius:50%; background:var(--ok); box-shadow:0 0 0 2px var(--bg)}
.hl-me-dot::after{content:""; position:absolute; inset:0; border-radius:inherit;
  background:var(--ok); animation:hl-me-ping 2.4s cubic-bezier(.22,.61,.36,1) infinite}
@keyframes hl-me-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-2px)}}
@keyframes hl-me-ping{0%{transform:scale(1);opacity:.6}70%,100%{transform:scale(2.4);opacity:0}}
.hl-me-text{display:inline-flex; flex-direction:column; min-width:0; line-height:1.1; text-align:left}
.hl-me-state{font-size:.8125rem; font-weight:650; color:var(--ink)}
.hl-me-sub{font-size:.6875rem; font-weight:500; color:var(--ink-dim)}
@media (max-width:640px){.acct-summary{padding:.2rem}.hl-me-text{display:none}}
@media (prefers-reduced-motion:reduce){.hl-me-ghost,.hl-me-dot::after{animation:none}}
/* Type system, decided deliberately: everything inside the account
   affordance is UI text and uses the body face. --mono is Silkscreen,
   an all-caps pixel display face — it forces an address to
   RENEOUN@GMAIL.COM, which is both unreadable and wrong, and a person's
   name fares little better. The pixel face stays on arcade chrome
   (LEADERBOARDS, room codes, game titles) where the shouting is the
   point. Mixing the two inside one small menu was the bug. */
.acct-who{
  max-width:min(38vw,16rem);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.acct-menu{
  position:absolute; right:0; top:calc(100% + .4rem); z-index:40;
  min-width:13.5rem; max-width:min(18rem,92vw);
  display:flex; flex-direction:column;
  padding:.3rem; border:1px solid var(--line);
  border-radius:var(--r);
  background:var(--panel-solid); box-shadow:var(--shadow);
}
.acct-email{
  margin:0; padding:.4rem .55rem .5rem;
  font-size:.74rem; color:var(--ink-dim); overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap;
  border-bottom:1px solid var(--line);
}
.acct-item{
  appearance:none; background:none; border:0; cursor:pointer;
  font:inherit; font-size:.85rem; color:var(--ink); text-align:left;
  text-decoration:none; padding:.5rem .55rem; border-radius:calc(var(--r) / 2);
}
.acct-item:hover{background:var(--line)}
.acct-item:focus-visible{outline:2px solid var(--brand); outline-offset:-2px}
.acct-out{color:var(--ink-dim); margin-top:.15rem; border-top:1px solid var(--line)}

/* ── leaderboards overlay ───────────────────────────────────────────────
   One card of a fixed size, so nothing moves when a board loads, is empty or
   is swapped for the next. Everything inside is in em of the card's own font
   size, which grows with the window: readable from a sofa on a TV, and still
   a full-width sheet on a phone. Pixel face for the arcade chrome (titles,
   ranks, numbers); the body face for names, which Silkscreen would shout. */
.lb-overlay{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;
  background:rgba(8,6,12,.8);padding:max(.6rem,env(safe-area-inset-top)) .6rem max(.6rem,env(safe-area-inset-bottom))}
.lb-overlay[hidden]{display:none}
.lb-card{
  --lb-h:44px; --lb-rw:3.8em;
  --lb-gold:#f5c542; --lb-silver:#cdd3db; --lb-bronze:#d08a4a;
  font-size:clamp(14px,1.15vw + .55vh,26px);
  width:min(100%,40em);height:min(100%,38em);display:flex;flex-direction:column;
  background:var(--panel-solid);border:2px solid var(--line-hot);border-radius:var(--r);box-shadow:var(--shadow);
  outline:none;overflow:hidden;
}
.lb-head{flex:0 0 auto;display:flex;align-items:center;gap:.5em;padding:.9em .9em .7em 1em}
.lb-titles{flex:1;min-width:0;text-align:center}
.lb-one .lb-titles{text-align:left}
.lb-kicker{margin:0;font-family:var(--display);font-size:.6em;letter-spacing:.18em;text-transform:uppercase;color:var(--brand)}
.lb-title{margin:.15em 0 .1em;font-family:var(--display);font-size:1.45em;line-height:1.1;letter-spacing:.02em;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lb-sub{margin:0;font-size:.72em;color:var(--ink-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lb-arrow,.lb-x{flex:0 0 auto;width:2.6em;height:2.6em;min-width:40px;min-height:40px;display:grid;place-items:center;padding:0;
  font-size:.8em;line-height:1;cursor:pointer;color:var(--ink-dim);
  background:rgba(255,255,255,.04);border:1px solid var(--line);border-radius:0;transition:color .15s,border-color .15s,background .15s}
.lb-arrow{color:var(--brand)}
.lb-arrow:hover,.lb-x:hover{color:var(--ink);border-color:var(--line-hot)}
.lb-arrow:active{background:var(--brand);color:#0b0a0d}
.lb-arrow:focus-visible,.lb-x:focus-visible,.lb-pip:focus-visible,.lb-retry:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.lb-x{margin-left:.3em}
/* one square a board, the lit one is the board on show */
.lb-pips{flex:0 0 auto;display:flex;justify-content:center;gap:.5em;padding:0 1em .7em}
.lb-pip{width:.9em;height:.9em;min-width:12px;min-height:12px;padding:0;border:1px solid var(--line-hot);border-radius:0;background:none;cursor:pointer;position:relative}
.lb-pip::after{content:"";position:absolute;inset:-.5em -.25em}
.lb-pip.on{background:var(--brand);border-color:var(--brand)}

.lb-cols,.lb-row{display:grid;grid-template-columns:var(--lb-rw) minmax(0,1fr) auto;column-gap:.8em;align-items:center;padding:0 1em 0 .8em}
.lb-cols{flex:0 0 auto;height:2.4em;font-family:var(--display);font-size:.6em;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-faint);border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  grid-template-columns:calc(var(--lb-rw) / .6) minmax(0,1fr) auto;column-gap:1.33em;padding:0 1.67em 0 1.33em}
.lb-cols .lb-rank{font-size:1em;text-align:center;color:inherit}
.lb-cols .lb-val{font-size:1em;color:inherit}
.lb-val{text-align:right}

.lb-view{flex:1 1 auto;min-height:0;position:relative}
/* The rows scroll under a fixed header and the pinned row. No scrollbar is
   drawn; a fade at an edge says there is more beyond it, and goes when there
   is not. The fades are two strips laid over the list, not a mask on it: a
   mask makes the browser repaint the whole list on every frame of a scroll. */
.lb-list{position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;
  -webkit-overflow-scrolling:touch;overscroll-behavior:contain;scrollbar-width:none}
.lb-list::-webkit-scrollbar{display:none;width:0;height:0}
.lb-view::before,.lb-view::after{content:"";position:absolute;left:0;right:0;height:2.6em;z-index:1;pointer-events:none;
  opacity:1;transition:opacity .2s linear}
.lb-view::before{top:0;background:linear-gradient(to bottom,var(--panel-solid),transparent)}
.lb-view::after{bottom:0;background:linear-gradient(to top,var(--panel-solid),transparent)}
.lb-view.lb-at-top::before,.lb-view.lb-at-end::after{opacity:0}
/* with the viewer's row pinned to an edge, the fade starts above (or below) it */
.lb-view.lb-pinned-top::before{top:var(--lb-h)}
.lb-view.lb-pinned-bot::after{bottom:var(--lb-h)}
.lb-rows{position:relative;min-height:100%}
/* a row that has not arrived yet: grey bars, exactly where it will be */
.lb-rows::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.5;
  background:
    linear-gradient(to bottom,var(--panel-solid) 0 36%,transparent 36% 64%,var(--panel-solid) 64% calc(100% - 1px),var(--line) calc(100% - 1px)) 0 0/100% var(--lb-h),
    linear-gradient(to right,transparent 0 1.6em,var(--line) 1.6em 3em,transparent 3em calc(var(--lb-rw) + 1.6em),var(--line) calc(var(--lb-rw) + 1.6em) 46%,transparent 46% calc(100% - 5.5em),var(--line) calc(100% - 5.5em) calc(100% - 1em),transparent calc(100% - 1em));
  animation:lbWait 1.3s steps(4,end) infinite alternate}
@keyframes lbWait{from{opacity:.28}to{opacity:.6}}
.lb-row{position:absolute;left:0;right:0;height:var(--lb-h);background:var(--panel-solid);
  box-shadow:inset 0 -1px 0 var(--line);color:var(--ink)}
.lb-rank{font-family:var(--display);font-size:.8em;color:var(--ink-dim);text-align:center;font-variant-numeric:tabular-nums;white-space:nowrap}
.lb-name{display:flex;align-items:center;gap:.5em;min-width:0}
.lb-name b{font-weight:600;font-size:.95em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lb-val{font-family:var(--display);font-size:.95em;color:var(--ink);font-variant-numeric:tabular-nums;white-space:nowrap}
.lb-val small{font-size:.62em;margin-left:.35em;color:var(--ink-faint);letter-spacing:.06em}
.lb-val.lb-dim{font-family:var(--font);font-size:.72em;color:var(--ink-dim)}
/* the podium: a medal square behind the rank, and the number in its colour */
.lb-top .lb-rank{justify-self:center;width:2.1em;height:2.1em;display:grid;place-items:center;color:#14110f;
  box-shadow:inset -2px -2px 0 rgba(0,0,0,.28),inset 2px 2px 0 rgba(255,255,255,.35)}
.lb-top1 .lb-rank{background:var(--lb-gold)}
.lb-top2 .lb-rank{background:var(--lb-silver)}
.lb-top3 .lb-rank{background:var(--lb-bronze)}
.lb-top1 .lb-val{color:var(--lb-gold)}
.lb-top2 .lb-val{color:var(--lb-silver)}
.lb-top3 .lb-val{color:var(--lb-bronze)}
.lb-top1{background:linear-gradient(90deg,rgba(245,197,66,.13),rgba(245,197,66,0) 60%),var(--panel-solid)}
/* the viewer's own row */
.lb-me{background:linear-gradient(rgba(245,158,11,.16),rgba(245,158,11,.16)),var(--panel-solid);
  box-shadow:inset 4px 0 0 var(--brand),inset 0 -1px 0 var(--line)}
.lb-me:not(.lb-top) .lb-rank{color:var(--ink)}
.lb-you{flex:0 0 auto;font-style:normal;font-family:var(--display);font-size:.52em;letter-spacing:.12em;text-transform:uppercase;
  padding:.25em .5em;background:var(--brand);color:#0b0a0d}
/* ...and its copy, pinned to the edge it lies beyond */
.lb-pin{position:absolute;left:0;right:0;height:var(--lb-h);z-index:2;pointer-events:none;visibility:hidden}
.lb-pin.on{visibility:visible}
.lb-pin .lb-row{top:0}
.lb-pin-top{top:0}
.lb-pin-top .lb-row{box-shadow:inset 4px 0 0 var(--brand),0 2px 0 var(--brand),0 8px 12px -4px rgba(0,0,0,.6)}
.lb-pin-bot{bottom:0}
.lb-pin-bot .lb-row{box-shadow:inset 4px 0 0 var(--brand),0 -2px 0 var(--brand),0 -8px 12px -4px rgba(0,0,0,.6)}

.lb-state{position:absolute;inset:0;z-index:3;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.7em;
  padding:1.5em;text-align:center}
.lb-overlay:not([data-state="loading"]) .lb-state{background:var(--panel-solid)}
.lb-state-t{margin:0;font-family:var(--display);font-size:1.05em;letter-spacing:.04em;color:var(--ink)}
.lb-state-p{margin:0;max-width:26em;font-size:.8em;line-height:1.5;color:var(--ink-dim)}
.lb-state-t.lb-loading{font-size:.7em;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-dim);background:var(--panel-solid);padding:.5em .9em;border:1px solid var(--line)}
.lb-loading::after{content:"...";display:inline-block;width:1.6em;text-align:left;overflow:hidden;vertical-align:bottom;animation:lbDots 1s steps(4,end) infinite}
@keyframes lbDots{from{width:0}to{width:1.6em}}
.lb-retry{font-family:var(--display);font-size:.7em;letter-spacing:.1em;text-transform:uppercase;cursor:pointer;
  padding:.8em 1.3em;min-height:40px;color:#0b0a0d;background:var(--brand);border:0;border-radius:0;box-shadow:3px 3px 0 rgba(0,0,0,.45)}
.lb-foot{flex:0 0 auto;margin:0;padding:.7em 1em;border-top:1px solid var(--line);
  font-family:var(--display);font-size:.52em;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-faint);text-align:center}
/* a finger needs no key hints */
@media (hover:none) and (pointer:coarse){.lb-foot{display:none}}
@media (max-width:560px){
  .lb-overlay{padding:0}
  .lb-card{width:100%;height:100%;border-width:0;font-size:15px}
  .lb-title{font-size:1.2em}
}
@media (prefers-reduced-motion:reduce){
  .lb-rows::before,.lb-loading::after{animation:none}
  .lb-view::before,.lb-view::after{transition:none}
}
[data-theme="light"] .lb-overlay{background:rgba(43,37,54,.5)}
[data-theme="light"] .lb-card{--lb-gold:#b7860b;--lb-silver:#6b7280;--lb-bronze:#a35a1c}
[data-theme="light"] .lb-top .lb-rank{color:#fff}
[data-theme="light"] .lb-arrow,[data-theme="light"] .lb-x{background:rgba(43,37,54,.04)}

/* The public-rooms dialog borrows the card's frame and nothing else of it. */
.rooms-overlay .lb-card{width:min(560px,96vw);height:auto;max-height:88vh;font-size:1rem;overflow:visible}
.rooms-overlay .lb-head{justify-content:space-between;gap:0;padding:.9rem 1rem;border-bottom:1px solid var(--line)}
.rooms-overlay .lb-head h2{margin:0;font-family:var(--display);font-size:1rem;letter-spacing:.02em;color:var(--ink)}
.rooms-overlay .lb-x{display:block;width:auto;height:auto;min-width:0;min-height:0;margin:0;background:none;border:0;font-size:1.4rem}

/* ── public rooms: "play with random players" ──────────────────────────── */
.random-cta{display:flex;flex-direction:column;align-items:center;gap:.3rem;margin:1.4rem 0 .4rem}
.random-cta .ghost-btn{font-family:var(--display);letter-spacing:.02em}
.sheet-btn.toggle-btn.on{border-color:var(--brand);background:rgba(245,158,11,.18);color:var(--ink);font-weight:700}

.rooms-body{flex:1 1 auto;overflow-y:auto;padding:.55rem .7rem;display:flex;flex-direction:column;gap:.5rem}
.rooms-empty{margin:1.4rem .4rem;text-align:center;color:var(--ink-dim);font-size:.9rem;line-height:1.5}
.room-row{display:flex;align-items:center;justify-content:space-between;gap:.8rem;width:100%;
  text-align:left;background:var(--panel);border:1px solid var(--line);color:var(--ink);
  padding:.7rem .8rem;cursor:pointer;transition:border-color .12s,transform .06s}
.room-row:hover{border-color:var(--line-hot)}
.room-row:active{transform:scale(.995)}
.room-row.full{opacity:.55;cursor:default}
.room-main{display:flex;flex-direction:column;gap:.15rem;min-width:0}
.room-game{font-family:var(--display);font-size:.9rem;letter-spacing:.02em;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.room-host{font-size:.75rem;color:var(--ink-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.room-meta{display:flex;align-items:center;gap:.7rem;flex:0 0 auto}
.room-count{font-family:var(--display);font-size:.8rem;color:var(--ink-dim)}
.room-cta{font-size:.8rem;font-weight:700;color:var(--brand-2)}
.room-row.full .room-cta{color:var(--ink-faint)}
.rooms-foot{display:flex;align-items:center;justify-content:space-between;gap:.7rem;
  padding:.55rem .7rem;border-top:1px solid var(--line);flex:0 0 auto}
.rooms-hint{font-size:.72rem;color:var(--ink-faint)}

/* ══════════════ unified adjustable controller ══════════════
   One shared, player-adjustable geometry for every gameplay pad: the
   left directional cluster and the right action cluster are each moved
   (translate) and sized (scale) by CSS vars saved per device. Emitters
   are untouched — this only positions the existing controls. */
#pad-live{--ctlL-x:0px;--ctlL-y:0px;--ctlL-s:1;--ctlR-x:0px;--ctlR-y:0px;--ctlR-s:1;}
#pad-live .ctl-cluster[data-ctl="L"]{
  transform:translate(var(--ctlL-x),var(--ctlL-y)) scale(var(--ctlL-s));
  transform-origin:left bottom;}
#pad-live .ctl-cluster[data-ctl="R"]{
  transform:translate(var(--ctlR-x),var(--ctlR-y)) scale(var(--ctlR-s));
  transform-origin:right bottom;}

/* edit mode */
.ctl-edit-bar{display:flex;align-items:center;justify-content:space-between;gap:.6rem;
  flex-wrap:wrap;background:var(--panel);border:1px solid var(--brand);border-radius:12px;
  padding:.5rem .65rem;box-shadow:0 6px 22px rgba(0,0,0,.28)}
.ctl-edit-tip{font-size:.8rem;color:var(--ink);letter-spacing:.02em}
.ctl-edit-tools{display:flex;align-items:center;gap:.4rem}
.ctl-edit-lbl{font-size:.72rem;color:var(--ink-dim);letter-spacing:.08em;text-transform:uppercase}
.ctl-edit-btn{background:var(--panel-2,var(--panel));border:1px solid var(--line);color:var(--ink);
  border-radius:9px;min-width:40px;height:38px;padding:0 .7rem;font-size:1rem;font-weight:700;
  cursor:pointer;transition:border-color .12s,transform .06s;font-family:inherit}
.ctl-edit-btn:hover{border-color:var(--line-hot)}
.ctl-edit-btn:active{transform:scale(.96)}
.ctl-edit-btn.done{border-color:var(--brand);color:var(--brand-2,var(--brand))}

#pad-live.ctl-edit .ctl-cluster{outline:2px dashed var(--brand);outline-offset:6px;
  border-radius:16px;touch-action:none;position:relative}
#pad-live.ctl-edit .ctl-cluster[data-sel="1"]{outline-style:solid;outline-color:var(--brand-2,var(--brand));
  background:rgba(245,158,11,.10)}
/* while editing, the inner controls must not swallow the drag or fire the game */
#pad-live.ctl-edit .ctl-cluster *{pointer-events:none}
#pad-live.ctl-edit .ctl-cluster{cursor:grab}
#pad-live.ctl-edit .ctl-cluster:active{cursor:grabbing}

/* ══════════════ screen transitions (anim.js) ══════════════
   transform + opacity ONLY. Animating width/height/top/left here
   would lay out the whole shell — including the game canvases —
   every frame. Durations match MPG.anim.D; keep them in step.

   The global prefers-reduced-motion rule above collapses every
   duration to .01ms, and anim.js additionally skips the classes
   outright, so there is no transient transform for anyone who
   asked for less motion. The ::view-transition pseudo-elements
   are NOT matched by that `*` rule, so they are handled again
   explicitly at the bottom of this block. */

@keyframes viewIn      { from { opacity:0; transform:translate3d(0,10px,0) scale(.994) } to { opacity:1; transform:none } }
@keyframes bodyIn      { from { opacity:0; transform:translate3d(0,8px,0)  } to { opacity:1; transform:none } }
@keyframes bodyInBack  { from { opacity:0; transform:translate3d(0,-8px,0) } to { opacity:1; transform:none } }
@keyframes cardIn      { from { opacity:0; transform:translate3d(0,14px,0) scale(.97) } to { opacity:1; transform:none } }
@keyframes seatIn      { from { opacity:0; transform:translate3d(-10px,0,0) } to { opacity:1; transform:none } }
@keyframes seatOpen    { from { opacity:.25 } to { opacity:1 } }
@keyframes gateIn      { from { opacity:0; transform:translate3d(0,12px,0) scale(.97) } to { opacity:1; transform:none } }

/* A view that just became active (fallback path, and any browser
   without View Transitions). */
.view.v-in{animation:viewIn .48s cubic-bezier(.45,0,.25,1) both}

/* lobby ↔ stage, inside the screen shell */
.screen-body.b-in     {animation:bodyIn .2s cubic-bezier(.22,.7,.3,1) both}
.screen-body.b-in-back{animation:bodyInBack .2s cubic-bezier(.22,.7,.3,1) both}

/* the game picker, revealed in sequence; delay is set inline per card */
.game-card.c-in{animation:cardIn .24s cubic-bezier(.22,.7,.3,1) both}

/* the coin before a game opens (token.js): an overlay of chunky pixels over
   the cabinet, and the chosen card lighting up as the coin goes in. The
   delay is forced because the reveal above leaves one inline on the card. */
#coin-fx{position:fixed;inset:0;z-index:60;overflow:hidden;pointer-events:none}
#coin-fx[hidden]{display:none}
#coin-fx canvas{position:absolute;left:0;top:0;image-rendering:pixelated;image-rendering:crisp-edges}
.game-card.coin-lit{animation:coinLit .5s cubic-bezier(.2,.7,.3,1) both;animation-delay:0s!important}
@keyframes coinLit{
  0%{box-shadow:0 0 0 2px #ffe08a,0 0 46px 8px rgba(255,196,70,.8);filter:brightness(1.5) saturate(1.2)}
  100%{box-shadow:0 0 0 0 rgba(255,196,70,0),0 0 0 0 rgba(255,196,70,0);filter:none}
}

/* a real join / a seat actually opening up — never a peer message */
.seat.s-in  {animation:seatIn .18s cubic-bezier(.22,.7,.3,1) both}
.seat.s-out {animation:seatOpen .18s ease-out both}

/* the "up next" gate */
#gate.g-in{animation:gateIn .24s cubic-bezier(.22,.7,.3,1) both}

/* ── View Transitions, when the browser has them ──
   Used for top-level view swaps only. anim.js sets data-vt on
   <html> for the duration, so nothing here can fire during play.

   A split wipe: the new view sits still underneath while the old one,
   slightly blurred, parts along a leaning seam from the centre outwards.
   Going into a room the seam leans forward, coming back it leans the
   other way. --vt-wipe is registered so the mask stops can interpolate. */
@property --vt-wipe{syntax:'<length-percentage>';inherits:false;initial-value:0px}
@media (prefers-reduced-motion:no-preference){
  html[data-vt]::view-transition-old(root),
  html[data-vt]::view-transition-new(root){
    animation:none;mix-blend-mode:normal;opacity:1;
  }
  html[data-vt]::view-transition-new(root){z-index:0}
  html[data-vt]::view-transition-old(root){
    z-index:1;filter:blur(4px);
    --vt-lean:100deg;
    -webkit-mask-image:linear-gradient(var(--vt-lean),#000 0,#000 calc(50% - var(--vt-wipe)),#0000 calc(50% - var(--vt-wipe)),#0000 calc(50% + var(--vt-wipe)),#000 calc(50% + var(--vt-wipe)),#000 100%);
    mask-image:linear-gradient(var(--vt-lean),#000 0,#000 calc(50% - var(--vt-wipe)),#0000 calc(50% - var(--vt-wipe)),#0000 calc(50% + var(--vt-wipe)),#000 calc(50% + var(--vt-wipe)),#000 100%);
    -webkit-mask-size:100% 100%;mask-size:100% 100%;
    -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
    animation:vtSplit .48s cubic-bezier(.45,0,.25,1) both;
  }
  html[data-vt="back"]::view-transition-old(root){--vt-lean:80deg}
}
@keyframes vtSplit { from { --vt-wipe:0px } to { --vt-wipe:80% } }

/* Belt and braces: `*` does not match view-transition pseudo-elements. */
@media (prefers-reduced-motion:reduce){
  html[data-vt]::view-transition-old(root),
  html[data-vt]::view-transition-new(root){animation:none !important}
}

/* A player with an account wears the violet brand colour on their border.
   Last in the file so it outranks the lost-connection border in both themes.
   Violet, not amber: amber is already "selected" everywhere and sits too
   close to the orange of a lost connection in the room menu. */
.seat.acct,.mp-row.acct,.mp-row.lost.acct,[data-theme="light"] .mp-row.lost.acct{border-color:var(--brand-2)}
/* What the account is: an admin's border is red, a paid account's gold;
   admin wins. A pale, yellow gold, kept clear of the amber that means
   "selected" and of the orange of a lost connection. */
:root{--tier-admin:#f2545b;--tier-paid:#ffe066}
:root[data-theme="light"]{--tier-admin:#c81e2b;--tier-paid:#a67c00}
.seat.acct.paid,.mp-row.acct.paid,.mp-row.lost.acct.paid,[data-theme="light"] .mp-row.lost.acct.paid{border-color:var(--tier-paid)}
.seat.acct.admin,.mp-row.acct.admin,.mp-row.lost.acct.admin,[data-theme="light"] .mp-row.lost.acct.admin{border-color:var(--tier-admin)}

/* ══════════════ how-to panel (howto.js) ══════════════
   Shown at the ready step before a game: the lines on how to play, and a
   pixel-art picture of how to hold the phone. The picture is a 64×48
   canvas, so every size below is a WHOLE multiple of that: anything else
   would smear its pixels. It carries its own dark ground, so it looks
   the same in both themes. */
.howto{display:flex;align-items:center;justify-content:center;gap:.8rem;text-align:left;min-width:0}
.howto-fig{flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:.4rem;margin:0}
.howto-art{
  display:block;width:128px;height:96px;flex:0 0 auto;
  image-rendering:crisp-edges;image-rendering:pixelated;
  background:#16131a;border:2px solid var(--line-hot);box-sizing:content-box;
}
.howto-cap{margin:0;max-width:132px;text-align:center;font-family:var(--mono);font-size:.6rem;line-height:1.4;color:var(--brand)}
[data-theme="light"] .howto-cap{color:#8a5200}
.howto-text{min-width:0;display:flex;flex-direction:column;gap:.5rem}
.howto-lines{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.4rem}
.howto-lines li{position:relative;padding-left:.85rem;font-size:.84rem;line-height:1.35;color:var(--ink)}
.howto-lines li::before{content:"";position:absolute;left:0;top:.42em;width:5px;height:5px;background:var(--brand)}
.howto-keys{margin:0;font-family:var(--mono);font-size:.62rem;line-height:1.5;color:var(--ink-dim);border:1px dashed var(--line);padding:.35rem .5rem}
.howto-kb{display:flex;flex-direction:column;gap:.35rem;border-top:1px dashed var(--line);padding-top:.5rem}

/* a line only one kind of seat needs, and the phone's own way to the second page */
.howto-note{border-style:dotted}
.howto-more{align-self:flex-start;background:none;border:1px dashed var(--line);color:var(--brand);font:inherit;font-size:.9em;
  padding:.3em .8em;min-height:30px;cursor:pointer}
[data-theme="light"] .howto-more{color:#8a5200}

/* The panel is sized by two numbers on its box: --hf, its type in px, and
   --hk, how many times 64x48 its picture is drawn. howto.js (H.fit) steps
   them down until the panel fits; what is here is what it starts from. */
/* Nothing in it eases from one size to the next: it is measured the moment
   the numbers are set (and under prefers-reduced-motion every property has
   a 0.01 ms transition, which is enough to make that measurement the old size). */
.gate-howto,.gate-howto *,.ready-howto,.ready-howto *{transition:none !important}
/* on the phone: picture over the lines, NEXT underneath */
#lay-ready.has-howto{gap:.5rem}
#lay-ready.has-howto .ready-info{padding:0 .1rem}
#lay-ready.has-howto .ready-title{margin:.15rem 0 .45rem;font-size:1.15rem}
#lay-ready.has-howto .ready-btn{min-height:clamp(64px,13vh,120px)}
.ready-howto{flex-direction:column;gap:.55rem;font-size:var(--hf,13.4px)}
.ready-howto .howto-art{width:calc(64px * var(--hk,2));height:calc(48px * var(--hk,2))}
.ready-howto .howto-cap{max-width:none}
.ready-howto .howto-lines{gap:.3em}
.ready-howto .howto-lines li{font-size:1em;line-height:1.32}
@media (max-height:600px) and (orientation:portrait){
  #lay-ready.has-howto .ready-kicker{display:none}
}
@media (orientation:landscape) and (max-height:520px){
  #lay-ready.has-howto{gap:.7rem}
  #lay-ready.has-howto .ready-info{flex:1 1 auto;align-self:stretch}
  #lay-ready.has-howto .ready-btn{flex:0 0 26%;min-height:clamp(70px,40vh,120px)}
  #lay-ready.has-howto .ready-kicker{display:none}
  #lay-ready.has-howto .ready-title{font-size:1rem;margin:0 0 .3rem}
  .ready-howto{flex-direction:row;align-items:center;justify-content:flex-start;gap:.7rem}
  .ready-howto .howto-cap{max-width:132px}
}

/* on the big screen: read from across the room, beside who is ready */
.gate-card.has-howto{max-width:min(1500px,94vw)}
.gate-howto{font-size:var(--hf,16px);gap:1.3em;margin:.2em 0 .3em}
.gate-howto .howto-art{width:calc(64px * var(--hk,3));height:calc(48px * var(--hk,3))}
.gate-howto .howto-cap{max-width:calc(64px * var(--hk,3) + 4px);font-size:clamp(9px,.42em,15px)}
/* a controller, drawn as one is held, with what each button does on it (howto.js H.padPicture) */
.howto-pad{align-self:flex-start;max-width:100%;box-sizing:border-box;display:flex;flex-direction:column;gap:.3em;font-family:var(--mono);font-size:.62rem;line-height:1.2;color:var(--ink)}
.hp-top{display:flex;flex-wrap:wrap;justify-content:space-between;gap:.4em 2em;padding:0 1em}
.hp-body{display:flex;flex-wrap:wrap;align-items:center;gap:.6em 1.4em;border:2px solid var(--line);border-radius:1.4em 1.4em 2.6em 2.6em;padding:.5em 1em;background:rgba(0,0,0,.18)}
.hp-left{display:flex;flex-direction:column;gap:.45em}
.hp-face{display:grid;grid-template-columns:auto auto auto;grid-template-rows:auto auto auto;gap:.15em .4em;align-items:center;justify-items:center}
.hp-y{grid-area:1/2}.hp-x{grid-area:2/1}.hp-b{grid-area:2/3}.hp-a{grid-area:3/2}
.hp-key{display:inline-flex;align-items:center;gap:.35em;white-space:nowrap}
.hp-cap{display:inline-flex;align-items:center;justify-content:center;min-width:1.7em;height:1.7em;padding:0 .3em;border:2px solid var(--ink-dim);border-radius:.85em;font-weight:700;color:var(--ink)}
.hp-top .hp-cap{border-radius:.3em}
.hp-left .hp-cap{border-radius:.2em}
.hp-act{font-weight:700;letter-spacing:.06em;color:var(--brand)}
[data-theme="light"] .hp-act{color:#8a5200}
.gate-howto .howto-pad{font-size:clamp(10px,.5em,17px)}
.gate-howto .howto-text{gap:.6em;max-width:40em}
.gate-howto .howto-text.wide{max-width:52em}
/* a screen with no height to spare spends its width: lines run on, not down */
@media (max-height:520px){.gate-howto .howto-text,.gate-howto .howto-text.wide{max-width:none}}
.gate-howto .howto-lines{gap:.42em}
/* what the game is, above how to play it: the cabinet's cards no longer say it */
.gate-howto .howto-lead{margin:0;font-size:.82em;line-height:1.4;color:var(--ink-dim)}
.gate-howto .howto-lines li{font-size:1em;line-height:1.28;padding-left:1.1em}
.gate-howto .howto-lines li::before{width:.4em;height:.4em;top:.45em}
.gate-howto .howto-keys{font-size:clamp(10px,.5em,17px);padding:.4em .7em}
/* The buttons of each seat at the screen. Keyboards, two or more: a table,
   an action a line and a seat a column. The rest (one keyboard, the
   controllers, what means "ready"): a row each, two columns of them once
   there are many. */
.gate-howto .howto-kb{gap:.5em;padding-top:.5em}
.gate-howto .kl-rows{display:grid;grid-template-columns:minmax(0,1fr);gap:.3em 1.4em}
.gate-howto .kl-rows.many{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
.gate-howto .kl-rows:empty{display:none}
.gate-howto .howto-kb .kl-row,.gate-howto .kl-table{font-size:clamp(11px,.58em,21px)}
.gate-howto .howto-kb .kl-row{gap:.3em .35em;min-width:0}
.gate-howto .howto-kb .kcap{font-size:.62em}
.gate-howto .howto-kb .kl-act{margin-right:.7em;color:var(--ink-dim)}
.gate-howto .howto-kb .kl-name{font-size:inherit}
.gate-howto .kl-table{display:grid;grid-template-columns:minmax(0,auto) repeat(var(--cols,2),auto);gap:.22em 1.2em;
  align-items:center;justify-content:start}
.gate-howto .kl-table .kl-name{margin:0;text-align:center;white-space:nowrap}
.gate-howto .kl-table .kl-act{margin:0}
.gate-howto .kl-table .kl-c{text-align:center}
@media (min-width:1800px) and (min-height:980px){
  .gate-card.has-howto .gate-players:not(.many) .gate-who{font-size:1.15rem;padding:.45rem 1rem}
  .gate-card.has-howto .gate-players:not(.many) .gate-who span{font-size:.9rem}
}
@media (max-width:720px){
  .gate-howto{flex-direction:column;gap:.7em}
  .gate-howto .howto-cap{max-width:none}
  .gate-howto .kl-rows.many{grid-template-columns:minmax(0,1fr)}
  .gate-howto .kl-table{gap:.22em .6em}
}
}

/* ══════════════ CONTROLLERS ══════════════
   A controller's seat, the focus ring and hint bar of padnav.js, the
   selected game in full, and the controller tester (gamepad-test.js). */
/* a controller seat names itself: the pad and whose make it is */
.seat-pad{font-family:var(--mono);font-size:.68rem;letter-spacing:.04em;color:var(--ink-dim);
  border:1px solid currentColor;padding:.08rem .32rem;white-space:nowrap;flex:0 0 auto}
.seat-pad.lost{color:var(--p4);border-style:dashed}
/* a button pressed on a seated controller lights its seat: which is which */
.seat.ping{animation:seatPing .5s steps(5,end)}
@keyframes seatPing{0%{box-shadow:0 0 0 4px currentColor;background:rgba(255,255,255,.14)}100%{box-shadow:0 0 0 0 transparent}}
@media (prefers-reduced-motion:reduce){.seat.ping{animation:none}}
.join-pad{margin:.9rem 0 0;font-family:var(--mono);font-size:.66rem;line-height:1.4;letter-spacing:.04em;color:var(--ink-faint)}
@media (max-height:820px),(max-width:860px){.join-pad{display:none}}

/* ── the focus ring ──
   Only while the last input came from a controller or the keyboard
   (html[data-input], set by padnav.js): a mouse or a finger sees the
   page exactly as before. Hard-edged and thick enough to find from a
   sofa, with a second, fainter edge that beats slowly. */
html[data-input="pad"] .nav-focus,html[data-input="keys"] .nav-focus{
  outline:3px solid var(--brand) !important;outline-offset:3px;
  box-shadow:0 0 0 8px rgba(245,158,11,.30);animation:navFocus 1.1s steps(2,end) infinite;
  scroll-margin:1rem 1rem calc(var(--nav-h) + 1rem);
}
@keyframes navFocus{50%{box-shadow:0 0 0 8px rgba(245,158,11,.12)}}
@media (prefers-reduced-motion:reduce){html[data-input] .nav-focus{animation:none}}
/* a card of the cabinet sits in a frame that clips: keep the ring inside it */
html[data-input="pad"] .game-card.nav-focus,html[data-input="keys"] .game-card.nav-focus{outline-offset:2px;box-shadow:none;animation:none}
html[data-input="pad"] .seg button.nav-focus,html[data-input="keys"] .seg button.nav-focus,
html[data-input="pad"] .lb-pip.nav-focus,html[data-input="keys"] .lb-pip.nav-focus{outline-offset:1px;box-shadow:none;animation:none}
html[data-input="pad"] .pv-card.nav-focus,html[data-input="keys"] .pv-card.nav-focus{border-color:var(--line-hot);color:var(--ink)}
[data-theme="light"][data-input] .nav-focus{box-shadow:0 0 0 8px rgba(180,83,9,.22)}

/* ── the hint bar ──
   One strip along the bottom, there only with the focus ring. What it
   lies over is moved up by its height (--nav-h), so it covers nothing:
   the lobby, the landing page and every overlay end above it. It is
   never up during a round. */
:root{--nav-h:0px}
html.nav-on{--nav-h:2.6rem}
.nav-hints{
  position:fixed;left:0;right:0;bottom:0;height:2.6rem;z-index:130;
  display:flex;align-items:center;justify-content:flex-end;gap:.3rem 1.3rem;
  padding:0 max(clamp(.7rem,2vw,1.6rem),env(safe-area-inset-right,0px)) 0 clamp(.7rem,2vw,1.6rem);
  background:var(--panel-solid);border-top:2px solid var(--line-hot);
  font-family:var(--mono);font-size:.8rem;color:var(--ink);white-space:nowrap;overflow:hidden;pointer-events:none;
}
.nav-hints[hidden]{display:none}
.nh{display:inline-flex;align-items:center;gap:.4rem;flex:0 0 auto}
.nh-hold{font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-dim)}
.nh-t{color:var(--ink-dim)}
/* a glyph: a square cap with a hard shadow, like every button here */
.ng{
  position:relative;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
  min-width:1.55rem;height:1.55rem;padding:0 .3rem;box-sizing:border-box;overflow:hidden;
  font:inherit;font-size:.72rem;line-height:1;color:#0b0a0d;background:var(--ink);
  border:2px solid #0b0a0d;box-shadow:2px 2px 0 rgba(0,0,0,.55);
}
.ng-face{width:1.55rem;padding:0;font-family:system-ui,sans-serif;font-weight:700;font-size:.8rem}
.ng-pill{padding:0 .4rem;background:var(--ink-dim)}
.ng-key{padding:0 .45rem;background:var(--ink-dim);font-family:var(--mono)}
/* the face buttons in their makers' colours; a Switch pad's are plain */
.ng-xbox.ng-a{background:#6fd36b}.ng-xbox.ng-b{background:#ef5f5f}.ng-xbox.ng-x{background:#62a8f5}.ng-xbox.ng-y{background:#f3cf4a}
.ng-playstation.ng-a{background:#8fb4ff}.ng-playstation.ng-b{background:#ff8a8a}.ng-playstation.ng-x{background:#f3a8e0}.ng-playstation.ng-y{background:#7fe0b6}
.ng-generic.ng-a{background:#6fd36b}.ng-generic.ng-b{background:#ef5f5f}
/* the d-pad: a cross drawn out of two bars */
.ng-dpad{width:1.55rem;padding:0;background:none;border:0;box-shadow:none;overflow:visible}
.ng-dpad i,.ng-dpad i::after{position:absolute;background:var(--ink);box-shadow:2px 2px 0 rgba(0,0,0,.55)}
.ng-dpad i{left:.52rem;top:.1rem;width:.5rem;height:1.35rem}
.ng-dpad i::after{content:"";left:-.42rem;top:.42rem;width:1.35rem;height:.5rem;box-shadow:none}
/* a button that has to be held fills from the left while it is */
.ng-hold::after{content:"";position:absolute;left:0;bottom:0;height:100%;width:calc(var(--hold,0) * 100%);background:rgba(11,10,13,.38)}
@media (max-width:640px){.nav-hints{font-size:.68rem;gap:.3rem .7rem}.nh-hold{display:none}}
@media (min-width:1800px) and (min-height:980px){
  html.nav-on{--nav-h:3.1rem}
  .nav-hints{height:3.1rem;font-size:.98rem;gap:.3rem 1.7rem}
  .ng{min-width:1.9rem;height:1.9rem;font-size:.86rem}
  .ng-face,.ng-dpad{width:1.9rem}
  .ng-face{font-size:.98rem}
  .ng-dpad i{left:.66rem;top:.14rem;width:.58rem;height:1.62rem}
  .ng-dpad i::after{left:-.52rem;top:.52rem;width:1.62rem;height:.58rem}
}
/* everything the bar lies over ends above it */
html.nav-on #view-screen .screen-shell:not(.playing){height:calc(100dvh - var(--nav-h))}
/* the landing scrolls as a page: its end stays clear of the bar, and so
   does whatever the controller scrolls into view */
html.nav-on{scroll-padding-bottom:var(--nav-h)}
html.nav-on #view-landing.active{border-bottom:var(--nav-h) solid transparent}
html.nav-on .sheet,html.nav-on .lb-overlay,html.nav-on .pt-overlay,html.nav-on .rebind,html.nav-on .sing-modal{bottom:var(--nav-h)}
html.nav-on .gate-overlay{padding-bottom:calc(.7rem + var(--nav-h))}
/* A toast while the bar is up goes INTO the strip, left of the hints, on
   one line, the newest first: that strip is reserved, so there is nothing
   under it to cover. Where the hints leave no room for one (--nav-free,
   measured by padnav.js) the rail sits just above the bar instead. */
html.nav-on .toast-rail{bottom:.42rem;left:clamp(.7rem,2vw,1.6rem);z-index:131;display:flex;flex-direction:row-reverse;justify-content:flex-end;
  gap:.5rem;max-width:var(--nav-free,40vw);overflow:hidden}
html.nav-on .toast-rail .toast{flex:0 0 auto;max-width:100%;padding:.3rem .8rem;font-size:.76rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
html.nav-on.nav-tight .toast-rail{bottom:calc(.6rem + var(--nav-h));display:grid;max-width:none;overflow:visible}
html.nav-on.nav-tight .toast-rail .toast{white-space:normal}
@media (min-width:1800px) and (min-height:980px){html.nav-on .toast-rail{bottom:.5rem}html.nav-on .toast-rail .toast{font-size:.9rem}}
/* over a round a toast is the arcade talking across the game's picture:
   smaller, and never wider than a third of it */
.screen-shell.playing .toast-rail .toast{font-size:.72rem;padding:.3rem .7rem;max-width:min(34rem,36vw)}
/* the result screen's lit button wears the same ring as everything else */
html[data-input="pad"] .ov-actions .sel,html[data-input="keys"] .ov-actions .sel{outline-width:3px;box-shadow:0 0 0 8px rgba(245,158,11,.30)}
/* the board's own line of keys says the same thing less exactly */
html.nav-on .lb-foot{display:none}
/* the room menu, read from a sofa */
@media (min-width:1800px) and (min-height:980px){
  .room-sheet .sheet-card{max-width:1280px}
  .room-sheet .menu-players{grid-template-columns:repeat(auto-fill,minmax(380px,1fr))}
  .room-sheet .sheet-btn{font-size:1.1rem;padding:.75rem 1.1rem}
  .room-sheet .mp-row{font-size:1.08rem}
  .room-sheet .mp-kick,.room-sheet .mp-keys{font-size:.82rem;padding:.3rem .6rem}
  .room-sheet .sheet-label{font-size:.84rem}
  .room-sheet .sheet-fine,.room-sheet .sheet-note{font-size:.9rem}
  .room-sheet .seg.wide button{font-size:1rem;padding:.75rem .5rem}
}

/* ── the selected game in full (screen.js paintInfo) ──
   Under the cards, always the same height so the grid above never
   moves: the name, who can play and on what (and what it is best
   with), the whole description, and what is still missing. */
.game-info{
  flex:0 0 auto;display:grid;grid-template-columns:auto minmax(0,1fr);gap:.2rem 1rem;align-items:baseline;align-content:start;
  height:5rem;margin:.3rem 0 0;padding:.5rem .9rem;overflow:hidden;
  border:1px solid var(--line);background:var(--panel);
}
.gi-name{font-family:var(--display);font-weight:400;font-size:1rem;letter-spacing:.06em;text-transform:uppercase;color:var(--brand)}
.gi-facts{font-family:var(--mono);font-size:.72rem;color:var(--ink-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gi-desc{grid-column:1/-1;font-size:.92rem;line-height:1.4;color:var(--ink);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.gi-no{grid-column:1/-1;font-family:var(--mono);font-size:.7rem;color:var(--p4)}
.game-info:has(.gi-no) .gi-desc{-webkit-line-clamp:1}
.lobby-games:has(.game-info){gap:.55rem}
.lobby-games .fit-legend{margin-top:0}
@media (max-height:700px),(max-width:860px){.game-info{display:none}}
@media (min-width:1800px) and (min-height:980px){
  .game-info{height:6rem}
  .gi-name{font-size:1.2rem}.gi-facts{font-size:.86rem}.gi-desc{font-size:1.1rem}.gi-no{font-size:.82rem}
}
/* the cabinet's game under the focus on the landing page, in words */
.pv-info{display:none;margin:.6rem auto 0;max-width:70ch;min-height:2.7em;text-align:center;font-size:.86rem;line-height:1.35;color:var(--ink-dim)}
html[data-input="pad"] .pv-info,html[data-input="keys"] .pv-info{display:block}
.pv-card[role="button"]{cursor:pointer}

/* ── the controller tester ──
   Sized in em off the sheet, so a TV gets the same picture larger. */
.pt-overlay{position:fixed;inset:0;z-index:95;display:flex;align-items:center;justify-content:center;
  padding:clamp(.4rem,2vh,1.4rem) clamp(.4rem,2vw,1.4rem);background:rgba(6,5,8,.78);overflow:auto}
.pt-overlay[hidden]{display:none}
[data-theme="light"] .pt-overlay{background:rgba(40,34,26,.55)}
.pt-sheet{width:fit-content;max-width:100%;max-height:100%;overflow:auto;margin:auto;font-size:.95rem;
  background:var(--panel-solid);border:2px solid var(--line-hot);padding:.7em .8em .8em;color:var(--ink)}
.pt-none .pt-sheet{width:min(34em,100%)}
.pt-head{display:flex;align-items:center;gap:.8em;margin-bottom:.6em}
.pt-head h2{margin:0;font-family:var(--display);font-weight:400;font-size:1.05em;letter-spacing:.08em;text-transform:uppercase}
.pt-sub{flex:1 1 auto;min-width:0;font-family:var(--mono);font-size:.68em;color:var(--ink-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pt-none .pt-sub{visibility:hidden}
.pt-x{flex:0 0 auto;padding:.25em .6em}
.pt-empty{display:flex;flex-direction:column;align-items:center;gap:.5em;text-align:center;padding:1.4em 1em 1.6em;border:1px dashed var(--line)}
.pt-empty[hidden]{display:none}
.pt-empty b{font-family:var(--display);font-weight:400;font-size:1.15em;letter-spacing:.04em}
.pt-empty span{color:var(--ink-dim);max-width:44ch;line-height:1.45;font-size:.88em}
.pt-pulse{width:1em;height:1em;background:var(--brand);animation:ptPulse 1.3s steps(4,end) infinite}
@keyframes ptPulse{50%{opacity:.25;transform:scale(1.6)}}
@media (prefers-reduced-motion:reduce){.pt-pulse{animation:none}}

/* cards of one width, as many to a row as fit; the sheet is as wide as
   its cards, so one controller is a small panel and four fill a TV */
.pt-grid{display:flex;flex-wrap:wrap;justify-content:center;gap:.6em}
.pt-card{--c:var(--ink-faint);flex:1 1 17.5em;max-width:21em;min-width:0;display:flex;flex-direction:column;gap:.5em;padding:.55em .6em .6em;
  border:2px solid var(--line);border-top:.3em solid var(--c);background:rgba(255,255,255,.02)}
/* the card of the controller in use */
.pt-card.hot{border-color:var(--c);background:rgba(255,255,255,.06);box-shadow:0 0 0 .2em var(--c)}
.pt-top{display:flex;align-items:center;flex-wrap:wrap;gap:.3em .45em;min-width:0}
.pt-num{flex:0 0 auto;width:1.5em;height:1.5em;display:grid;place-items:center;background:var(--c);color:#0b0a0d;
  font-style:normal;font-family:var(--mono);font-size:.8em}
.pt-name{flex:1 1 7em;min-width:0;font-size:.82em;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pt-fam,.pt-warn,.pt-state{flex:0 0 auto;font-family:var(--mono);font-size:.58em;letter-spacing:.06em;text-transform:uppercase;padding:.2em .45em;border:1px solid var(--line)}
.pt-fam{color:var(--brand);border-color:var(--line-hot)}
.pt-warn{color:var(--p4);border-color:currentColor;border-style:dashed}
.pt-state{color:var(--ink-faint)}
.pt-card.ok .pt-state{background:var(--ok);border-color:var(--ok);color:#0b0a0d}

/* the picture: shoulders on top, then d-pad, stick, the three small
   buttons, stick, face buttons */
.pt-pad{display:flex;flex-direction:column;gap:.45em;padding:.5em .55em .9em;border:1px solid var(--line);background:rgba(0,0,0,.18)}
.pt-sh{display:grid;grid-template-columns:3.6em 2.6em minmax(0,1fr) 2.6em 3.6em;gap:.35em;align-items:center}
.pt-pad i,.pt-raw i,.pt-act{font-style:normal}
.pt-trg{position:relative;height:1.15em;border:1px solid var(--line);background:rgba(255,255,255,.04);overflow:hidden;display:grid;place-items:center}
.pt-trg i{position:absolute;left:0;top:0;bottom:0;width:0;background:var(--ok)}
.pt-trg-r i{left:auto;right:0}
.pt-trg b{position:relative;font-family:var(--mono);font-weight:400;font-size:.56em;color:var(--ink-dim)}
.pt-trg.on b{color:#0b0a0d}
.pt-bmp{height:2.05em;display:grid;place-items:center;border:1px solid var(--line);font-family:var(--mono);font-size:.56em;line-height:1;color:var(--ink-dim)}
.pt-count{text-align:center;font-family:var(--mono);font-size:.58em;color:var(--ink-faint)}
.pt-row{display:flex;align-items:center;justify-content:space-between;gap:.3em}
.pt-dp{flex:0 0 auto;display:grid;grid-template-columns:repeat(3,1em);grid-template-rows:repeat(3,1em)}
.pt-d{border:1px solid var(--line);background:rgba(255,255,255,.04)}
.pt-d-up{grid-area:1/2}.pt-d-left{grid-area:2/1}.pt-d-mid{grid-area:2/2;border-color:transparent}.pt-d-right{grid-area:2/3}.pt-d-down{grid-area:3/2}
.pt-st{position:relative;flex:0 0 auto;width:3em;height:3em;border:1px solid var(--line);border-radius:50%;background:rgba(255,255,255,.03)}
.pt-st.past{border-color:var(--ok)}
.pt-st.on{background:rgba(78,232,165,.28);border-color:var(--ok)}
.pt-dead{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);border:1px dashed var(--ink-faint);border-radius:50%}
.pt-dot{position:absolute;left:50%;top:50%;width:.6em;height:.6em;margin:-.3em 0 0 -.3em;border-radius:50%;background:var(--ink-dim);z-index:1}
.pt-st.drift .pt-dot{background:var(--warn)}
.pt-st.past .pt-dot{background:var(--ok)}
/* how far off centre it rests or is pushed: amber inside the dead zone */
.pt-off{position:absolute;left:50%;top:100%;transform:translateX(-50%);margin-top:.15em;font-style:normal;font-family:var(--mono);font-size:.52em;line-height:1;color:var(--ok);white-space:nowrap}
.pt-st.drift .pt-off{color:var(--warn)}
.pt-mid{flex:0 0 auto;display:flex;gap:.2em;align-items:center}
.pt-sm{width:.7em;height:.42em;border:1px solid var(--line);background:rgba(255,255,255,.04)}
.pt-home{width:.6em;height:.6em}
.pt-fb{flex:0 0 auto;display:grid;grid-template-columns:repeat(3,1.1em);grid-template-rows:repeat(3,1.1em)}
.pt-f{display:grid;place-items:center;border:1px solid var(--line);border-radius:50%;font-family:system-ui,sans-serif;font-size:.58em;font-weight:700;line-height:1;color:var(--ink-dim);background:rgba(255,255,255,.04)}
.pt-f-y{grid-area:1/2}.pt-f-x{grid-area:2/1}.pt-f-b{grid-area:2/3}.pt-f-a{grid-area:3/2}
/* answered once: a brighter edge. down right now: lit */
.pt-pad .seen,.pt-raw .seen{border-color:rgba(78,232,165,.55)}
.pt-pad .on,.pt-raw .on,.pt-act.on{background:var(--ok);border-color:var(--ok);color:#0b0a0d}
.pt-raw{display:flex;flex-wrap:wrap;gap:.2em}
.pt-b{min-width:1.7em;padding:.2em 0;text-align:center;border:1px solid var(--line);font-family:var(--mono);font-size:.56em;line-height:1;color:var(--ink-faint)}
.pt-tools{display:flex;align-items:center;gap:.4em;min-width:0}
.pt-tools .mini-btn{flex:0 0 auto;font-size:.66em;padding:.3em .6em}
.pt-tools .mini-btn.on{border-color:var(--brand);color:var(--ink)}
.pt-note{flex:1 1 auto;min-width:0;text-align:right;font-family:var(--mono);font-size:.56em;color:var(--ink-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pt-details{display:flex;flex-direction:column;gap:.35em;padding-top:.45em;border-top:1px dashed var(--line);font-family:var(--mono);font-size:.6em;color:var(--ink-faint)}
.pt-details[hidden]{display:none}
.pt-details p{margin:0;line-height:1.4;overflow-wrap:anywhere}
.pt-id{color:var(--ink-dim)}
.pt-axes{display:flex;flex-wrap:wrap;gap:.15em .9em}
.pt-axis b,.pt-vals{color:var(--ink-dim);font-weight:400;font-variant-numeric:tabular-nums}
.pt-details .pt-b{font-size:.92em}
.pt-acts{display:flex;align-items:center;gap:.3em;flex-wrap:wrap}
.pt-act{border:1px solid var(--line);padding:.15em .45em}
@media (min-width:1800px) and (min-height:980px){.pt-sheet{font-size:1.2rem}}

/* The start-up animation's ground (hl-splash.css): the Arcade's light paper
   is warmer than the shared default. */
.hl-splash{--hls-light:#ece5d8;--hls-dark:#0b0a0d}

/* ══════════════ Short Fuse pad (fusepad.js) ══════════════
   Two faces. The manual is a document read in a hurry: one page at a
   time under an index of tabs, body text at reading size in the plain
   face, the pixel face kept for headings and labels, tables with room
   in them. Its page is the only thing that scrolls: the pad around it
   is touch-action:none, so a thumb moving the text moves nothing else.
   The remote is four arrows and one button, as big as the phone allows.
   A phone on this pad is never sent a picture of the stage, so the
   little view has nothing to show and gives its room to the page. */
#pad-live.is-fuse .mini-wrap{display:none}
#lay-fuse{gap:.5rem;min-height:0}
.fz-man{flex:1;min-height:0;display:flex;flex-direction:column;gap:.45rem}
.fz-head{display:flex;flex-wrap:wrap;gap:.15rem .8rem;align-items:baseline;font-family:var(--mono);font-size:.62rem;line-height:1.4;color:var(--ink-dim)}
.fz-ed{color:var(--brand)}
.fz-note.hot{color:var(--fit);font-weight:700}
.fz-tabs{flex:0 0 auto;display:flex;gap:.35rem;overflow-x:auto;overflow-y:hidden;touch-action:pan-x;overscroll-behavior:contain;scrollbar-width:none;padding-bottom:2px}
.fz-tabs::-webkit-scrollbar{display:none}
.fz-tab{flex:0 0 auto;display:flex;flex-direction:column;align-items:flex-start;justify-content:center;gap:.1rem;min-height:46px;padding:.3rem .7rem;
  border:1px solid var(--line);background:var(--panel-solid);color:var(--ink-dim);font-family:var(--mono);text-align:left;cursor:pointer;touch-action:pan-x}
.fz-tab b{font-size:.72rem;letter-spacing:.06em;color:var(--ink);white-space:nowrap}
.fz-tab span{font-family:var(--font);font-size:.7rem;color:var(--ink-dim);white-space:nowrap}
.fz-tab.on{border-color:var(--brand);background:rgba(245,158,11,.16)}
.fz-tab.on b{color:var(--brand)}
[data-theme="light"] .fz-tab.on b{color:#8a5200}
.fz-page{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;touch-action:pan-y;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  border:1px solid var(--line);background:var(--bg-2);padding:.9rem .85rem 1.4rem;user-select:none;-webkit-user-select:none}
.fz-doc{max-width:42rem;margin:0 auto;font-family:var(--font);font-size:1.03rem;line-height:1.5;color:var(--ink)}
.fz-dochead{border-bottom:2px solid var(--brand);padding-bottom:.45rem;margin-bottom:.8rem}
.fz-doc h3{margin:0;font-family:var(--display);font-size:1.25rem;line-height:1.2;letter-spacing:.06em;color:var(--brand)}
[data-theme="light"] .fz-doc h3{color:#8a5200}
.fz-sub{margin:.2rem 0 0;font-family:var(--mono);font-size:.7rem;color:var(--ink-dim);text-transform:uppercase}
.fz-doc h4{margin:1.1rem 0 .45rem;font-family:var(--mono);font-size:.82rem;letter-spacing:.08em;color:var(--brand-2);text-transform:uppercase}
.fz-doc p{margin:0 0 .75rem}
.fz-doc .fz-sub{margin:.2rem 0 0}
.fz-steps{margin:0 0 .9rem;padding-left:1.6rem;display:flex;flex-direction:column;gap:.5rem}
.fz-steps li{padding-left:.15rem}
.fz-steps li::marker{font-family:var(--mono);font-weight:700;color:var(--brand)}
.fz-doc .fz-tip{border-left:3px solid var(--brand-2);padding:.45rem .7rem;background:rgba(167,139,250,.09);font-size:.92rem;color:var(--ink-dim)}
.fz-tablewrap{overflow-x:auto;touch-action:pan-x pan-y;margin:0 0 .9rem}
.fz-table{border-collapse:collapse;width:100%;font-size:.98rem}
.fz-table th,.fz-table td{border:1px solid var(--line);padding:.45rem .5rem;text-align:left;vertical-align:middle}
.fz-table thead th{font-family:var(--mono);font-size:.62rem;font-weight:400;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-dim);background:rgba(128,128,128,.1)}
.fz-table tbody th{font-weight:600;white-space:nowrap}
.fz-table tbody tr:nth-child(even){background:rgba(128,128,128,.07)}
.fz-col{display:inline-flex;align-items:center;gap:.35rem;white-space:nowrap}
.fz-col svg{width:1.05em;height:1.05em;flex:0 0 auto}
.fz-col b{font-family:var(--mono);font-size:.72em;letter-spacing:.04em}
.fz-leds{display:inline-flex;gap:.22rem;align-items:center;vertical-align:middle}
.fz-leds i{width:.95rem;height:.95rem;box-sizing:border-box;border:2px solid #7a4f06;background:#16131a}
.fz-leds i.on{background:var(--brand);border-color:#fde08a;box-shadow:0 0 0 1px #7a4f06}
.fz-gcols{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:.3rem;margin:0 0 .9rem}
.fz-gcol{display:flex;flex-direction:column;align-items:center;gap:.35rem;border:1px solid var(--line);padding:.3rem .1rem .45rem;background:rgba(128,128,128,.06)}
.fz-gnum{font-family:var(--mono);font-size:.72rem;color:var(--brand)}
.fz-glyph{display:flex;flex-direction:column;align-items:center;gap:.1rem;min-width:0}
.fz-glyph canvas{width:2.3rem;height:2.3rem;image-rendering:pixelated;image-rendering:crisp-edges;background:#16131a}
.fz-glyph b{font-family:var(--mono);font-size:.5rem;font-weight:400;letter-spacing:.02em;color:var(--ink-dim)}
.fz-mazes{display:flex;flex-wrap:wrap;gap:1rem 1.3rem;justify-content:center;margin:0 0 .9rem}
.fz-maze{margin:0}
.fz-maze figcaption{font-family:var(--mono);font-size:.64rem;color:var(--brand-2);margin-bottom:.3rem;text-transform:uppercase}
.fz-mgrid{display:grid;grid-template-columns:1.1rem repeat(5,2.2rem);grid-template-rows:1.1rem repeat(5,2.2rem)}
.fz-mk{display:grid;place-items:center;font-family:var(--mono);font-size:.62rem;color:var(--ink-dim)}
.fz-mc{position:relative;border:0 solid var(--ink);background:rgba(128,128,128,.1);box-shadow:inset 0 0 0 1px rgba(128,128,128,.12)}
.fz-mc.r{border-right-width:3px}
.fz-mc.d{border-bottom-width:3px}
.fz-mc.l{border-left-width:3px}
.fz-mc.u{border-top-width:3px}
.fz-ring{position:absolute;top:50%;left:50%;width:58%;aspect-ratio:1/1;transform:translate(-50%,-50%);box-sizing:border-box;border:3px solid var(--ok);border-radius:50%}
[data-theme="light"] .fz-ring{border-color:#0a8f5c}
.fz-words{list-style:none;margin:0 0 .9rem;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.3rem .45rem;font-family:var(--mono);font-size:1.02rem;letter-spacing:.14em}
.fz-words li{border:1px solid var(--line);padding:.4rem .1rem;text-align:center}
.fz-doc .fz-foot{margin:1.2rem 0 0;font-family:var(--mono);font-size:.56rem;line-height:1.5;color:var(--ink-dim);text-align:center}

.fz-rem{flex:1;min-height:0;display:flex;flex-direction:column;gap:.8rem}
.fz-say{margin:0;font-size:.95rem;line-height:1.4;color:var(--ink-dim);text-align:center}
.fz-say b{color:var(--brand)}
[data-theme="light"] .fz-say b{color:#8a5200}
.fz-cross{flex:1 1 0;min-height:0;display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:.4rem;
  width:min(100%,22rem,52vh);aspect-ratio:1/1;margin:0 auto;max-height:52vh}
.fz-arrow{border:1px solid var(--line);background:var(--panel-solid);color:var(--ink);font-size:1.7rem;cursor:pointer;touch-action:none;min-width:44px;min-height:44px;transition:transform .08s,background .1s}
.fz-u{grid-area:1/2}.fz-l{grid-area:2/1}.fz-r{grid-area:2/3}.fz-d{grid-area:3/2}
.fz-arrow.held{background:var(--brand-2);color:#0b0a0d;transform:scale(.96)}
.fz-act{flex:0 0 auto;min-height:clamp(84px,20vh,150px);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.3rem;
  border:2px solid var(--brand);background:rgba(245,158,11,.14);color:var(--ink);cursor:pointer;touch-action:none;transition:transform .08s,background .1s}
.fz-act b{font-family:var(--display);font-size:1.5rem;letter-spacing:.14em}
.fz-act span{font-size:.78rem;color:var(--ink-dim)}
.fz-act.held{background:var(--brand);color:#0b0a0d;transform:scale(.98)}
.fz-act.held span{color:#0b0a0d}

/* a phone on its side: the index down the left, the page beside it; the remote's arrows and button side by side */
@media (orientation:landscape) and (max-height:560px){
  .fz-man{display:grid;grid-template-columns:minmax(8.5rem,24%) 1fr;grid-template-rows:auto minmax(0,1fr);gap:.35rem .5rem}
  .fz-head{grid-column:1 / -1}
  .fz-tabs{flex-direction:column;overflow-x:hidden;overflow-y:auto;touch-action:pan-y;padding:0}
  .fz-tab{touch-action:pan-y;min-height:44px}
  .fz-tab b,.fz-tab span{white-space:normal}
  .fz-page{padding:.6rem .8rem 1rem}
  .fz-doc h3{font-size:1.05rem}
  .fz-rem{flex-direction:row;flex-wrap:wrap;align-items:stretch;gap:.5rem 1rem}
  .fz-say{flex:1 1 100%;font-size:.82rem}
  .fz-cross{flex:0 0 auto;width:auto;height:100%;max-height:none;margin:0}
  .fz-rem .fz-cross{height:calc(100% - 2.6rem)}
  .fz-act{flex:1 1 0;min-height:0;height:calc(100% - 2.6rem)}
}

/* ── Beacon's pad (beaconpad.js): the hero strip, the line that says what to do, the enemies, the
      hand, the choices, a sheet. The lighthouse is dark whatever the arcade's theme, so the pad
      carries its own inks; the words of a card are set in the phone's own reading face, large
      enough to read, and the names and numbers in the arcade's pixels ── */
#pad-live.is-beacon{--bg:#0b0a0d;--ink:#f5f1ea;--ink-dim:#b8afa4;--ink-faint:#7d7468;--line:rgba(184,175,164,.24);--panel-solid:#1d1922;--panel:rgba(29,25,34,.9);background:#0b0a0d;color:#f5f1ea}
#pad-live.is-beacon .pad-hint,#pad-live.is-beacon .pad-keys{display:none}
#lay-beacon{gap:.4rem;position:relative;min-height:0;--ok:#4ee8a5;--gold:#fbbf24;--bad:#fb7185;--blk:#8fb6ff;
  --read:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}
#lay-beacon,#lay-beacon button{font-family:var(--mono);letter-spacing:0;color:var(--ink)}
#lay-beacon button{-webkit-tap-highlight-color:transparent;touch-action:manipulation;border-radius:0;cursor:pointer;margin:0}
#lay-beacon .bc-main,#lay-beacon .bc-hand,#lay-beacon .bc-sbody,#lay-beacon .bc-foes,#lay-beacon .bc-focus{touch-action:pan-x pan-y}
.bc-ico{display:inline-block;width:1em;height:1em;vertical-align:-.12em;flex:none}
.bc-ico svg{display:block;width:100%;height:100%;fill:currentColor}

.bc-top{flex:none;display:flex;flex-direction:column;gap:.3rem;padding:.4rem .5rem;border:1px solid var(--line);border-left:3px solid var(--knob);background:rgba(255,255,255,.03)}
.bc-hrow{display:flex;align-items:center;gap:.45rem;min-height:1.5rem;font-size:.75rem;min-width:0}
.bc-hname{color:var(--knob);letter-spacing:.06em}
.bc-bar{position:relative;flex:1;display:flex;align-items:center;height:1.3rem;background:#241a22;border:1px solid var(--line);min-width:4rem;overflow:hidden}
.bc-bar>i{position:absolute;left:0;top:0;bottom:0;background:#2f9e6f;transition:width .3s}
.bc-bar.low>i{background:#c2415a}
.bc-bar.fhp>i{background:#b23a55}
.bc-bar>b{position:relative;flex:1;text-align:center;font-size:.72rem;font-weight:700;text-shadow:0 1px 0 #000,0 0 3px #000}
.bc-blk{position:relative;display:inline-flex;align-items:center;gap:.15rem;align-self:stretch;padding:0 .35rem;background:var(--blk);color:#0b0a0d;font-style:normal;font-weight:700;font-size:.74rem}
.bc-gold{color:var(--gold);font-weight:700}
.bc-en,.bc-wk{flex:none;display:inline-flex;align-items:center;gap:.16rem;color:var(--gold)}
.bc-en>i:not(.bc-ico),.bc-wk>i:not(.bc-ico){width:.8rem;height:.8rem;border:2px solid #5a4a22}
.bc-en>i.on{background:var(--gold);border-color:var(--gold);box-shadow:0 0 6px rgba(251,191,36,.7)}
.bc-wk{color:#fb8f3a}
.bc-wk>i.on{background:#fb8f3a;border-color:#ffd36b;box-shadow:0 0 6px rgba(251,143,58,.8)}
.bc-en b{margin-left:.2rem;font-size:.8rem;color:var(--ink)}
.bc-floor{flex:none;color:var(--ink-dim);font-size:.66rem}
#lay-beacon .bc-sts{flex:1;display:flex;gap:.2rem;flex-wrap:wrap;min-width:0;min-height:1.8rem;max-height:1.8rem;align-items:center;background:transparent;border:0;padding:0;overflow:hidden}
.bc-st{flex:none;font-style:normal;font-weight:700;font-size:.58rem;line-height:1;padding:.2rem .25rem;border:1px solid var(--c,#b8afa4);color:var(--c,#b8afa4);background:rgba(0,0,0,.35);white-space:nowrap}
.bc-st.bad{border-style:dashed}
#lay-beacon .bc-mini{flex:none;min-height:1.8rem;padding:.1rem .5rem;font-size:.66rem;border:1px solid var(--line);background:var(--panel-solid);color:var(--ink-dim)}

.bc-main{flex:1;min-height:0;display:flex;flex-direction:column;gap:.4rem;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain}
#lay-beacon[data-mode="fight"] .bc-main{overflow:hidden}
.bc-now{flex:none;display:flex;align-items:center;gap:.5rem;min-height:2.2rem;padding:.3rem .55rem;border-left:3px solid var(--ink-faint);background:rgba(255,255,255,.04)}
.bc-now .bc-say{flex:1;font-family:var(--read);font-size:.9rem;line-height:1.2;color:var(--ink-dim)}
.bc-now.go{border-color:var(--knob)}
.bc-now.go .bc-say{color:var(--ink)}
.bc-now.aim{border-color:var(--bad);background:rgba(251,113,133,.14)}
.bc-now.aim .bc-say{color:var(--ink);font-weight:600}
.bc-now.warn,.bc-now.bad{border-color:var(--bad);background:rgba(251,113,133,.18)}
.bc-now.warn .bc-say,.bc-now.bad .bc-say{color:#ffd0d8}
.bc-now.note{border-color:var(--gold)}
.bc-clock{flex:none;font-size:.9rem;padding:.12rem .4rem;border:1px solid var(--line)}
.bc-clock.hot{color:#0b0a0d;background:var(--bad);border-color:var(--bad)}
.bc-allies{flex:none;display:flex;gap:.3rem;flex-wrap:wrap}
.bc-ally{font-family:var(--read);font-size:.74rem;color:var(--ink-dim);padding:.12rem .4rem;border:1px solid var(--line);border-left:3px solid var(--c)}
.bc-ally b{font-family:var(--mono);font-size:.62rem;color:var(--c)}
.bc-ally.done{opacity:.7}
.bc-ally.down{opacity:.5}

.bc-foes{flex:none;display:flex;gap:.3rem;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;padding:3px}
.bc-foes::-webkit-scrollbar{display:none}
#lay-beacon .bc-foe{flex:1 1 0;min-width:6.4rem;display:flex;flex-direction:column;gap:.22rem;align-items:stretch;padding:.35rem .4rem;border:2px solid #3a3444;background:var(--panel-solid);text-align:left}
#lay-beacon .bc-foe.at{border-color:#7a3443}
#lay-beacon .bc-foe.elite{border-top-color:#f59e0b}
#lay-beacon .bc-foe.boss{border-top-color:#ffd36b}
.bc-fname{font-size:.64rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bc-fint{display:flex;align-items:center;gap:.3rem;color:var(--c);min-height:1.5rem}
.bc-fint .bc-ico{width:1.35rem;height:1.35rem}
.bc-fint b{font-size:1.15rem;line-height:1}
.bc-fint b.word{font-size:.68rem}
.bc-fint em{font-style:normal;font-size:.58rem;color:var(--ink-dim);margin-left:auto;white-space:nowrap}
.bc-fint em.you{color:#0b0a0d;background:var(--bad);padding:.12rem .25rem;font-weight:700}
.bc-fint em:empty{display:none}
.bc-fint.none{color:var(--ink-faint)}
.bc-foe .bc-bar{flex:none;height:1.1rem;min-width:0}
.bc-fst{display:flex;gap:.15rem;flex-wrap:wrap}
.bc-pre{font-size:.72rem;font-weight:700;text-align:center;color:#0b0a0d;background:var(--bad);padding:.22rem}
#lay-beacon .bc-foes.aiming .bc-foe{border-color:var(--bad);animation:bc-pulse 1s infinite}
@keyframes bc-pulse{50%{box-shadow:0 0 0 3px rgba(251,113,133,.35)}}

.bc-hand{flex:1 1 0;min-height:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:min-content;gap:.45rem;align-content:start;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding:.7rem .4rem .5rem}
.bc-hand.pick{flex:none;overflow:visible}
#lay-beacon .bc-card{position:relative;display:flex;flex-direction:column;gap:.2rem;min-width:0;min-height:8.4rem;padding:.3rem .4rem .4rem;border:2px solid var(--tc);border-top-width:.4rem;background:#16131c;text-align:left;--tc:#4a4458;--th:#b8afa4}
#lay-beacon .bc-card.t-attack,.bc-focus.t-attack{--tc:#b23a55;--th:#fb7185}
#lay-beacon .bc-card.t-skill,.bc-focus.t-skill{--tc:#3f6fb5;--th:#8fb6ff}
#lay-beacon .bc-card.t-power,.bc-focus.t-power{--tc:#b99326;--th:#ffd36b}
#lay-beacon .bc-card.t-junk{--tc:#55505f;--th:#8c86a8}
.bc-focus.t-potion{--tc:#2f9e6f;--th:#4ee8a5}
.bc-chead{display:flex;align-items:center;gap:.3rem;min-width:0}
.bc-cost{flex:none;width:1.75rem;height:1.75rem;display:flex;align-items:center;justify-content:center;background:var(--gold);color:#0b0a0d;font-size:1.05rem;font-weight:700;border-radius:50%;box-shadow:0 0 0 2px #0b0a0d,0 0 0 3px var(--gold)}
.bc-cost.none{background:#55505f;color:#d8d2c8;box-shadow:0 0 0 2px #0b0a0d,0 0 0 3px #55505f}
.bc-cname{font-size:.66rem;line-height:1.15;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow-wrap:break-word;min-width:0}
.bc-card.up .bc-cname{color:var(--ok)}
.bc-ctype{display:flex;align-items:center;gap:.25rem;font-size:.5rem;letter-spacing:.08em;color:var(--th)}
.bc-ctext{flex:1;font-family:var(--read);font-size:.84rem;line-height:1.22;overflow:hidden}
.bc-ctext b.more,.bc-ftext b.more{color:var(--ok)}
.bc-ctext b.less,.bc-ftext b.less{color:var(--bad)}
.bc-cfoot{display:flex;flex-wrap:wrap;gap:.2rem;align-items:center}
.bc-tag{font-style:normal;font-size:.48rem;padding:.14rem .25rem;border:1px solid var(--line);color:var(--ink-dim);display:inline-flex;gap:.15rem;align-items:center}
.bc-tag.aim{color:var(--bad);border-color:#7a3443}
.bc-need{font-style:normal;font-size:.54rem;padding:.16rem .3rem;background:var(--bad);color:#0b0a0d;font-weight:700}
.bc-price{font-style:normal;font-size:.74rem;color:var(--gold);margin-left:auto;font-weight:700}
.bc-price.sold{color:var(--ink-faint)}
#lay-beacon .bc-card.dim{border-color:#37323e;background:#100e14}
.bc-card.dim>*:not(.bc-cfoot),.bc-card.dim .bc-tag{opacity:.42}
#lay-beacon .bc-card.sel{outline:3px solid var(--ink);outline-offset:0;box-shadow:0 0 0 6px var(--knob),0 0 18px 6px var(--knob);transform:translateY(-3px);z-index:2}
.bc-card.sel::after{content:"PICKED UP";position:absolute;right:-2px;top:-.4rem;transform:translateY(-55%);font-size:.48rem;padding:.14rem .3rem;background:var(--ink);color:#0b0a0d;font-weight:700}
.bc-hand.pick .bc-card.sel::after{content:"CHOSEN"}
.bc-card.going,.bc-btn.going,.bc-act.going{animation:bc-go .45s infinite alternate;pointer-events:none}
@keyframes bc-go{to{opacity:.35}}
#lay-beacon .bc-card.big{width:100%;min-height:0}
.bc-card.big .bc-cname{font-size:.9rem;-webkit-line-clamp:3}
.bc-card.big .bc-ctext{font-size:1.05rem;line-height:1.35;overflow:visible}
.bc-card.big .bc-ctype{font-size:.6rem}

.bc-bottom{flex:none;display:flex;flex-direction:column;gap:.35rem}
.bc-row{display:flex;gap:.3rem;align-items:stretch}
#lay-beacon .bc-btn{min-height:2.7rem;padding:.45rem .6rem;border:1px solid var(--line);background:var(--panel-solid);font-size:.8rem;letter-spacing:.04em}
#lay-beacon .bc-pile{flex:1;min-height:2.3rem;padding:.2rem;font-size:.56rem;color:var(--ink-dim)}
.bc-pile b{font-size:.82rem;color:var(--ink)}
#lay-beacon .bc-pot{flex:1;min-height:2.3rem;padding:.2rem .3rem;font-size:.6rem;border-color:var(--ok);color:var(--ok);display:flex;align-items:center;justify-content:center;gap:.25rem}
#lay-beacon .bc-pot.dim{opacity:.5}
#lay-beacon .bc-end{min-height:3.2rem;font-size:1rem;background:var(--knob);color:#0b0a0d;border-color:var(--knob);font-weight:700}
#lay-beacon .bc-end.dim{background:var(--panel-solid);color:var(--ink-dim);border-color:var(--line)}
#lay-beacon .bc-play{flex:2;min-height:3.2rem;background:var(--ok);color:#0b0a0d;border-color:var(--ok);font-weight:700;font-size:.95rem}
#lay-beacon .bc-play.dim{background:var(--panel-solid);color:var(--ink-dim);border-color:var(--line);font-size:.8rem;font-family:var(--read);font-weight:400}
#lay-beacon .bc-cancel,#lay-beacon .bc-skip{flex:1;min-height:3.2rem;font-size:.7rem}
.bc-aimhint{flex:2;display:flex;align-items:center;justify-content:center;font-size:.78rem;color:var(--bad);border:1px dashed var(--bad)}
.bc-focus{border:2px solid var(--tc,#4a4458);border-top-width:.4rem;background:#131018;padding:.45rem .55rem;display:flex;flex-direction:column;gap:.3rem;max-height:38vh;overflow-y:auto}
.bc-fhead{display:flex;gap:.45rem;align-items:center;color:var(--th)}
.bc-fhead .bc-ico{width:1.5rem;height:1.5rem}
.bc-ftitle{font-size:.86rem;color:var(--ink)}
.bc-ftext{margin:0;font-family:var(--read);font-size:1.02rem;line-height:1.3}
.bc-words{list-style:none;margin:0;padding:0;font-family:var(--read);font-size:.8rem;color:var(--ink-dim);line-height:1.3}
.bc-words b{font-family:var(--mono);font-size:.62rem}
.bc-whynot{margin:0;font-family:var(--read);font-size:.9rem;color:#ffd0d8;background:rgba(251,113,133,.18);padding:.35rem .45rem}

.bc-text{margin:0;font-family:var(--read);font-size:.95rem;line-height:1.4}
.bc-waiting{margin:0;padding:1.2rem .8rem;border:1px dashed var(--line);font-family:var(--read);font-size:1rem;color:var(--ink-dim);text-align:center}
.bc-big{margin:.2rem 0;font-size:1.4rem;color:var(--gold)}
.bc-h4{margin:.3rem 0 0;font-size:.62rem;letter-spacing:.14em;color:var(--ink-dim)}
.bc-empty{margin:0;grid-column:1/-1;font-family:var(--read);font-size:.88rem;color:var(--ink-faint)}
.bc-acts{display:flex;flex-direction:column;gap:.35rem}
#lay-beacon .bc-act{position:relative;display:flex;flex-direction:column;align-items:flex-start;gap:.2rem;min-height:3rem;padding:.5rem .6rem;border:2px solid var(--c,#3a3444);background:var(--panel-solid);text-align:left}
.bc-act b{display:flex;gap:.35rem;align-items:center;font-size:.84rem}
#lay-beacon .bc-act.sel{border-color:var(--ink);box-shadow:inset 0 0 0 2px var(--knob),0 0 12px var(--knob)}
.bc-act.dim>*{opacity:.5}
.bc-asub{font-size:.68rem;color:var(--gold)}
.bc-atext{font-family:var(--read);font-size:.86rem;color:var(--ink-dim);line-height:1.3}
.bc-alines{margin:.1rem 0 0;padding-left:1rem;font-family:var(--read);font-size:.8rem;color:var(--ink-dim);line-height:1.3}
.bc-votes{position:absolute;right:.5rem;top:.5rem;display:flex;gap:.2rem;align-items:center}
.bc-votes i{display:block;width:.55rem;height:.55rem;background:var(--knob)}
.bc-votes em{font-style:normal;font-size:.54rem;color:var(--ink-dim)}
.bc-act.k-elite b{color:#fb7185}.bc-act.k-rest b{color:var(--ok)}.bc-act.k-shop b{color:#f59e0b}.bc-act.k-treasure b{color:#ffd36b}.bc-act.k-event b{color:#c88cff}.bc-act.k-boss b{color:var(--gold)}
#lay-beacon .bc-ready{min-height:3.2rem;font-size:1rem;background:var(--knob);color:#0b0a0d;border-color:var(--knob);font-weight:700}
#lay-beacon .bc-ready.on{background:var(--ok);border-color:var(--ok)}
.bc-res{list-style:none;margin:0;padding:0}
.bc-res li{display:flex;justify-content:space-between;font-family:var(--read);font-size:.95rem;padding:.25rem 0;border-top:1px solid rgba(255,255,255,.07)}

.bc-sheet{position:absolute;inset:0;z-index:4;display:flex;flex-direction:column;gap:.4rem;background:#0d0b12;padding:.5rem .6rem}
.bc-shead{flex:none;display:flex;align-items:center;gap:.5rem}
.bc-shead b{flex:1;letter-spacing:.08em;color:var(--gold);font-size:.8rem}
#lay-beacon .bc-shut{min-height:2.4rem;padding:.35rem .9rem;font-size:.72rem}
.bc-sbody{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;display:flex;flex-direction:column;gap:.45rem}
.bc-list{list-style:none;margin:0;padding:0}
.bc-list li{display:flex;flex-direction:column;gap:.12rem;padding:.3rem 0;border-top:1px solid rgba(255,255,255,.07)}
.bc-list li.row{flex-direction:row;gap:.5rem;align-items:center}
.bc-list li.row .bc-ico{width:1.4rem;height:1.4rem}
.bc-list li b{font-size:.7rem}
.bc-list li span{font-family:var(--read);font-size:.88rem;color:var(--ink-dim);line-height:1.3}
.bc-log{list-style:none;margin:0;padding:0;font-family:var(--read);font-size:.9rem;line-height:1.3}
.bc-log li{padding:.25rem .4rem;border-left:3px solid var(--knob)}
.bc-log li.foe{border-color:var(--bad);color:#ffd0d8}
.bc-log li.tick{border-color:var(--ink-faint);color:var(--ink-dim)}
.bc-log li.round{border:0;margin-top:.3rem;font-family:var(--mono);font-size:.6rem;letter-spacing:.14em;color:var(--ink-faint);text-transform:uppercase}

/* a small phone: the same pad, a little tighter */
@media (max-width:350px){
  .bc-ctext{font-size:.78rem}
  #lay-beacon .bc-card{min-height:7.6rem;padding:.25rem .3rem .35rem}
  .bc-cost{width:1.5rem;height:1.5rem;font-size:.95rem}
  .bc-cname{font-size:.6rem}
  #lay-beacon .bc-foe{min-width:5.6rem}
  .bc-hand{gap:.35rem}
}
@media (max-height:620px) and (orientation:portrait){
  .bc-top{padding:.25rem .4rem;gap:.2rem}
  #lay-beacon .bc-end,#lay-beacon .bc-play,#lay-beacon .bc-cancel,#lay-beacon .bc-skip{min-height:2.7rem}
  #lay-beacon .bc-pile,#lay-beacon .bc-pot{min-height:2rem}
  .bc-fst{display:none}
  .bc-focus .bc-words{display:none}
  .bc-alines{display:none}
  #lay-beacon .bc-act{min-height:2.6rem;padding:.35rem .5rem}
  .bc-focus{padding:.3rem .45rem;gap:.2rem}
  .bc-ftext{font-size:.95rem}
}
@media (min-height:760px) and (orientation:portrait){#lay-beacon .bc-card{min-height:9.4rem}}
/* on its side: the enemies down the left, the hand on the right, one row of buttons */
@media (orientation:landscape) and (max-height:520px){
  .bc-top{flex-direction:row;gap:.6rem;padding:.25rem .4rem}
  .bc-hrow{flex:1}
  #lay-beacon[data-mode="fight"] .bc-main{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,3fr);grid-template-rows:auto auto minmax(0,1fr);gap:.3rem}
  #lay-beacon[data-mode="fight"] .bc-now{grid-column:1;grid-row:1}
  #lay-beacon[data-mode="fight"] .bc-allies{grid-column:1;grid-row:2}
  #lay-beacon[data-mode="fight"] .bc-foes{grid-column:1;grid-row:3;flex-direction:column;overflow-x:hidden;overflow-y:auto}
  #lay-beacon[data-mode="fight"] .bc-foe{flex:none;flex-direction:row;flex-wrap:wrap;align-items:center;gap:.2rem .4rem}
  #lay-beacon[data-mode="fight"] .bc-foe .bc-bar{flex:1 1 5rem}
  #lay-beacon[data-mode="fight"] .bc-fname{flex:1 1 40%}
  #lay-beacon[data-mode="fight"] .bc-hand{grid-column:2;grid-row:1 / 4;grid-template-columns:repeat(4,minmax(0,1fr))}
  #lay-beacon .bc-card{min-height:6.4rem}
  .bc-ctext{font-size:.78rem}
  .bc-bottom{flex-direction:row;align-items:stretch}
  .bc-bottom>.bc-row{flex:2}
  .bc-bottom>.bc-focus{flex:3;max-height:6.2rem}
  .bc-bottom>.bc-focus .bc-words{display:none}
  #lay-beacon .bc-end{flex:1;min-height:2.6rem}
  #lay-beacon .bc-play,#lay-beacon .bc-cancel,#lay-beacon .bc-skip{min-height:2.6rem}
  .bc-acts{display:grid;grid-template-columns:1fr 1fr}
  .bc-hand.pick{grid-template-columns:repeat(5,minmax(0,1fr))}
}
@media (orientation:landscape) and (max-height:520px) and (max-width:700px){
  #lay-beacon[data-mode="fight"] .bc-hand{grid-template-columns:repeat(3,minmax(0,1fr))}
  .bc-hand.pick{grid-template-columns:repeat(4,minmax(0,1fr))}
  .bc-cname{font-size:.56rem}
  .bc-cost{width:1.45rem;height:1.45rem;font-size:.9rem}
}

/* ── Online (online.js): the landing card, the entry, the lobby, the match ── */
.choice-online .choice-cta{color:var(--brand)}
.art-globe{position:relative;width:84px;height:84px;border:2px solid var(--brand);border-radius:50%;background:rgba(245,158,11,.06)}
.art-globe .art-ring{position:absolute;inset:14px 6px;border:2px solid var(--brand);border-radius:50%;opacity:.5}
.art-globe .art-node{position:absolute;width:10px;height:10px;background:var(--brand-2);animation:nodeblink 2.4s ease-in-out infinite}
.art-globe .art-node.a{left:12px;top:30px}
.art-globe .art-node.b{right:14px;top:18px;animation-delay:.8s}
.art-globe .art-node.c{left:38px;bottom:12px;animation-delay:1.6s}
@keyframes nodeblink{0%,100%{opacity:.35}50%{opacity:1}}
.on-body{display:grid;place-items:center;padding:1rem;overflow:auto}
.on-card{width:min(440px,100%);text-align:center}
.on-card .field{text-align:left;display:block;margin:.8rem 0}
.on-h{margin:.4rem 0 .2rem;font-size:1.3rem}
.on-or{margin-top:1.1rem}
.on-code{letter-spacing:.3em;text-transform:uppercase}
.on-lobby-grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(300px,440px));justify-content:center;width:100%}
.on-room-code{margin:.3rem 0 .2rem;font-size:clamp(2rem,6vw,3.2rem);letter-spacing:.2em}
.on-qr{margin:.8rem auto .4rem;line-height:0}
.on-qr img{image-rendering:pixelated;border:4px solid #fff}
.on-seat{display:flex;align-items:center;gap:.5rem;text-align:left}
.on-seat .tag{font-family:var(--mono);font-size:.62rem;letter-spacing:.1em;border:1px solid var(--line);padding:.1rem .35rem;color:var(--ink-faint)}
.on-q{margin-left:auto;display:inline-flex;gap:.5rem;align-items:center;font-family:var(--mono);font-size:.72rem;color:var(--ink-faint)}
.on-tr{border:1px solid var(--line);padding:.1rem .4rem}
.on-tr-dc{color:var(--ok);border-color:var(--ok)}
.on-tr-ws{color:var(--warn);border-color:var(--warn)}
.on-sub{margin-top:1rem}
.on-games{display:flex;gap:.6rem;justify-content:center;margin:.5rem 0}
.on-game{display:grid;place-items:center;gap:.2rem;min-width:96px;padding:.6rem .8rem;border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer}
.on-game.sel{border-color:var(--brand);box-shadow:inset 0 0 0 1px var(--brand)}
.on-game:disabled{cursor:default;opacity:.8}
.on-game-ico{font-size:1.5rem;line-height:1}
.on-game-name{font-size:.8rem;color:var(--ink)}
.on-opts{display:grid;gap:.5rem;margin:.6rem 0 .9rem;text-align:left;font-size:.9rem}
.on-opt{display:flex;align-items:center;gap:.5rem;justify-content:space-between}
.on-opt select{background:var(--panel);color:var(--ink);border:1px solid var(--line);padding:.25rem .4rem}
.on-hash{margin:.6rem 0 0;font-family:var(--mono);font-size:.62rem;color:var(--ink-faint)}
.on-stage-wrap{position:relative;touch-action:none;user-select:none;-webkit-user-select:none}
#on-stage{display:block;width:100%;height:100%;touch-action:none;cursor:crosshair}
/* the shared screen's bar hides its middle slot (its roster says more); the online bar's middle is the game's one line */
#on-shell .sb-players{display:flex}
.on-hud{font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;color:var(--ink-faint);text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:60vw}
.on-hud.alert{color:var(--warn)}
.on-banner{position:absolute;left:50%;bottom:1rem;transform:translateX(-50%);max-width:92%;padding:.5rem .9rem;border:1px solid var(--warn);color:var(--warn);background:var(--panel);font-size:.85rem;pointer-events:none}
.on-result{position:absolute;inset:0;display:grid;place-content:center;gap:.4rem;text-align:center;background:rgba(0,0,0,.55);padding:1rem}
.on-result h2{margin:0;font-size:clamp(1.6rem,5vw,3rem)}
.on-result p{margin:0;color:var(--ink-dim)}
.on-result .prime-btn{margin-top:1rem;justify-self:center}
.on-quality{font-family:var(--mono);font-size:.72rem}
@media (max-width:700px){
  .on-body{padding:.6rem}
  .on-games{flex-wrap:wrap}
  /* a phone's bar has room for the dot, the game's line and the quality chip, not the room's name as well */
  #on-shell.playing #on-state{display:none}
  #on-shell.playing .on-hud{max-width:40vw}
  .on-quality{font-size:.62rem;padding:.2rem .4rem}
}
