/* ============================================================================
   Behrupiya Gang — "Voltage Minimal"
   Refined new-age-tech aesthetic: bone / near-black canvas, electric-lime accent,
   single coral danger signal. Clash Display + Satoshi + JetBrains Mono.
   Hairline borders over shadows, generous space, subtle grain, staggered reveals.
   Class names are consumed by play.js / common.js — do not rename.
   ========================================================================== */

/* ---- Tokens -------------------------------------------------------------- */
:root {
  --font-display: 'Clash Display', ui-sans-serif, system-ui, sans-serif;
  --font-body: 'Satoshi', ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* light theme (default) */
  --bg: #ECEAE1;
  --surface: #FAF9F4;
  --surface-2: #F2F0E8;
  --ink: #131316;
  --ink-2: rgba(19, 19, 22, 0.70);
  --ink-3: rgba(19, 19, 22, 0.48);
  --line: rgba(19, 19, 22, 0.13);
  --line-strong: rgba(19, 19, 22, 0.28);

  --accent: #C2F94C;          /* electric lime */
  --accent-press: #B2EE2E;
  --accent-ink: #161a07;      /* text on lime */
  --accent-glow: rgba(177, 240, 50, 0.55);

  --danger: #FF4634;
  --danger-ink: #2a0b07;           /* dark text on coral — AA on the light theme's coral fills */
  --danger-glow: rgba(255, 70, 52, 0.45);
  --danger-text: #B3261D;          /* coral-as-text on light surfaces (AA ≥4.5:1) */

  --spotlight: #131316;       /* dark "spotlight" panel (secret word, dialogs) */
  --spotlight-ink: #F6F5EF;
  --spotlight-line: rgba(246, 245, 239, 0.16);

  --r-card: 16px;
  --r-btn: 12px;
  --r-pill: 999px;

  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 72px;

  --content-max: 540px;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* type scale — body / UI text (display headings use per-element clamps) */
  --fs-2xs: 11px;   /* mono micro-labels: live, brandmark, footer */
  --fs-xs:  12px;   /* eyebrows, badges, field labels */
  --fs-sm:  14px;   /* hints / small body */
  --fs-md:  16px;   /* body base, buttons, chip names, counts */
  --fs-lg:  18px;   /* h3, large buttons, modal titles, emphasis */

  /* motion durations (transition scale; bespoke keyframe timings stay inline) */
  --dur-press: 0.12s;   /* tactile press */
  --dur-fast:  0.15s;   /* small chrome (chip controls) */
  --dur-base:  0.18s;   /* default control transition */
  --dur-ui:    0.2s;    /* chips, toggle */
  --dur-slow:  0.5s;    /* fills, staggered rise */

  /* elevation & glow — composed from the accent/danger glow tokens, so they
     resolve to the right value per theme at the point of use. */
  --ring-accent:        0 0 0 1px var(--accent);
  --ring-danger:        0 0 0 1px var(--danger);
  --glow-focus:         0 0 0 4px var(--accent-glow);            /* focus / pulse ring */
  --shadow-cta:         0 8px 30px -10px var(--accent-glow);     /* lifted lime button */
  --shadow-pop:         0 8px 28px -12px var(--accent-glow);     /* selected chip / armed lift */
  --shadow-hero:        0 22px 64px -22px var(--accent-glow);    /* game-over hero (win) */
  --shadow-hero-danger: 0 22px 64px -22px var(--danger-glow);    /* game-over hero (loss) */

  /* z-index scale (single source of stacking order) */
  --z-base:      1;     /* app content */
  --z-footer:    2;     /* homepage credit */
  --z-toggle:    50;    /* theme / 🖕 toggle */
  --z-fx:        60;    /* confetti / 👎 layer */
  --z-reconnect: 70;    /* reconnecting pill */
  --z-modal:     150;   /* bottom-sheet modal */
  --z-overlay:   200;   /* full-screen overlay (embedded game) */
}

