/* ══════════════════════════════════════════════════════════
   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);
  --font:"Inter Variable","Inter",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%}
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:100dvh}

/* ── wordmark ── */
.wordmark{font-weight:700;letter-spacing:-.03em;font-size:clamp(2.4rem,7vw,4.2rem);line-height:1}
.wordmark .wm-a{color:var(--ink)}
.wordmark .wm-b{background:linear-gradient(96deg,var(--brand),var(--brand-2));-webkit-background-clip:text;background-clip:text;color:transparent}
.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}

.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}

.feature-strip{list-style:none;display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));padding:0;margin:2.6rem 0 0}
.feature-strip li{border-left:2px solid var(--line-hot);padding:.15rem 0 .15rem 1rem}
.feature-strip b{display:block;font-size:.95rem;margin-bottom:.3rem}
.feature-strip span{color:var(--ink-faint);font-size:.9rem;line-height:1.5}

.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}
.pv-card h4{margin:.6rem 0 .3rem;font-size:1.05rem}
.pv-card p{margin:0;color:var(--ink-faint);font-size:.87rem;line-height:1.5}
.pv-meta{display:block;margin-top:.7rem;font-family:var(--mono);font-size:.72rem;color:var(--brand-2)}
.landing-foot{margin-top:3.2rem;color:var(--ink-faint);font-size:.82rem;text-align:center}

/* ══════════════ 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}

/* 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%}

/* The player-slots bar: the seat grid, then the seat-filling controls
   (add keyboard player, fill-with-bots) sitting inline as the bar's own
   action row rather than in a separate settings block. */
.seat-bar{display:flex;flex-direction:column;gap:.55rem}
.seat-list{display:grid;gap:.55rem}
.seat-adders{display:flex;align-items:center;gap:1.1rem;flex-wrap:wrap}
.seat-add{white-space:nowrap}
.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}
.seat .seat-tag{margin-left:auto;font-family:var(--mono);font-size:.7rem;color:var(--ink-faint)}

.lobby-games{display:flex;flex-direction:column;gap:1rem;min-width:0}
/* Picker header: title/subtitle on the left, the per-game options beside it
   (they wrap under on narrow screens). */
.lg-head{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem 1.4rem;flex-wrap:wrap}
.lg-headline{min-width:0}
.lg-head h2{margin:0;font-size:clamp(1.4rem,3vw,2rem);letter-spacing:-.02em}
.lg-head p{margin:.3rem 0 0;color:var(--ink-faint);font-size:.9rem}
.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}
.game-card p{margin:0;color:var(--ink-dim);font-size:.88rem;line-height:1.5}
.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}

/* Per-game options now ride in the picker header, so they need no top
   divider of their own; the header spacing carries them. */
.lobby-tools{display:flex;gap:1.1rem;align-items:center;flex-wrap:wrap;
  padding-top:.35rem}
/* Seat-filling controls are lobby-only: once a round runs the roster goes
   compact and must not cover the play area, so hide them then. */
.roster.compact .seat-adders{display:none}
.seg-group{display:flex;align-items:center;gap:.6rem}
.seg-label{font-size:.88rem;color:var(--ink-dim)}
.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}
.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-card{border:1px solid var(--line);border-radius:0;background:var(--panel);padding:1rem 1.1rem}
.mirror-head{display:flex;align-items:center;justify-content:space-between;gap:.6rem}
.mirror-head .join-kicker{margin:0}
.mirror-url{
  margin:.4rem 0 .8rem;font-family:var(--mono);font-size:.85rem;color:var(--brand-2);
  word-break:break-all;line-height:1.4;
}
.mirror-body{display:flex;gap:.9rem;align-items:center}
.qr-box.small{padding:.4rem}
.qr-box.small img,.qr-box.small canvas{width:clamp(72px,7vw,92px)}
.mirror-hint{margin:0;color:var(--ink-faint);font-size:.78rem;line-height:1.5}

/* ══════════════ 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:100dvh;overflow:hidden;touch-action:none;user-select:none;-webkit-user-select:none}
.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}
#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}

.pad-top{display:flex;align-items:center;gap:.7rem;flex:0 0 auto}
.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}
.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: a row of move buttons on the left, rotate/drop/hold on the right */
.tet-dir{display:flex;gap:.5rem;align-items:flex-end;width:100%}
.tet-dir .tet-move{flex:1 1 0;min-width:0;height:96px;font-size:1.7rem}
.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}
.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}