[data-theme='dark'] {
  --bg: #08080A;
  --surface: #131317;
  --surface-2: #1A1A1F;
  --ink: #ECEAE2;
  --ink-2: rgba(236, 234, 226, 0.68);
  --ink-3: rgba(236, 234, 226, 0.46);
  --line: rgba(236, 234, 226, 0.13);
  --line-strong: rgba(236, 234, 226, 0.26);

  --accent: #CBFF3D;
  --accent-press: #BCF52A;
  --accent-ink: #12160a;
  --accent-glow: rgba(203, 255, 61, 0.45);

  --danger: #FF5749;
  --danger-ink: #1a0c0a;
  --danger-glow: rgba(255, 87, 73, 0.40);
  --danger-text: var(--danger);   /* coral reads fine as text on the dark surface */

  --spotlight: #17171C;
  --spotlight-ink: #F4F3EC;
  --spotlight-line: rgba(244, 243, 236, 0.14);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --bg: #08080A; --surface: #131317; --surface-2: #1A1A1F;
    --ink: #ECEAE2; --ink-2: rgba(236,234,226,0.68); --ink-3: rgba(236,234,226,0.46);
    --line: rgba(236,234,226,0.13); --line-strong: rgba(236,234,226,0.26);
    --accent: #CBFF3D; --accent-press: #BCF52A; --accent-ink: #12160a;
    --accent-glow: rgba(203,255,61,0.45);
    --danger: #FF5749; --danger-ink: #1a0c0a; --danger-glow: rgba(255,87,73,0.40); --danger-text: #FF5749;
    --spotlight: #17171C; --spotlight-ink: #F4F3EC; --spotlight-line: rgba(244,243,236,0.14);
  }
}

/* ---- Reset --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--ink);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100dvh;
  /* column layout so the footer sits in normal flow at the bottom (not fixed /
     floating over scrolling content). .app grows; the footer rests below it. */
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
  /* iOS: kill the grey tap flash + the 300ms double-tap-zoom delay */
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
button, input { font: inherit; color: inherit; }

/* visually-hidden but available to assistive tech (live region, off-screen headings) */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
/* on the homepage the visible <h1> is the page title, so hide the SR fallback there */
body[data-view='entry'] #appTitleSr { display: none; }
/* iOS Safari zooms when an input <16px is focused — inputs are 17px (safe). */
img { max-width: 100%; display: block; }

/* ---- Atmosphere: soft accent glow + fine grain -------------------------- */
body::before {
  content: '';
  position: fixed;
  inset: -20% -20% auto -20%;
  height: 60vh;
  background: radial-gradient(60% 80% at 50% 0%, var(--accent-glow), transparent 70%);
  opacity: 0.5;
  filter: blur(20px);
  pointer-events: none;
  z-index: 0;
}
body::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: var(--grain-blend, multiply);
}
[data-theme='dark'] body::after { --grain-blend: screen; opacity: 0.06; }

/* ---- App shell ----------------------------------------------------------- */
.app {
  position: relative;
  z-index: var(--z-base);
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  flex: 1 0 auto;   /* fill the column; the in-flow footer rests below */
  padding:
    calc(env(safe-area-inset-top) + var(--sp-8))   /* clearance for the fixed top-right toggle */
    calc(env(safe-area-inset-right) + var(--sp-5))
    var(--sp-6)                                     /* footer (below) owns the bottom + safe-area */
    calc(env(safe-area-inset-left) + var(--sp-5));
  display: flex;
  flex-direction: column;
  /* `safe` keeps short screens centered but avoids clipping/overlap when content
     is taller than the viewport (falls back to start-aligned + scroll). */
  justify-content: safe center;
  gap: var(--sp-5);
}

/* staggered entrance on each full (re)render */
@media (prefers-reduced-motion: no-preference) {
  .app > * { animation: rise var(--dur-slow) var(--ease) both; }
  .app > *:nth-child(1) { animation-delay: 0.02s; }
  .app > *:nth-child(2) { animation-delay: 0.08s; }
  .app > *:nth-child(3) { animation-delay: 0.14s; }
  .app > *:nth-child(4) { animation-delay: 0.20s; }
  .app > *:nth-child(5) { animation-delay: 0.26s; }
  .app > *:nth-child(n+6) { animation-delay: 0.30s; }
}
@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* ---- Typography ---------------------------------------------------------- */
h1, h2, h3 { font-family: var(--font-display); font-weight: 600; line-height: 1.02; letter-spacing: -0.02em; }
h1 { font-size: clamp(30px, 9vw, 52px); }
/* keep the homepage wordmark (+ its *️⃣) on a single line on phones */
body[data-view='entry'] h1 { white-space: nowrap; }
h2 { font-size: clamp(24px, 7vw, 34px); }
h3 { font-size: var(--fs-lg); letter-spacing: -0.01em; }
/* tiny keycap-asterisk superscript next to the homepage wordmark (also the
   hidden 4-tap easter-egg trigger — keep taps from selecting the title text) */
.title-star { font-size: 0.4em; vertical-align: super; line-height: 0; margin-left: 0.5em; user-select: none; -webkit-user-select: none; cursor: default; }

/* ── Homepage wordmark: Apple-style entrance — a soft accent light blooms BEHIND
      the letters and settles. Pure text-shadow → renders beneath the glyphs and
      CANNOT affect layout/overflow/viewport (so nothing else shifts). ── */
@media (prefers-reduced-motion: no-preference) {
  .name-glow .wordmark { animation: wordmarkBloom 6.5s cubic-bezier(0.33, 0, 0.2, 1) both; }
}
@keyframes wordmarkBloom {
  0%   { text-shadow: 0 0 0 rgba(0,0,0,0); }
  24%  { text-shadow: 0 0 46px var(--accent-glow), 0 0 22px var(--accent-glow), 0 0 9px var(--accent-glow); }
  58%  { text-shadow: 0 0 34px var(--accent-glow), 0 0 16px var(--accent-glow); }
  100% { text-shadow: 0 0 0 rgba(0,0,0,0); }
}

.hint {
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: 1.5;
}

.eyebrow, .tally-row__label, .badge, .pill {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

/* ---- Cards --------------------------------------------------------------- */
.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--sp-5);
}
.card--flat { background: transparent; border-color: transparent; padding: 0; }

/* the "spotlight" — dark precision panel for the secret word & dialogs */
.card--accent {
  background: var(--spotlight);
  color: var(--spotlight-ink);
  border: 1px solid var(--spotlight-line);
  overflow: hidden;
}
.card--accent .hint { color: rgba(246, 245, 239, 0.6); }
.card--accent .btn--ghost {
  color: var(--spotlight-ink);
  border-color: var(--spotlight-line);
}
.card--accent .btn--ghost:active { background: rgba(255, 255, 255, 0.06); }

/* ---- Buttons ------------------------------------------------------------- */
.btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 50px;
  padding: 0 var(--sp-5);
  border-radius: var(--r-btn);
  border: 1px solid transparent;
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-md);
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: transform var(--dur-press) var(--ease), background var(--dur-base), border-color var(--dur-base), opacity var(--dur-base), box-shadow var(--dur-base);
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.btn:active { transform: translateY(1px) scale(0.995); }
/* disabled reads as a flat, clearly-inactive neutral chip — never a murky, dimmed accent */
.btn:disabled {
  cursor: not-allowed;
  background: var(--surface-2);
  color: var(--ink-3);
  border-color: var(--line);
  box-shadow: none;
  opacity: 1;
  transform: none;
}
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.btn--primary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: transparent;
}
.btn--primary:not(:disabled):hover { background: var(--accent-press); box-shadow: var(--shadow-cta); }

.btn--secondary { background: var(--surface-2); color: var(--ink); border-color: var(--line); }
.btn--secondary:not(:disabled):hover { border-color: var(--line-strong); }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn--ghost:not(:disabled):hover { background: var(--surface-2); }

/* selected segment of the entry host/join toggle — neutral so lime stays the CTA */
.btn--toggle-on { background: var(--ink); color: var(--bg); border-color: transparent; }

.btn--danger { background: var(--danger); color: var(--danger-ink); }
.btn--danger:not(:disabled):hover { filter: brightness(1.05); }

/* outlined accent — transparent fill + 2px lime border, ink label (lime text fails on the light surface) */
.btn--outline { background: transparent; color: var(--ink); border-color: var(--accent); border-width: 2px; }
.btn--outline:not(:disabled):hover { border-color: var(--accent-press); box-shadow: var(--shadow-cta); }

.btn--block { width: 100%; }
.btn--lg { min-height: 58px; font-size: var(--fs-lg); }