/* ══════════ 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}
#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 minmax(120px,24vw);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 auto;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/3;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}
}


.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;gap:.6rem;align-items:stretch;height:min(34vh,230px)}
.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}

/* ── 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;
}
.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)}
/* 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:460px;border-radius:0;border:1px solid var(--line-hot);
    padding-bottom:1.1rem;max-height:86dvh;animation:popIn .2s cubic-bezier(.2,.9,.3,1.2);
  }
}
.menu-players{display:grid;gap:.4rem;margin-bottom:.5rem}
.mp-row{
  display:flex;align-items:center;gap:.6rem;border:1px solid var(--line);
  border-radius:0;padding:.55rem .7rem;font-size:.92rem;
}
.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;
}
.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}
.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)}

.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));
}

/* ══════════════ keyboard players ══════════════ */
.seat.has-keys{flex-wrap:wrap;row-gap:.5rem}
.seat-keys{display:flex;gap:.3rem;flex-wrap:wrap;flex-basis:100%;order:3}
.seat-tools{display:flex;gap:.4rem;order:4;margin-left:auto}
.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}
.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-rules-btn{padding:.22rem .6rem;font-size:.72rem}
}

/* ══════════════════════════════════════════════════════════
   Mobile-only mode — a phone hosts the room.

   The screen page already renders the board; all that changes is the
   shape. The pad is MOVED in under the stage rather than duplicated,
   so every existing binding keeps working. Controls own their space:
   the board flexes and gives way, the pad does not.
   ══════════════════════════════════════════════════════════ */
.mobile-host { height: 100dvh; }
.mobile-host .screen-bar { padding: .45rem .7rem; gap: .5rem; }
.mobile-host .wordmark.small { font-size: 1rem; }
/* The shared @media (max-width:860px) rule pushes the player chips onto
   their own full-width row. On a 740x360 phone that made the header 93px —
   a quarter of the screen — and starved the board. Keep them inline and let
   them clip instead. */
/* The shared @media (max-width:860px) rule pushes the player chips onto their
   own full-width row; on a 740x360 phone that made the header 93px — a quarter
   of the screen. Keeping them inline instead just collided them with the room
   pill on a narrow phone. Every mobile-enabled game already names its players
   on the board, so drop the chips here and give the header back to the code. */
.mobile-host .sb-players { display: none; }
.mobile-host .chip { font-size: .74rem; padding: .18rem .55rem; }

/* the lobby has to fit a phone, so the desktop furniture stands down */
.mobile-host #lobby { grid-template-columns: 1fr; padding: .8rem; gap: .8rem; }
.mobile-host .mirror-card,
.mobile-host .seat-adders .switch b { display: none; }
.mobile-host .qr-box img, .mobile-host .qr-box canvas { width: clamp(96px, 30vw, 132px); }
.mobile-host .join-card { padding: .9rem; }
.mobile-host .room-code { font-size: clamp(2.2rem, 13vw, 3rem); margin: .2rem 0 .6rem; }
.mobile-host .game-grid { grid-template-columns: 1fr 1fr; gap: .6rem; }
.mobile-host .game-card { padding: .7rem; border-radius:0; }
.mobile-host .game-card h3 { font-size: .95rem; margin: .4rem 0 .2rem; }
.mobile-host .game-card p { font-size: .78rem; }
.mobile-host .gc-ico { font-size: 1.4rem; }
.mobile-host .gc-meta { display: none; }
.mobile-host .gc-meta .gc-tv { display: inline-block; }

/* a game that wants a television says so instead of rendering unplayably */
.game-card.needs-tv { opacity: .45; }
.game-card.needs-tv:hover { transform: none; box-shadow: none; border-color: var(--line); }
.gc-tv {
  font-family: var(--mono); font-size: .66rem; border-radius:0;
  padding: .16rem .55rem; color: var(--warn); border: 1px solid var(--warn);
}
.mobile-host .game-card.needs-tv .gc-meta { display: flex; }

/* board on top, pad below — the pad's height is the fixed quantity */
.mobile-host #stage-wrap { flex: 1 1 auto; min-height: 20vh; }
.host-pad-slot {
  flex: 0 0 clamp(280px, 46vh, 430px);
  min-height: 0; display: flex; position: relative; z-index: 2;
  border-top: 1px solid var(--line); background: rgba(8, 11, 28, .6);
}
.host-pad-slot[hidden] { display: none; }
.host-pad-slot > .pad-stage {
  flex: 1; min-height: 0; height: auto;
  padding: .6rem .7rem calc(env(safe-area-inset-bottom) + .6rem);
  /* The pad is MOVED here from the controller view, and carried a stale
     width with it — measured 390px inside a 360px slot, so the right edge of
     the hand and the TAKE button sat outside the screen. Pin it to the slot. */
  width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box;
}
/* The buzzer is sized from viewport WIDTH, which overflows a pad slot that is
   sized from height — measured bottom 857 in an 844px viewport. Let height
   drive it when that is the tighter axis. */