/* ---- Bottom-sheet modal (vote confirm) ---------------------------------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);              /* above content, toggle, fx + reconnecting */
  display: flex;
  align-items: flex-end;                /* dock to the bottom — thumb reach */
  justify-content: center;
  padding: var(--sp-4);
  padding-bottom: calc(env(safe-area-inset-bottom) + var(--sp-4));
  background: rgba(8, 8, 10, 0.62);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.modal-backdrop > .card { width: 100%; max-width: var(--content-max); max-height: calc(100dvh - 2 * var(--sp-4)); overflow-y: auto; }
@media (prefers-reduced-motion: no-preference) {
  .modal-backdrop { animation: fadeIn var(--dur-base) var(--ease); }
  .modal-backdrop > .card { animation: sheetUp 0.28s var(--ease) both; }
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes sheetUp { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }

/* ---- Room code ----------------------------------------------------------- */
.room-code {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(28px, 10vw, 52px);
  letter-spacing: 0.04em;
  line-height: 1.05;
  color: var(--ink);
  text-align: center;
  max-width: 100%;
  overflow-wrap: anywhere;
  /* a tear-off "ticket" stub — the shareable hero of the lobby */
  display: inline-block;
  padding: var(--sp-3) var(--sp-5);
  border: 2px dashed var(--line-strong);
  border-radius: var(--r-btn);
  background: var(--surface-2);
}
@media (prefers-reduced-motion: no-preference) {
  .room-code--stamp { animation: stampIn 0.5s var(--ease) both; animation-delay: 0.14s; }
}
@keyframes stampIn {
  0%   { opacity: 0; transform: scale(1.16) rotate(-1.4deg); }
  55%  { opacity: 1; transform: scale(0.98) rotate(0.6deg); }
  100% { transform: scale(1) rotate(0); }
}

/* ---- Secret word --------------------------------------------------------- */
.big-word {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.0;
  letter-spacing: -0.03em;
  font-size: clamp(44px, 14vw, 72px);
}
@media (prefers-reduced-motion: no-preference) {
  .card--accent .big-word { animation: wordIn 0.6s var(--ease) both; animation-delay: 0.15s; }
}
@keyframes wordIn {
  from { opacity: 0; filter: blur(10px); transform: translateY(8px) scale(0.96); }
  to   { opacity: 1; filter: blur(0); transform: none; }
}

/* ---- Tap-to-reveal swivel card (clue/discuss secret word) ---------------- */
/* Single element (no preserve-3d / backface — those fail to paint on some
   engines). A rotateY swivel with the text swapped at the edge-on midpoint. */
.reveal-card { cursor: pointer; -webkit-tap-highlight-color: transparent; will-change: transform; }
.reveal-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.reveal-card.swivel { animation: swivel 0.46s var(--ease); }
@keyframes swivel {
  0%   { transform: perspective(900px) rotateY(0deg); }
  50%  { transform: perspective(900px) rotateY(90deg); }
  100% { transform: perspective(900px) rotateY(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .reveal-card.swivel { animation: none; }
}
/* first-time affordance: a soft accent-glow ring breathes until the player reveals once */
@media (prefers-reduced-motion: no-preference) {
  .reveal-card--hint { animation: hintPulse 2.4s var(--ease) infinite; }
}
@keyframes hintPulse {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50%      { box-shadow: var(--glow-focus); }
}

/* Non-reveal affordance: a very thin glowing line runs laps around the card's
   border while the secret word is hidden. Technique: a conic-gradient ring,
   masked to just a hairline border, whose bright head (with a short fading
   tail) sweeps the perimeter as its start angle animates 0→360°. The line sits
   1px inside the edge so its lime bloom reads inward (the card clips overflow).
   Registering the angle as a typed @property is what lets it interpolate. */
@property --reveal-run-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
@media (prefers-reduced-motion: no-preference) {
  .reveal-card.border-run::before {
    content: "";
    position: absolute;
    inset: 1px;
    border-radius: inherit;
    padding: 1.5px;                 /* = line thickness */
    background: conic-gradient(from var(--reveal-run-angle),
      var(--accent) 0deg,
      var(--accent) 7.2deg,
      color-mix(in oklab, var(--accent) 45%, transparent) 28.8deg,
      transparent 60deg,
      transparent 360deg);
    -webkit-mask:
      linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    filter: drop-shadow(0 0 5px var(--accent-glow));
    pointer-events: none;
    animation: revealBorderRun 2.4s linear infinite;
  }
}
@keyframes revealBorderRun {
  to { --reveal-run-angle: 360deg; }
}

/* ---- Fields -------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: var(--sp-2); }
.field label, label {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: var(--fs-xs);
  color: var(--ink-2);
}
input {
  width: 100%;
  min-height: 52px;
  padding: 0 var(--sp-4);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-btn);
  color: var(--ink);
  font-size: var(--fs-md);  /* ≥16px avoids iOS focus-zoom */
  transition: border-color var(--dur-base), box-shadow var(--dur-base);
}
input::placeholder { color: var(--ink-2); } /* AA-legible, still clearly lighter than typed ink */
input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--glow-focus);
}

/* ---- Player chips / avatars --------------------------------------------- */
.player-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 6px 14px 6px 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  transition: border-color var(--dur-ui), transform var(--dur-ui) var(--ease), box-shadow var(--dur-ui);
}
.player-chip__name { font-weight: 500; font-size: var(--fs-md); max-width: 9.5em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.player-chip--out { opacity: 0.45; }
.player-chip--out .player-chip__name { text-decoration: line-through; }
.player-chip--turn {
  border-color: var(--accent);
  box-shadow: var(--ring-accent), var(--shadow-pop);
}

.avatar {
  display: inline-grid;
  place-items: center;
  width: 40px; height: 40px;
  flex: none;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  border: 1px solid var(--line);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  color: var(--ink);
  overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 34px; height: 34px; font-size: var(--fs-xs); }
.avatar--lg { width: 64px; height: 64px; font-size: 22px; }
.player-chip--turn .avatar { background: var(--accent); color: var(--accent-ink); border-color: transparent; }

/* host-only kick control on a player chip (two-tap: ✕ → "Remove?") */
.chip-kick {
  appearance: none;
  position: relative;        /* anchors the expanded hit area below */
  margin-left: 2px;
  min-height: 28px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--ink-2);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast), border-color var(--dur-fast);
}
/* keep the compact look but extend the tap target to ≥44px (WCAG 2.5.5) */
.chip-kick::after { content: ''; position: absolute; inset: -8px -6px; }
.chip-kick:hover { color: var(--danger); border-color: var(--danger); }
.chip-kick--armed { background: var(--danger); color: var(--danger-ink); border-color: transparent; }

/* a tapped vote candidate is "armed" (accent ring) while the confirm dialog asks to lock it in */
.vote-btn--armed { border-color: var(--accent); box-shadow: var(--ring-accent), var(--shadow-pop); }

/* ---- Pills & badges ------------------------------------------------------ */
.pill, .badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  font-size: var(--fs-xs);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--ink-2);
  white-space: nowrap;
}
.pill--accent, .badge--accent { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.badge--danger { background: var(--danger); color: var(--danger-ink); border-color: transparent; }
.badge--neutral { background: var(--ink); color: var(--bg); border-color: transparent; }

/* ---- Tally --------------------------------------------------------------- */
.tally-row {
  display: grid;
  grid-template-columns: minmax(72px, auto) 1fr auto;
  align-items: center;
  gap: var(--sp-3);
}
.tally-row__label { font-size: var(--fs-xs); color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tally-bar {
  display: block;
  height: 12px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  border: 1px solid var(--line);
  position: relative;
  overflow: hidden;
}
.tally-bar::after {
  content: '';
  position: absolute;
  inset: 0;
  width: var(--pct, 0%);
  background: var(--accent);
  border-radius: var(--r-pill);
  /* hairline edge so the lime fill is distinguishable from the track in light mode (1.4.11) */
  box-shadow: inset 0 0 0 1px var(--line-strong);
  transition: width var(--dur-slow) var(--ease);
}
.tally-row__count { font-family: var(--font-mono); font-weight: 700; font-size: var(--fs-md); min-width: 1.4em; text-align: right; }

/* ---- Game-over reveal: aligned name | badge | word columns --------------- */
.reveal-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) max-content max-content;
  align-items: center;
  gap: var(--sp-3) var(--sp-3);
}
.reveal-grid .player-chip__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.reveal-grid__badge { justify-self: start; }   /* align all badges' left edges */
.reveal-grid .hint { justify-self: start; white-space: nowrap; }