.host-pad-slot .buzzer { height: min(100%, 78vw, 340px); width: auto; margin: auto; }
/* Already inside the host page: a second ✕ next to the header's own one
   is just two ways to leave, sitting side by side. */
.host-pad-slot #btn-pad-quit { display: none; }
/* 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; }
}
/* The name is on the seat list and the title says the game — inside the
   host page the pad header only needs its menu button. */
.host-pad-slot .pad-top { gap: .5rem; }

/* "room ABCD is live" wrapped to three lines on a 390px phone and made the
   header taller than the board deserved. */
.mobile-host .net-pill { white-space: nowrap; font-size: .66rem; padding: .2rem .5rem; }
.mobile-host .net-pill i { width: 6px; height: 6px; }
.mobile-host .sb-left { gap: .4rem; min-width: 0; }
.mobile-host .sb-right { gap: .35rem; }
.mobile-host .ghost-btn { padding: .38rem .6rem; font-size: .82rem; }
.mobile-host #host-share { white-space: nowrap; }

/* Landscape is the tight case: a phone on its side has very little height
   to divide. "Controls first" means the pad stays USABLE, not that it takes
   everything — an earlier split left a 58px board, which is not a game. The
   pad takes a comfortable fixed slice and the board keeps the rest. */
@media (orientation: landscape) {
  .mobile-host .game-grid { grid-template-columns: repeat(3, 1fr); }
  .host-pad-slot { flex: 0 0 clamp(160px, 46vh, 230px); }
  /* Pong and Vertigo carry only a strip and a button. Reserving a full
     gamepad slot for them starved the board — Vertigo's tower fell to a
     97px sliver — so a compact pad takes what it needs and no more. */
  .pad-compact .host-pad-slot { flex: 0 0 clamp(100px, 30vh, 150px); }
  .pad-tall .host-pad-slot { flex: 0 0 clamp(190px, 56vh, 250px); }
  .mobile-host #stage-wrap { min-height: 34vh; }
  .host-pad-slot > .pad-stage { padding: .4rem .6rem calc(env(safe-area-inset-bottom) + .4rem); gap: .5rem; }
  .mobile-host .pad-hint { display: none; }   /* the board needs the pixels more */
}
@media (max-height: 460px) {
  .host-pad-slot { flex: 0 0 clamp(150px, 44vh, 200px); }
  .mobile-host .screen-bar { padding: .3rem .6rem; }
  .mobile-host .pad-top { display: none; }    /* game title is on the board */
}

/* Big view — for a room hosted on a phone, where this IS the screen.
   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; } }

/* On a phone the pad owns the bottom of the screen, so a bottom-left toast
   lands squarely on the controls — "MAX is out" sat on the POWER button.
   Put them above the board instead, where nothing is being pressed. */
.mobile-host-body .toast-rail { bottom: auto; top: 3.2rem; left: 50%; transform: translateX(-50%); justify-items: center; }
/* Landscape centres a square arena under that top-centre rail, so "HOST · 4
   left" landed on the board and its own player label. Tuck it into the
   corner, where the games keep least. */
@media (orientation: landscape) {
  .mobile-host-body .toast-rail { top: 2.9rem; left: auto; right: .6rem; transform: none; justify-items: end; }
}
.mobile-host-body .toast { font-size: .78rem; padding: .35rem .8rem; }

/* 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);
}

/* ── 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"] .host-pad-slot{background:rgba(228,219,202,.80)}
[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,.seg-label,
.action-btn,.dp,.buzzer,.menu-sel,.pv-meta,.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,.seat-tag,.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}}

/* ── 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,.seat-tag,.pv-meta,.net-pill,.sheet-note,.mp-tag,
.join-hint,.landing-foot,.pad-hint,.lg-head p{
  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}
.seg-label,.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,.seg-label,.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 .seat-tag,
.roster.compact .seat-keys{display:none}
.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;
}
/* Show that there is more rather than cutting content off silently. */
.games-frame::before,
.games-frame::after{
  content:"";position:sticky;left:0;right:0;height:26px;display:block;
  pointer-events:none;z-index:2;opacity:0;transition:opacity .18s;
}
.games-frame::before{top:0;margin-bottom:-26px;background:linear-gradient(var(--bg),transparent)}
.games-frame::after{bottom:0;margin-top:-26px;background:linear-gradient(transparent,var(--bg))}
.games-frame.more-above::before{opacity:1}
.games-frame.more-below::after{opacity:1}