/* ---- Standings (game-over leaderboard) ---------------------------------- */
/* Names/points stay full-contrast ink in both themes; the lime highlight is a
   rank chip, never lime body text (which is illegible on the light surface). */
.standing { padding: 2px 0; }
.standing__rank {
  display: inline-block;
  min-width: 1.7em;
  margin-right: 10px;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--fs-sm);
  text-align: center;
  color: var(--ink-2);
}
.standing__rank--top {
  color: var(--accent-ink);
  background: var(--accent);
  border-radius: var(--r-pill);
  padding: 2px 8px;
}
/* the viewer's own row on the final scorecard */
.standing--you {
  background: var(--accent-glow);
  border-radius: var(--r-btn);
  padding: var(--sp-2) var(--sp-3);
  margin: 0 calc(-1 * var(--sp-3));
}
.standing__you {
  margin-left: var(--sp-2);
  padding: 1px 8px;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  vertical-align: middle;
}
.footer-link {
  color: var(--ink-3);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
}
.footer-link:hover { color: var(--ink-2); border-color: var(--accent); }

/* ---- Banners / results --------------------------------------------------- */
.banner {
  border-radius: var(--r-card);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  font-weight: 500;
}
.banner--accent { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.banner--danger { background: var(--danger); color: var(--danger-ink); border-color: transparent; }
.banner--neutral { background: var(--ink); color: var(--bg); border-color: transparent; }

/* game-over hero result — the payoff. Win = electric lime, loss = coral, each with its glow. */
.banner--win {
  background: var(--accent); color: var(--accent-ink); border-color: transparent;
  box-shadow: var(--shadow-hero), var(--ring-accent);
}
.banner--loss {
  background: var(--danger); color: var(--danger-ink); border-color: transparent;
  box-shadow: var(--shadow-hero-danger), var(--ring-danger);
}
@media (prefers-reduced-motion: no-preference) {
  .banner--win, .banner--loss { animation: resultPop 0.6s var(--ease) both; }
}
@keyframes resultPop {
  0%   { opacity: 0; transform: scale(0.92) translateY(10px); }
  55%  { opacity: 1; transform: scale(1.03); }
  100% { transform: scale(1); }
}

.result { border-radius: var(--r-card); padding: var(--sp-6) var(--sp-5); text-align: center; }
.result__title { font-family: var(--font-display); font-weight: 700; font-size: clamp(34px, 12vw, 60px); line-height: 0.98; letter-spacing: -0.03em; }

/* ---- Live indicator / spinner ------------------------------------------- */
.live {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: var(--fs-2xs);
  color: var(--ink-2);
}
.live::before {
  content: '';
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 var(--accent-glow);
}
@media (prefers-reduced-motion: no-preference) {
  .live::before { animation: ping 1.6s var(--ease) infinite; }
}
@keyframes ping {
  0% { box-shadow: 0 0 0 0 var(--accent-glow); }
  70% { box-shadow: 0 0 0 8px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
.spinner {
  width: 22px; height: 22px;
  border: 2px solid var(--line-strong);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---- Theme toggle -------------------------------------------------------- */
.theme-toggle {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 14px);
  right: calc(env(safe-area-inset-right) + 14px);
  z-index: var(--z-toggle);
  width: 46px; height: 46px;
  display: grid;
  place-items: center;
  border-radius: var(--r-pill);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  transition: transform var(--dur-ui) var(--ease), border-color var(--dur-ui);
}
.theme-toggle:hover { border-color: var(--accent); transform: rotate(-12deg); }

/* Embedded "The Fingering" overlay (hidden trigger: 4 quick taps on the
   homepage wordmark's *️⃣ — see the inline script in index.html) */
.tingu-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background: var(--bg);
  display: flex;
  flex-direction: column;
}
.tingu-overlay[hidden] { display: none; }
.tingu-bar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: calc(env(safe-area-inset-top) + 10px) calc(env(safe-area-inset-right) + 14px) 10px
           calc(env(safe-area-inset-left) + 16px);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.tingu-bar .ttl {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: var(--fs-2xs);
  color: var(--ink-2);
}
.tingu-overlay iframe { flex: 1; width: 100%; border: 0; display: block; }
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: block; }

/* Theme toggle = circular reveal (View Transitions API). The JS animates the new
   snapshot's clip-path from the button outward; disable the default cross-fade so
   only the ripple shows. */
::view-transition-old(root),
::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(root),
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none !important; }
}
[data-theme='dark'] .theme-toggle .icon-sun { display: block; }
[data-theme='dark'] .theme-toggle .icon-moon { display: none; }

/* ---- Layout helpers (consumed by play.js) ------------------------------- */
.stack { display: flex; flex-direction: column; gap: var(--sp-4); }
.stack--tight { gap: var(--sp-2); }
.stack--loose { gap: var(--sp-6); }
.row { display: flex; gap: var(--sp-3); align-items: center; }
.row--wrap { flex-wrap: wrap; }
.row--between { justify-content: space-between; }
.center { text-align: center; display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); }
/* a centered title sitting at the very top of a view must clear the fixed
   theme/🖕 toggles in the top-right (symmetric inset keeps it centered). */
.app > .center:first-child:has(> h2),
.app > h1:first-child,
.app > h2:first-child { padding-inline: 52px; }

/* tablist (entry host/join toggle) sits inside a .row */
[role='tablist'] { width: 100%; }
[role='tablist'] .btn { flex: 1; }

/* ---- iPad / large screens: a wider, roomier column (still a focused card) - */
@media (min-width: 768px) {
  :root { --content-max: 620px; }
  .app { gap: var(--sp-6); padding-top: calc(env(safe-area-inset-top) + var(--sp-8)); }
  .card { padding: var(--sp-6); }
  h1 { font-size: clamp(44px, 7vw, 64px); }
  .btn { min-height: 54px; }
  .btn--lg { min-height: 62px; }
}
/* very wide / desktop: cap so it never feels stretched */
@media (min-width: 1100px) {
  :root { --content-max: 660px; }
}

/* ---- Clue / discuss screen: a single, non-scrolling view on phones --------
   This screen (secret word + round + controls) must fit one viewport at default
   zoom with no scroll. Tighten padding, gaps, card insets and the hero word so
   the whole stack stays inside 100dvh; `justify-content: center` then keeps it
   vertically balanced. Top padding still clears the fixed top-right toggle. */
body[data-phase='clue'] .app {
  padding-top: calc(env(safe-area-inset-top) + 66px);
  padding-bottom: var(--sp-2);   /* the in-flow footer below owns the bottom + safe-area */
  gap: var(--sp-2);
}
/* Scope the card tightening to the screen's own cards (direct children of the
   app) so it never leaks into the swap-word consent modal, which is also a
   `.card.stack` but lives nested inside a fixed .modal-backdrop. */
body[data-phase='clue'] .app > .card { padding: var(--sp-4); }
body[data-phase='clue'] .hint { line-height: 1.4; }
/* The secret-word card is the hero — let it stay generous: larger word, roomier
   padding and a min-height so it dominates, while the screen still never scrolls.
   Uses `.app >` so it outranks the `.app > .card` tightening above (same scope). */
body[data-phase='clue'] .app > .reveal-card {
  padding: var(--sp-6) var(--sp-5);
  min-height: clamp(168px, 22vh, 212px);
  justify-content: center;
}
body[data-phase='clue'] .big-word { font-size: clamp(42px, 13vw, 66px); }
/* Trim the supporting elements so the hero can stay big yet the whole screen
   still fits the *usable* viewport once Safari's chrome + the Dynamic Island
   safe-area are subtracted (which a full-height preview doesn't account for). */
body[data-phase='clue'] h2 { font-size: clamp(20px, 5.5vw, 26px); }
body[data-phase='clue'] .app > .card:has(h2) { padding: var(--sp-3); }   /* the "Words are in" box */
body[data-phase='clue'] .app > .card.center { gap: var(--sp-2); }        /* tighten its rows */
body[data-phase='clue'] .app > .card.stack { padding: var(--sp-3); gap: var(--sp-2); }   /* the host controls */
body[data-phase='clue'] .app > .card.stack .btn--lg { min-height: 48px; font-size: var(--fs-md); }