/* ══════════════ ready gate ══════════════ */
.gate-overlay{
  position:absolute;inset:0;z-index:6;display:grid;place-items:center;
  background:rgba(8,8,18,.86);backdrop-filter:blur(6px);padding:1rem;
}
[data-theme="light"] .gate-overlay{background:rgba(40,36,30,.72)}
.gate-card{
  text-align:center;max-width:min(560px,92vw);
  border:2px solid var(--line-hot);background:var(--panel-solid);
  padding:clamp(1rem,3vw,2rem);
}
.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.5rem,5vw,2.6rem);line-height:1.05}
.gate-sub{margin:0 0 1rem;color:var(--ink-dim);font-size:.95rem;line-height:1.5}
.gate-players{display:flex;flex-wrap:wrap;gap:.5rem;justify-content:center;margin-bottom:1.1rem}
.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}
.gate-count{font-family:var(--mono);font-size:.8rem;color:var(--ink-faint);min-width:2.5em}

/* ══════════════ 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;overflow-y:auto;
}
.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}
}

/* The lobby itself must not scroll, or the games frame never gets a
   bounded height to scroll inside. Left column scrolls on its own. */
#lobby{overflow:hidden}
#lobby .lobby-join{min-height:0;overflow-y:auto}
#lobby .lobby-games{min-height:0}

/* Below the two-column breakpoint the lobby stacks, so an inner scroll
   region would be a scroll inside a scroll — and on a phone host it
   collapsed the games frame to a ~50px slot. There, the page scrolls as
   one and the games simply flow. */
@media (max-width:860px){
  #lobby{overflow:auto}
  #lobby .lobby-join{overflow:visible}
  .games-frame{overflow:visible;flex:none;min-height:0}
  .games-frame::before,.games-frame::after{display:none}
}
/* A hosting phone has the least room of anything: keep the roster to a
   glance so the cabinet keeps the screen. */
.mobile-host .roster{max-height:22vh}
.mobile-host .roster .seat-list{grid-template-columns:1fr 1fr;gap:.35rem}
.mobile-host .roster .seat{padding:.3rem .5rem;font-size:.78rem}
.mobile-host .roster .seat-tag{display:none}

/* ── 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}
.landing-account a.ghost-btn:visited{color:var(--ink-dim)}
.landing-account .acct-who{
  font-family:var(--mono); font-size:.8rem; color:var(--ink-dim);
  max-width:40vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
#room-who{ color:var(--ink); font-family:var(--mono); }

/* ── leaderboards overlay ─────────────────────────────────────────────── */
.lb-overlay{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;
  background:rgba(0,0,0,.6);backdrop-filter:blur(3px);padding:1rem}
.lb-overlay[hidden]{display:none}
.lb-card{width:min(560px,96vw);max-height:88vh;display:flex;flex-direction:column;
  background:var(--panel-solid);border:2px solid var(--line-hot);border-radius:var(--r);box-shadow:var(--shadow)}
.lb-head{display:flex;align-items:center;justify-content:space-between;padding:.9rem 1rem;border-bottom:1px solid var(--line)}
.lb-head h2{margin:0;font-family:var(--display);font-size:1rem;letter-spacing:.02em;color:var(--ink)}
.lb-x{background:none;border:0;color:var(--ink-dim);font-size:1.4rem;line-height:1;cursor:pointer}
.lb-x:hover{color:var(--ink)}
.lb-tabs{display:flex;gap:.3rem;overflow-x:auto;padding:.55rem .7rem;border-bottom:1px solid var(--line);flex:0 0 auto}
.lb-tab{flex:0 0 auto;background:var(--panel);border:1px solid var(--line);color:var(--ink-dim);
  border-radius:var(--r-sm);padding:.35rem .6rem;font-size:.72rem;cursor:pointer;white-space:nowrap}
.lb-tab:hover{color:var(--ink)}
.lb-tab.on{background:var(--brand);border-color:var(--brand);color:#0b0a0d}
.lb-body{overflow-y:auto;padding:.5rem .8rem 1rem}
.lb-col,.lb-row{display:grid;grid-template-columns:2.2rem 1fr auto;gap:.6rem;align-items:center;padding:.4rem .4rem}
.lb-col{color:var(--ink-faint);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;border-bottom:1px solid var(--line)}
.lb-row{border-bottom:1px solid var(--line);font-size:.9rem;color:var(--ink)}
.lb-row .lb-n{font-family:var(--mono);color:var(--ink-faint)}
.lb-row .lb-r{font-family:var(--mono);color:var(--brand-2);text-align:right}
.lb-me{background:rgba(168,85,247,.12);border-radius:var(--r-sm)}
.lb-me .lb-n,.lb-me span{color:var(--ink);font-weight:600}
.lb-empty,.lb-load{color:var(--ink-faint);text-align:center;padding:1.4rem;font-size:.85rem}
.lb-foot{margin:0;padding:.6rem 1rem;border-top:1px solid var(--line);color:var(--ink-faint);font-size:.74rem;text-align:center}
.lb-foot:empty{display:none}

/* ── 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}
.random-cta-note{font-size:.75rem;color:var(--ink-faint)}
.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}