/* Shorter / tall-chrome phones: the generous hero would overflow once Safari's
   toolbar + safe-areas are subtracted, so dial the secret-word card back to a
   compact-but-still-prominent size. The cutoff is by logical height, which on
   iOS maps to the LARGE viewport — devices ≤830pt (iPhone SE, Plus, and the
   812pt notch phones like X / 11 Pro / 13 mini, whose chrome eats ~120pt) get
   the compact hero; 844pt+ (12–17, Pro Max) keep the big one. Every size then
   stays a single, non-scrolling view. */
@media (max-height: 830px) {
  body[data-phase='clue'] .app > .reveal-card { padding: var(--sp-4); min-height: 0; }
  body[data-phase='clue'] .big-word { font-size: clamp(34px, 11vw, 52px); }
}

/* landscape on short phones: tighten vertical padding so content fits */
@media (max-height: 460px) and (orientation: landscape) {
  .app {
    padding-top: calc(env(safe-area-inset-top) + var(--sp-4));
    padding-bottom: calc(env(safe-area-inset-bottom) + var(--sp-6));
    gap: var(--sp-3);
  }
}

/* ---- Reconnecting pill (mid-game poll failures) -------------------------- */
.reconnecting {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 14px);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-reconnect);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: var(--r-pill);
  background: var(--spotlight);
  color: var(--spotlight-ink);
  border: 1px solid var(--spotlight-line);
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: var(--fs-2xs);
}

/* ---- Win/lose effects layer (confetti / 👎 rain) ------------------------- */
.fx-layer { position: fixed; inset: 0; z-index: var(--z-fx); pointer-events: none; overflow: hidden; }
.fx-layer .confetti {
  position: absolute; top: -24px;
  width: 9px; height: 14px; border-radius: 2px;
  opacity: 0.95;
  animation-name: fx-fall; animation-timing-function: linear; animation-fill-mode: forwards;
}
.fx-layer .emoji-drop {
  position: absolute; top: -32px;
  font-size: 26px; line-height: 1;
  animation-name: fx-fall-soft; animation-timing-function: linear; animation-fill-mode: forwards;
}
@keyframes fx-fall {
  0%   { transform: translateY(-10vh) rotate(0deg); }
  100% { transform: translateY(115vh) rotate(620deg); }
}
@keyframes fx-fall-soft {
  0%   { transform: translateY(-10vh) rotate(-12deg); }
  50%  { transform: translateY(52vh) rotate(12deg); }
  100% { transform: translateY(115vh) rotate(-8deg); }
}
@media (prefers-reduced-motion: reduce) { .fx-layer { display: none; } }

/* ---- Persistent brandmark (anchors short screens; subtle background) ----- */
/* In-flow site footer: rests at the bottom of the column (after .app). On short
   screens it sits at the viewport bottom; on tall/scrolling screens it follows
   the content instead of floating over it. Owns the bottom safe-area inset. */
.brandmark {
  flex: none;
  text-align: center;
  padding: var(--sp-1) var(--sp-5) calc(env(safe-area-inset-bottom) + var(--sp-2));
  pointer-events: none;
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.34em;
  font-size: var(--fs-2xs);
  color: var(--ink-3);
  white-space: nowrap;
}

/* ---- Homepage footer credit (only on the entry screen) ------------------- */
.site-footer {
  flex: none;
  align-self: center;
  z-index: var(--z-footer);
  display: none;                 /* shown only on the entry view (see below) */
  align-items: center;
  gap: 8px;
  padding: var(--sp-3) var(--sp-5) calc(env(safe-area-inset-bottom) + var(--sp-3));
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.04em;
  color: var(--ink-2);
  white-space: nowrap;
}
.site-footer a { color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--line-strong); }
.site-footer a:hover { border-color: var(--accent); }
.claude-mark { width: 16px; height: 16px; flex: none; }

/* On the homepage: show the credit footer, hide the generic brandmark. */
body[data-view='entry'] .site-footer { display: inline-flex; }
body[data-view='entry'] .brandmark { display: none; }
