/* =================================================================
   1·4·24 — Dice Game
   ================================================================= */

/* =================================================================
   Design tokens — warm-nostalgic bar-dice aesthetic
   OKLCH-based, tinted neutrals, copper accent.
   See .impeccable.md for the design brief.
   ================================================================= */

:root {
  /* Surfaces — warm near-black, tinted slightly toward copper (hue 55).
     Deep bar / back-room / pendant-light register. No cold navy. */
  --bg: oklch(17% 0.012 55);
  --surface: oklch(23% 0.015 55);
  --surface-2: oklch(28% 0.015 55);
  --surface-3: oklch(34% 0.02 55);
  --border: oklch(40% 0.02 55);
  --border-strong: oklch(55% 0.03 55);

  /* Ink — warm cream, slight amber cast. Bumped line-height on dark
     per impeccable (light-on-dark reads as lighter weight). */
  --ink: oklch(94% 0.01 85);
  --ink-dim: oklch(75% 0.015 80);
  --ink-muted: oklch(58% 0.02 75);
  --ink-faint: oklch(42% 0.02 70);

  /* Copper — the sole accent. Used rarely. No glow.
     Old-name `gold` aliases keep existing class rules valid during transition. */
  --copper: oklch(62% 0.14 55);
  --copper-hi: oklch(72% 0.13 60);
  --copper-lo: oklch(42% 0.11 50);
  --copper-ink: oklch(22% 0.04 55);      /* text-on-copper */
  --copper-wash: oklch(62% 0.14 55 / 0.14);
  --gold: var(--copper);
  --gold-hi: var(--copper-hi);
  --gold-lo: var(--copper-lo);
  --gold-dim: var(--copper-wash);

  /* Forest — qualify/success. Muted, not neon. */
  --forest: oklch(52% 0.11 155);
  --forest-hi: oklch(62% 0.10 155);
  --forest-ink: oklch(20% 0.03 155);
  --forest-wash: oklch(52% 0.11 155 / 0.16);
  --emerald: var(--forest);
  --emerald-dim: var(--forest-wash);

  /* Oxblood — bust/danger. Deep red wine, not fire-engine red. */
  --oxblood: oklch(46% 0.14 22);
  --oxblood-hi: oklch(58% 0.14 22);
  --oxblood-ink: oklch(96% 0.02 25);
  --oxblood-wash: oklch(46% 0.14 22 / 0.16);
  --ruby: var(--oxblood);
  --ruby-dim: var(--oxblood-wash);

  /* Amber — idle presence state */
  --amber: oklch(70% 0.15 65);

  /* Felt — solid muted saloon green. No radial, no glow.
     Paired with a tiny SVG noise texture for fabric feel. */
  --felt-color: oklch(32% 0.045 150);
  --felt-edge: oklch(22% 0.04 150);
  --felt-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.04),
    0 2px 0 oklch(0% 0 0 / 0.6),
    0 16px 32px -12px oklch(0% 0 0 / 0.55);

  /* Ink-printed shadows — short, defined, slightly offset.
     Never soft radial glow blobs. */
  --shadow-press: 0 1px 0 oklch(0% 0 0 / 0.4), 0 2px 4px oklch(0% 0 0 / 0.3);
  --shadow-tag:   0 2px 0 oklch(0% 0 0 / 0.45), 0 4px 10px -2px oklch(0% 0 0 / 0.4);
  --shadow-lift:  0 4px 0 oklch(0% 0 0 / 0.45), 0 10px 22px -6px oklch(0% 0 0 / 0.5);
  --shadow-drop:  0 8px 0 oklch(0% 0 0 / 0.4), 0 18px 28px -10px oklch(0% 0 0 / 0.55);

  /* Text shadow for old compat — back-compat alias */
  --text: var(--ink);
  --text-dim: var(--ink-dim);
  --text-muted: var(--ink-muted);
  --text-faint: var(--ink-faint);

  /* Typography — chunky sign-painter display + warm literary body */
  --font-display: 'Bungee', 'Arial Black', ui-sans-serif, sans-serif;
  --font-body: 'Literata', 'Charter', 'Iowan Old Style', Georgia, serif;

  /* Modular scale — 1.333 ratio (perfect fourth) from 1rem base */
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.333rem;
  --text-xl: 1.777rem;
  --text-2xl: 2.369rem;
  --text-3xl: 3.157rem;

  /* 4pt spacing scale, semantic names */
  --space-2xs: 4px;
  --space-xs: 8px;
  --space-sm: 12px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;
  --space-2xl: 48px;
  --space-3xl: 64px;
  --space-4xl: 96px;

  /* Radii — smaller than before, printed-tag feel */
  --radius: 10px;
  --radius-sm: 6px;
  --radius-lg: 16px;

  /* Motion */
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-out-expo:  cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-quart:  cubic-bezier(0.5, 0, 0.75, 0);
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html, body {
  height: 100%;
}

body {
  font-family: var(--font-body);
  /* Warm near-black base with a subtle paper-grain noise. No radial
     spotlight — the background is a surface, not a light source. */
  background-color: var(--bg);
  background-image:
    radial-gradient(oklch(0% 0 0 / 0.25) 1px, transparent 1px),
    radial-gradient(oklch(0% 0 0 / 0.15) 1px, transparent 1px);
  background-size: 3px 3px, 7px 7px;
  background-position: 0 0, 1px 2px;
  background-attachment: fixed;
  color: var(--ink);
  line-height: 1.55;              /* light-on-dark bump */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
  min-height: 100dvh;
  overflow-x: hidden;
  /* Disable iOS/Android double-tap-to-zoom while preserving pinch zoom.
     Also removes the 300ms tap-delay. */
  touch-action: manipulation;
  /* Honor device safe areas (notch / home indicator) */
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* ============ FOCUS RINGS (a11y) ============ */
:focus { outline: none; }

:focus-visible {
  outline: 2px solid var(--copper);
  outline-offset: 2px;
  border-radius: 3px;
}

.dice:focus-visible,
.player-chip:focus-visible,
.chip:focus-visible {
  outline-offset: 4px;
}

button {
  font-family: inherit;
  font-size: inherit;
  cursor: pointer;
  border: none;
  background: none;
  color: inherit;
}

input {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

.hidden { display: none !important; }

.container {
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding: 32px 24px;
}

.screen {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* Card — a paper panel on the table. Solid surface, short drop shadow,
   no blur/glass effect (glassmorphism is on impeccable's no-list). */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-xl);
  box-shadow:
    0 1px 0 oklch(100% 0 0 / 0.03) inset,
    0 2px 0 oklch(0% 0 0 / 0.4),
    0 10px 22px -8px oklch(0% 0 0 / 0.5);
}

/* ============ BUTTONS — stamped ink on paper ============ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: 13px 22px;
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  transition:
    transform 0.12s var(--ease-out-quart),
    box-shadow 0.12s,
    background 0.15s,
    color 0.15s,
    opacity 0.15s;
  min-height: 46px;
  user-select: none;
  -webkit-user-select: none;
  white-space: nowrap;
  border: 1px solid transparent;
}

.btn-large {
  padding: 16px 28px;
  font-size: var(--text-base);
  min-height: 56px;
  border-radius: var(--radius);
  letter-spacing: 0.05em;
}

/* Block button — fills its form column. Primary form submits belong here. */
.btn-block {
  display: flex;
  width: 100%;
}

/* Primary buttons inside a form card default to block — no orphan-short CTAs. */
.lobby-create > .btn,
.setup-form > .btn,
.waiting-wrapper > .btn {
  display: flex;
  width: 100%;
}

/* Primary — a stamped copper key, no glow. Depth from a solid dark
   offset shadow (ink-bleed feel), not a halo. */
.btn-primary {
  background: var(--copper);
  color: var(--copper-ink);
  box-shadow:
    0 3px 0 0 var(--copper-lo),
    0 5px 10px -3px oklch(0% 0 0 / 0.5),
    inset 0 1px 0 oklch(100% 0 0 / 0.4);
}

@media (hover: hover) {
  .btn-primary:hover:not(:disabled) {
    transform: translateY(-2px);
    background: var(--copper-hi);
    box-shadow:
      0 5px 0 0 var(--copper-lo),
      0 8px 14px -4px oklch(0% 0 0 / 0.55),
      inset 0 1px 0 oklch(100% 0 0 / 0.5);
  }
}

.btn-primary:active:not(:disabled) {
  transform: translateY(2px);
  box-shadow:
    0 1px 0 0 var(--copper-lo),
    0 2px 6px -2px oklch(0% 0 0 / 0.5),
    inset 0 1px 0 oklch(100% 0 0 / 0.3);
}

.btn-primary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  filter: saturate(0.6);
}

/* Ghost — a thin copper-ink label on the dark surface. */
.btn-ghost {
  background: transparent;
  color: var(--ink-dim);
  border-color: var(--border);
}

@media (hover: hover) {
  .btn-ghost:hover {
    background: var(--surface);
    color: var(--ink);
    border-color: var(--copper);
  }
}

/* ============ MODE TABS ============ */

/* Segmented control at the top of the lobby + setup screens.
   Pinned paper tabs — active one stamped copper, inactive is ghosted ink.
   Lives in a flex wrapper that centers it at the very top of the screen. */
.mode-tabs {
  display: inline-flex;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 3px;
  gap: 2px;
  margin: 24px auto 0;
  align-self: center;
  /* Center the tabs within the full-width container */
  width: fit-content;
  display: flex;
  justify-self: center;
}

[data-screen="lobby"] .container,
[data-screen="setup"] .container {
  display: flex;
  flex-direction: column;
}

[data-screen="lobby"] .mode-tabs,
[data-screen="setup"] .mode-tabs {
  align-self: center;
  margin-bottom: var(--space-md);
}

.mode-tab {
  padding: 9px 20px;
  background: transparent;
  border: none;
  color: var(--ink-muted);
  font-family: var(--font-display);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  border-radius: 2px;
  transition: background 0.15s, color 0.15s, transform 0.12s var(--ease-out-quart);
  line-height: 1;
}

@media (hover: hover) {
  .mode-tab:not(.mode-tab--active):hover {
    color: var(--ink);
    background: var(--surface-2);
  }
}

.mode-tab--active {
  background: var(--copper);
  color: var(--copper-ink);
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}

.mode-tab--active:hover {
  color: var(--copper-ink);
}

/* ============ GAME-TYPE PICKER ============
   Two-option segmented control inside the Create Game card that swaps the
   visible settings panel below. Each option is a stamped paper tile with
   a large display label and a smaller body-font subtitle. Active tile
   flips to copper fill; inactive is ghost on surface. */
.game-type-picker {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin: var(--space-sm) 0 var(--space-md);
}

.game-type-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 14px 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--ink-muted);
  cursor: pointer;
  transition: background 0.12s var(--ease-out-quart),
              color 0.12s var(--ease-out-quart),
              border-color 0.12s var(--ease-out-quart),
              transform 0.12s var(--ease-out-quart);
  box-shadow: var(--shadow-press);
}

.game-type-option:active {
  transform: translateY(1px);
}

@media (hover: hover) {
  .game-type-option:not(.game-type-option--active):hover {
    color: var(--ink);
    border-color: var(--border-strong);
    background: var(--surface-2);
  }
}

.game-type-option--active {
  background: var(--copper);
  border-color: var(--copper-hi);
  color: var(--copper-ink);
  box-shadow: var(--shadow-tag);
}

.game-type-label {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  letter-spacing: 0.04em;
  line-height: 1;
}

.game-type-sub {
  font-family: var(--font-body);
  font-style: italic;
  font-size: var(--text-xs);
  opacity: 0.8;
  letter-spacing: 0.02em;
}

.settings-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.settings-panel--hidden {
  display: none;
}

.settings-blurb {
  margin: 0;
  font-size: var(--text-sm);
  font-style: italic;
  color: var(--ink-muted);
  font-family: var(--font-body);
  line-height: 1.4;
}

/* Tight two-option segmented control — used inside settings panels. */
.seg-control {
  display: inline-flex;
  gap: 4px;
  padding: 3px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.seg-option {
  padding: 6px 12px;
  background: transparent;
  border: none;
  color: var(--ink-muted);
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  border-radius: 2px;
  transition: background 0.12s, color 0.12s;
}

@media (hover: hover) {
  .seg-option:not(.seg-option--active):hover {
    color: var(--ink);
    background: var(--surface-2);
  }
}

.seg-option--active {
  background: var(--copper);
  color: var(--copper-ink);
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}

.seg-blurb {
  margin: 6px 0 0;
  font-family: var(--font-body);
  font-style: italic;
  font-size: var(--text-xs);
  color: var(--ink-muted);
  line-height: 1.4;
}

/* Plain number input with an inline suffix ("points", etc.) —
   matches the .money-input treatment but without the $ prefix. */
.number-input {
  display: flex;
  align-items: stretch;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.number-input input {
  flex: 1 1 auto;
  padding: 10px 12px;
  background: transparent;
  border: none;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--text-base);
  letter-spacing: 0.02em;
  min-width: 0;
}

.number-input input:focus {
  outline: none;
}

.number-suffix {
  display: inline-flex;
  align-items: center;
  padding: 0 12px;
  color: var(--ink-muted);
  font-family: var(--font-body);
  font-style: italic;
  font-size: var(--text-sm);
  border-left: 1px solid var(--border);
  background: var(--surface-2);
}

/* ============ GAME LIST: TYPE BADGES ============ */
.game-type-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 6px;
  margin-right: 8px;
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border-radius: 2px;
  vertical-align: middle;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}

.game-type-badge--dice {
  background: var(--forest-wash);
  color: var(--forest-hi);
  border: 1px solid var(--forest);
}

.game-type-badge--gin {
  background: var(--oxblood-wash);
  color: var(--oxblood-hi);
  border: 1px solid var(--oxblood);
}

/* ============ GIN GAME ============ */

.gin-wrapper {
  max-width: 1000px;
  margin: 0 auto;
  padding: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.gin-board {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  padding: var(--space-lg);
  min-height: 520px;
}

/* Player chip row on the gin game screen */
.gin-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--ink-dim);
}

.gin-chip--self   { border-color: var(--copper); color: var(--ink); }
.gin-chip--active { border-color: var(--copper-hi); background: var(--copper-wash); color: var(--ink); }
.gin-chip--gone   { opacity: 0.4; }

/* 1 card in hand = one discard away from winning the round. The table
   sees this chip pulse so the threat is visible to everyone. */
.gin-chip--going-out {
  animation: gin-going-out-pulse 1.5s var(--ease-out-quart) infinite;
}
@keyframes gin-going-out-pulse {
  0%, 100% { box-shadow: 0 0 0 0 oklch(62% 0.14 55 / 0); }
  50%      { box-shadow: 0 0 0 4px oklch(62% 0.14 55 / 0.35); }
}

/* Active-chip "thinking" affordance for spectators — a subtle ellipsis
   rendered via ::after on the active chip when it's not YOUR seat.
   Tells the table the active player is still deciding. */
.gin-chip--active:not(.gin-chip--self)::after {
  content: '\2026';
  margin-left: 6px;
  color: var(--copper-hi);
  font-weight: 700;
  animation: gin-thinking 1.4s steps(4, end) infinite;
  display: inline-block;
  width: 1.2em;
  text-align: left;
  overflow: hidden;
}
@keyframes gin-thinking {
  0%   { content: ''; }
  25%  { content: '.'; }
  50%  { content: '..'; }
  75%  { content: '...'; }
  100% { content: ''; }
}

.gin-chip__name  { font-weight: 600; }
.gin-chip__score {
  font-family: var(--font-display);
  color: var(--copper-hi);
  margin-left: 4px;
}
.gin-chip__hand {
  font-family: var(--font-body);
  font-style: italic;
  color: var(--ink-muted);
  font-size: 11px;
}
/* Running round-points — sits next to the cumulative score so players
   can watch this round's plays accumulate live instead of waiting for
   the round-end summary. Forest green keeps it visually distinct from
   the copper cumulative score. */
.gin-chip__round {
  font-family: var(--font-display);
  color: var(--forest-hi);
  font-size: 11px;
  padding: 1px 5px;
  background: var(--forest-wash);
  border: 1px solid var(--forest);
  border-radius: 2px;
  margin-left: 4px;
}
.gin-chip__badge {
  font-family: var(--font-display);
  font-size: 10px;
  padding: 1px 5px;
  background: var(--forest-wash);
  color: var(--forest-hi);
  border: 1px solid var(--forest);
  border-radius: 2px;
}

/* ---- Table: melds zone at top ---- */
.gin-melds-zone {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  min-height: 90px;
  padding: var(--space-sm);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  background: oklch(30% 0.04 150 / 0.35);
}

.gin-melds-zone__label {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.gin-melds-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

/* Per-player meld sections — "Alice · 5 cards left" header + meld rail */
.gin-melds-players {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.gin-player-melds {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.gin-player-melds__header {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: var(--font-body);
}

.gin-player-melds__name {
  font-weight: 700;
  color: var(--ink);
  font-size: var(--text-sm);
  letter-spacing: 0.01em;
}

.gin-player-melds--self .gin-player-melds__name {
  color: var(--copper-hi);
}

.gin-player-melds__count {
  font-style: italic;
  color: var(--ink-muted);
  font-size: var(--text-xs);
}

.gin-player-melds__list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: flex-start;
}

/* Extension tile — single card a player added onto someone else's meld.
   Lives in the extender's section with a small label linking to the
   original meld's owner so the scoring area stays "cards this player
   put down" even for extensions. */
.gin-ext {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  background: var(--felt-color);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-press);
}

.gin-ext__card {
  display: flex;
}

.gin-ext__label {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 10px;
  color: var(--ink-muted);
  white-space: nowrap;
}

.gin-meld__kind {
  font-family: var(--font-body);
  font-style: italic;
  color: var(--ink-muted);
  font-size: 11px;
  text-transform: lowercase;
  letter-spacing: 0.04em;
}

.gin-meld {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  background: var(--felt-color);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-press);
}

.gin-meld--targetable {
  cursor: pointer;
  border-color: var(--copper);
}

/* "Hint" targetable — the user has a single card selected that COULD
   extend this meld, but hasn't clicked the extend button yet. The ring
   is softer + dashed to signal "available" vs the solid-border
   extend-confirm state. Tapping a hint-mode meld still commits. */
.gin-meld--hint {
  border-style: dashed;
  animation: gin-meld-hint-pulse 1.8s var(--ease-out-quart) infinite;
}

@keyframes gin-meld-hint-pulse {
  0%, 100% { box-shadow: 0 0 0 0 oklch(62% 0.14 55 / 0); }
  50%      { box-shadow: 0 0 0 3px oklch(62% 0.14 55 / 0.28); }
}

@media (hover: hover) {
  .gin-meld--targetable:hover {
    background: var(--copper-wash);
  }
}

.gin-meld__cards {
  display: flex;
  gap: -24px;  /* cards overlap */
}

.gin-meld__owner {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 11px;
  color: var(--ink-muted);
}

.gin-meld-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-md);
  color: var(--ink-faint);
  font-family: var(--font-body);
  font-style: italic;
  font-size: var(--text-sm);
}

/* ---- Middle row: stock on the LEFT, discard fan to its right ---- */
.gin-middle {
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
  gap: var(--space-lg);
  padding: 0 var(--space-sm);
}

.gin-pile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-width: 0;                /* allow children with overflow to shrink */
}

/* Discard pile claims whatever horizontal space is left after the stock.
   Its inner fan scrolls independently. */
.gin-pile--discard {
  flex: 1 1 auto;
  align-items: stretch;
  min-width: 90px;
}

.gin-pile__label {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.gin-pile__count {
  font-family: var(--font-display);
  font-size: var(--text-sm);
  color: var(--copper-hi);
}

.gin-pile__cards {
  position: relative;
  width: 90px;
  height: 135px;
}

.gin-pile__cards .gin-card {
  position: absolute;
  left: 0;
  top: 0;
  width: 90px;
  height: 135px;
}

/* Discard pile is laid out as rows of 5 cards. The BOTTOM row always
   holds the newest 5; older cards stack into additional rows ABOVE.
   Within each row, cards overlap with the left edge (rank + suit)
   of each card peeking out from under the next.
   --rows, --row-h, --row-pull are CSS custom properties so JS only
   needs to set --rows; size/overlap values flow from CSS (and respond
   to media queries automatically). */
.gin-pile__cards--discard {
  --rows: 1;
  --row-h: 135px;
  --row-pull: 88px;
  --card-w: 90px;
  --card-overlap: 50px;
  width: auto;
  min-width: 90px;
  max-width: 100%;
  /* +6px buffer accounts for the padding-top inside the container —
     otherwise the last row's bottom edge can creep past the container's
     computed height and touch the count/take siblings below. */
  min-height: calc(var(--row-h) + (var(--rows) - 1) * (var(--row-h) - var(--row-pull)) + 6px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  padding-top: 6px;
  overflow: visible;
}

/* Rows stack VERTICALLY with heavy overlap: each subsequent row covers
   most of the row above, leaving ~38px peeking out — enough to fully
   clear the rank glyph at the top-left. The bottom row is the newest
   and shows in full. Sizes driven by the --row-h / --row-pull /
   --card-w / --card-overlap custom properties defined on the container
   so media queries can tune them in one place. */
.gin-discard-row {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
}

/* Compact tier — kicks in when the pile has 4+ rows so the whole stack
   still fits without pushing the hand off the screen. */
.gin-pile__cards--discard--compact {
  --row-h: 105px;
  --row-pull: 75px;
  --card-w: 70px;
  --card-overlap: 40px;
}

/* Ultra-compact tier for very deep piles (6+ rows). */
.gin-pile__cards--discard--ultra {
  --row-h: 82px;
  --row-pull: 58px;
  --card-w: 55px;
  --card-overlap: 32px;
}

.gin-pile__cards--discard .gin-discard-card {
  position: relative;
  flex: 0 0 auto;
  width: var(--card-w);
  height: var(--row-h);
  margin-left: calc(-1 * var(--card-overlap));    /* cards overlap left-to-right */
  border-radius: 4px;
  transition: transform 0.2s var(--ease-out-quart);
  box-shadow: var(--shadow-press);
}

.gin-discard-row {
  height: var(--row-h);
}

.gin-discard-row + .gin-discard-row {
  margin-top: calc(-1 * var(--row-pull));
}

.gin-discard-row .gin-discard-card:first-child {
  margin-left: 0;
}

/* Newest card in the whole pile lifts + gets a stronger shadow. */
/* Newest discard — emphasized via shadow only, NOT a vertical lift.
   The previous translateY(-2px) made the newest card sit higher than
   the cards under it in the same row, so a discard pile with 2+
   visible cards rendered visibly misaligned (A♠ at baseline, 9♥
   floating 2px above it). Shadow alone keeps it on the same row
   while still drawing the eye. */
.gin-pile__cards--discard .gin-discard-card--newest {
  box-shadow: var(--shadow-lift);
}

.gin-discard-card--takeable {
  cursor: pointer;
}

@media (hover: hover) {
  .gin-discard-card--takeable:hover {
    transform: translateY(-6px);
    box-shadow: var(--shadow-lift);
  }
}


.gin-discard-card--preview {
  outline: 2px solid var(--copper-hi);
  outline-offset: 0;
  transform: translateY(-6px) !important;
}

.gin-take-hint {
  font-family: var(--font-body);
  font-style: italic;
  font-size: var(--text-xs);
  color: var(--ink-muted);
  text-align: center;
  margin-top: 4px;
}

.gin-take-confirm {
  display: flex;
  gap: 6px;
  justify-content: center;
  margin-top: 6px;
}

/* Stock pile acts as its own draw affordance — tap/click the card to
   draw. A pulsing copper ring fires when the current player can draw;
   otherwise the pile sits quietly. Replaces the old floating "Draw"
   button that was a redundant second target. */
.gin-pile--stock .gin-pile__cards {
  position: relative;
  cursor: default;
  border-radius: 6px;
  transition: box-shadow 0.15s var(--ease-out-quart), transform 0.12s var(--ease-out-quart);
  outline: none;
}

.gin-pile__cards--drawable {
  cursor: pointer;
  animation: gin-drawable-pulse 1.6s var(--ease-out-quart) infinite;
}

@keyframes gin-drawable-pulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--copper-hi), 0 0 0 6px oklch(62% 0.14 55 / 0); }
  50%      { box-shadow: 0 0 0 2px var(--copper-hi), 0 0 0 6px oklch(62% 0.14 55 / 0.35); }
}

@media (hover: hover) {
  .gin-pile__cards--drawable:hover {
    transform: translateY(-2px);
  }
}

.gin-pile__cards--drawable:focus-visible {
  outline: 2px solid var(--copper-hi);
  outline-offset: 3px;
}

/* Matching affordance for the discard pile when it's acting as the
   "confirm discard" target (single card selected, legal to discard). */
.gin-pile__cards--discardable {
  cursor: pointer;
  animation: gin-discardable-pulse 1.2s var(--ease-out-quart) infinite;
  border-radius: 6px;
}

@keyframes gin-discardable-pulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--oxblood-hi), 0 0 0 6px oklch(45% 0.14 25 / 0); }
  50%      { box-shadow: 0 0 0 2px var(--oxblood-hi), 0 0 0 6px oklch(45% 0.14 25 / 0.3); }
}

.gin-pile__hint {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 11px;
  color: var(--copper-hi);
  text-align: center;
  min-height: 1.2em;
  margin-top: 2px;
}

.gin-take-count {
  display: flex;
  gap: 4px;
  justify-content: center;
  margin-top: 6px;
}

.gin-take-count button {
  padding: 4px 10px;
  font-size: 11px;
  min-height: 32px;
  min-width: 32px;
}

/* ---- Player hand fan ---- */
.gin-hand-zone {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.gin-hand-label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* Firm 7-card per-row cap. Keeps rows readable regardless of viewport:
   a fresh 7-card hand sits in a single row; pickups / redeals that push
   the hand above 7 wrap to additional rows of up to 7 each. */
.gin-hand {
  display: grid;
  grid-template-columns: repeat(7, auto);
  justify-content: center;
  gap: 6px;
  /* Top padding reserves room for .gin-card--selected (-10px) and
     .gin-card--just-drawn (-4px) lifts without escaping the dashed
     container. Without this, selected cards visually punched through
     the top border. */
  padding: 14px var(--space-sm) var(--space-sm);
  background: oklch(30% 0.04 150 / 0.25);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  min-height: 150px;
}

.gin-hand .gin-card {
  cursor: pointer;
  transition: transform 0.12s var(--ease-out-quart), box-shadow 0.12s var(--ease-out-quart);
  width: 96px;
  height: 144px;
}

/* Only freshly-dealt cards get the deal-in animation; existing cards
   stay put across state updates. Stagger is set via inline style in JS. */
.gin-hand .gin-card--new {
  animation: gin-card-deal 0.32s var(--ease-out-expo) backwards;
}

/* Newly-arrived cards (draw / take-discard) wear a copper outline for
   ~2.5s after they land so the player can spot what they just drew in
   the auto-sorted hand. Class is removed via JS timeout. */
.gin-hand .gin-card--just-drawn {
  outline: 2px solid var(--copper-hi);
  outline-offset: 0;
  /* Inner copper-wash ring was 4px and collided with adjacent cards at
     the 4-6px grid gap. 2px stays within the outline and doesn't
     spill into neighbors. */
  box-shadow: 0 0 0 2px var(--copper-wash), var(--shadow-lift);
  transform: translateY(-4px);
}

@keyframes gin-card-deal {
  0%   { opacity: 0; transform: translateY(24px) rotate(-6deg); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0) rotate(0); }
}

/* Flip-in: cards drawn individually from the stock pile do a brief
   face-down → face-up rotation. backface-visibility is false-positive
   on most browsers for pseudo-elements, so we fake the "back" with a
   half-width scaleX compression through the midpoint — the card briefly
   looks like an edge turning before revealing its face. Simpler and
   more robust than a true 3D flip + second card-back node. */
.gin-hand .gin-card--flip-in {
  animation: gin-card-flip-in 0.42s var(--ease-out-expo) backwards;
  transform-origin: center center;
}

@keyframes gin-card-flip-in {
  0%   { opacity: 0; transform: rotateY(-110deg) scale(0.88); }
  40%  { opacity: 1; transform: rotateY(-90deg) scale(0.92); }
  65%  { opacity: 1; transform: rotateY(-20deg) scale(0.98); }
  100% { opacity: 1; transform: rotateY(0) scale(1); }
}

/* Group chips — a horizontal row of labeled buttons above the hand,
   one per potential meld. Replaces the earlier "colored pip in the
   corner of each card" cue which was cryptic (players had to learn
   the color mapping). Chips spell out exactly what each group is
   ("three 7s", "5-6-7♥") AND are tappable: click to select that
   group's cards so the auto-validating play-meld button lights up. */
.gin-groups {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: center;
  margin-bottom: 6px;
  min-height: 0;
}
.gin-groups:empty {
  display: none;
}

.gin-group-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 12px;
  line-height: 1.2;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  transition: transform 0.1s var(--ease-out-quart), background 0.15s var(--ease-out-quart), border-color 0.15s var(--ease-out-quart);
}
.gin-group-chip:hover {
  transform: translateY(-1px);
}
.gin-group-chip:active {
  transform: translateY(0);
}

.gin-group-chip__kind {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 3px;
}
.gin-group-chip__label {
  font-weight: 600;
}

/* Valid-as-meld (≥3 cards) chips lean into the kind's color as a
   primary action affordance — these are plays the user can commit
   to right now. */
.gin-group-chip--valid.gin-group-chip--set {
  border-color: var(--forest);
  background: var(--forest-wash);
}
.gin-group-chip--valid.gin-group-chip--set .gin-group-chip__kind {
  background: var(--forest-hi);
  color: var(--surface);
}
.gin-group-chip--valid.gin-group-chip--run {
  border-color: oklch(55% 0.14 240);
  background: oklch(55% 0.14 240 / 0.15);
}
.gin-group-chip--valid.gin-group-chip--run .gin-group-chip__kind {
  background: oklch(58% 0.14 240);
  color: var(--surface);
}

/* Extend chips — copper, distinct from sets (green) and runs (blue),
   so the player can see at a glance "this is a play onto someone
   else's meld." Always valid (single card → existing meld). */
.gin-group-chip--extend {
  border-color: var(--copper);
  background: var(--copper-wash);
}
.gin-group-chip--extend .gin-group-chip__kind {
  background: var(--copper);
  color: var(--copper-ink, var(--surface));
}

/* "Armed" chip — first tap pulses it as a confirm affordance. The
   second tap commits. Without this, a stray finger graze on a
   tappable chip would auto-play a meld the user didn't intend.
   1.5 s timeout reverts to idle (handled in JS). */
.gin-group-chip--armed {
  animation: gin-chip-armed 0.6s var(--ease-out-quart) infinite;
  outline: 2px solid var(--copper-hi);
  outline-offset: 2px;
}
.gin-group-chip--armed::after {
  content: ' — tap again';
  font-style: italic;
  color: var(--copper-hi);
  margin-left: 4px;
  font-size: 11px;
}
@keyframes gin-chip-armed {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.04); }
}

/* Deadwood badge on YOUR chip — what you'd lose if the round ended
   right now. Oxblood (loss-color) so it reads as "cost," not "score." */
.gin-chip__deadwood {
  font-family: var(--font-display);
  color: var(--oxblood-hi);
  font-size: 11px;
  padding: 1px 5px;
  background: oklch(45% 0.14 25 / 0.18);
  border: 1px solid var(--oxblood);
  border-radius: 2px;
  margin-left: 4px;
}

/* Newly-played meld card slide-in — drops in from below (where the
   hand sits) into the meld's spot on the table. Staggered via
   inline animation-delay set in JS. ~340 ms duration so a 4-card
   meld lands cleanly inside half a second. */
.gin-meld__cards .gin-card--played-in,
.gin-ext__card .gin-card--played-in {
  animation: gin-card-played-in 0.34s var(--ease-out-expo) backwards;
  transform-origin: center bottom;
}
@keyframes gin-card-played-in {
  0%   { opacity: 0; transform: translateY(60px) scale(0.85) rotate(-4deg); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0) scale(1) rotate(0); }
}

/* "Near" (2-card) chips are informational — dashed border + muted
   ink so the player sees "you're one card away" without mistaking
   the chip for a ready-to-play meld. Still tappable (selects the
   pair) but no colored fill. */
.gin-group-chip--near {
  border-style: dashed;
  background: transparent;
  opacity: 0.75;
}
.gin-group-chip--near .gin-group-chip__kind {
  background: var(--surface-2);
  color: var(--ink-muted);
}

@media (max-width: 520px) {
  .gin-group-chip {
    font-size: 11px;
    padding: 3px 8px;
  }
  .gin-group-chip__kind {
    font-size: 9px;
    padding: 1px 4px;
  }
}

.gin-hand .gin-card:active {
  transform: translateY(-2px);
}

@media (hover: hover) {
  .gin-hand .gin-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-lift);
  }
}

.gin-card--selected {
  transform: translateY(-10px) !important;
  box-shadow: var(--shadow-lift) !important;
  outline: 2px solid var(--copper-hi);
  outline-offset: 0;
}

/* Must-play card pulses an oxblood ring — mustPlayCard was previously
   a flat box-shadow that was easy to miss in a crowded hand. The pulse
   makes the "you have to play this before discarding" constraint
   immediately legible. */
.gin-card--must-play {
  animation: gin-must-play-pulse 1.1s var(--ease-out-quart) infinite;
}

@keyframes gin-must-play-pulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--oxblood-hi), 0 0 0 4px oklch(45% 0.14 25 / 0), var(--shadow-press); }
  50%      { box-shadow: 0 0 0 2px var(--oxblood-hi), 0 0 0 8px oklch(45% 0.14 25 / 0.35), var(--shadow-press); }
}

.gin-card--dim {
  opacity: 0.55;
}

/* ---- Actions bar ---- */
.gin-actions {
  display: flex;
  justify-content: center;
  gap: var(--space-sm);
  flex-wrap: wrap;
}

.gin-hint {
  text-align: center;
  font-family: var(--font-body);
  font-style: italic;
  font-size: var(--text-sm);
  color: var(--ink-muted);
  min-height: 1.2em;
}

/* ---- Card primitive (SVG wrapper) ---- */
.gin-card {
  position: relative;
  display: inline-block;
  user-select: none;
  -webkit-user-select: none;
  box-shadow: var(--shadow-press);
  border-radius: 4px;
  overflow: hidden;
}

.gin-card__svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* Paper stock fill — warm cream, not pure white */
.gin-card__paper {
  fill: oklch(92% 0.022 85);
  stroke: oklch(42% 0.02 70);
  stroke-width: 0.4;
}

.gin-card__back-paper {
  fill: oklch(28% 0.04 150);
  stroke: oklch(42% 0.05 150);
  stroke-width: 0.4;
}

.gin-card__back-inner {
  stroke: oklch(52% 0.08 55);
  stroke-width: 0.4;
}

.gin-card__back-brand {
  font-family: var(--font-display);
  font-size: 8px;
  fill: oklch(62% 0.12 55);
  letter-spacing: 0.1em;
}

.gin-card__back-tagline {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 5px;
  fill: oklch(52% 0.08 55);
  letter-spacing: 0.2em;
}

/* Four-color deck:
     ♥ hearts   → red
     ♦ diamonds → blue
     ♣ clubs    → green
     ♠ spades   → black
   Inks are picked so each reads at a glance on the cream stock without
   feeling neon. */
.ink-h { fill: oklch(48% 0.22 27);   }  /* red  */
.ink-d { fill: oklch(44% 0.18 255);  }  /* blue */
.ink-c { fill: oklch(40% 0.15 150);  }  /* green */
.ink-s { fill: oklch(18% 0.02 55);   }  /* black */

/* HUGE rank in the top-left and (rotated) bottom-right, plus one big
   suit glyph in the center. Sized to scream legibility. */
.gin-card__rank {
  font-family: var(--font-display), 'Arial Black', sans-serif;
  font-size: 16px;
  font-weight: 900;
  letter-spacing: -0.04em;
}

.gin-card__suit {
  font-size: 32px;
}

/* Small card variant — for meld strips and discard peek */
.gin-card--sm {
  width: 44px;
  height: 66px;
}

.gin-card--xs {
  width: 30px;
  height: 45px;
}

/* Meld cards: overlap like a real fan */
.gin-meld__cards .gin-card {
  margin-left: -22px;
}
.gin-meld__cards .gin-card:first-child {
  margin-left: 0;
}

/* On mobile, pin the gin action buttons to the bottom of the viewport
   so the player never has to scroll past the hand to hit Play / Discard.
   Backdrop gradient so cards behind fade cleanly; respects safe-area
   for the home-indicator gesture zone. Desktop keeps it in flow.
   Scoped to #gin-actions (the DYNAMIC play-actions bar inside the
   gin-board) so the separate Leave-Table footer row doesn't also
   get pinned and stack on top. */
@media (max-width: 720px) {
  #gin-actions {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    padding: 10px calc(env(safe-area-inset-right) + 12px)
             calc(env(safe-area-inset-bottom) + 10px)
             calc(env(safe-area-inset-left) + 12px);
    background: linear-gradient(
      to top,
      oklch(18% 0.015 55 / 0.96) 40%,
      oklch(18% 0.015 55 / 0.78) 80%,
      oklch(18% 0.015 55 / 0) 100%
    );
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: center;
    margin: 0;
  }
  #gin-actions:empty {
    display: none;
  }
  /* Leave room at the bottom of the board for the fixed bar so the
     hand + history scroll above it instead of under. */
  .gin-board {
    padding-bottom: 88px;
  }
  .gin-history {
    margin-bottom: 0;
  }
  /* Leave-table footer — compact at the very bottom, out of the way of
     the fixed play-actions bar when there are no selections. */
  .gin-footer-row {
    display: flex;
    justify-content: center;
    padding: var(--space-sm) 0 calc(env(safe-area-inset-bottom) + 80px);
  }
}

/* Desktop / wide screens keep the leave-table row in the normal flow
   at the end of the game screen; the play-actions bar flows above it. */
.gin-footer-row {
  display: flex;
  justify-content: center;
  padding: var(--space-md) 0;
}

/* When the round-end or game-over overlay has content, it SHOULD be the
   center-of-attention. Hide everything below the middle zone (hand,
   actions, history, hint) so the scorecard + Play-again button appear
   where the cards would be. :has() is broadly supported in 2026. */
.gin-board:has(#gin-round-end-wrap:not(:empty)) .gin-hand-zone,
.gin-board:has(#gin-round-end-wrap:not(:empty)) .gin-actions,
.gin-board:has(#gin-round-end-wrap:not(:empty)) .gin-history,
.gin-board:has(#gin-round-end-wrap:not(:empty)) .gin-hint,
.gin-board:has(#gin-round-end-wrap:not(:empty)) .gin-groups {
  display: none !important;
}

/* Action history feed — small text strip below the action bar showing
   the last few actions. Useful for spectators and for the active
   player to verify their last move registered. Auto-scrolls to bottom. */
.gin-history {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: var(--space-xs);
  padding: 6px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: 11px;
  color: var(--ink-muted);
  max-height: 78px;
  overflow-y: auto;
  line-height: 1.35;
}
.gin-history:empty {
  display: none;
}
.gin-history__entry {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gin-history__actor {
  font-weight: 600;
  color: var(--ink);
}

/* Game-over overlay actions row — primary "Play again" + ghost "Back to
   lobby" side by side instead of just the lobby exit. */
.gin-round-end__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  justify-content: center;
}
.gin-round-end__hint {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-muted);
  text-align: center;
}

/* ---- Round end overlay ---- */
.gin-round-end {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-drop);
}

.gin-round-end__title {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  color: var(--copper-hi);
  margin: 0;
}

.gin-summary {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  max-width: 420px;
}

.gin-summary__row {
  display: grid;
  /* Extra leading column for the ready-state checkmark. */
  grid-template-columns: 18px 1fr auto auto auto;
  gap: var(--space-sm);
  padding: 8px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  align-items: baseline;
}

.gin-summary__row--winner {
  background: var(--copper-wash);
  border-color: var(--copper);
}

.gin-summary__ready {
  font-family: var(--font-display);
  color: var(--forest-hi);
  font-size: 14px;
  text-align: center;
}
.gin-summary__ready--pending {
  color: var(--ink-faint);
}

.gin-summary__name { color: var(--ink); font-weight: 600; }
.gin-summary__plays { color: var(--forest-hi); font-family: var(--font-display); font-size: 12px; }
.gin-summary__penalty { color: var(--oxblood-hi); font-family: var(--font-display); font-size: 12px; }

/* Game-over rows have a simpler name|score layout — the round-end
   grid's tiny 16px lead column (for the ready checkmark) would clip
   the name text here. Override to a clean 2-column layout. */
.gin-summary__row--gameover {
  grid-template-columns: 1fr auto !important;
}
.gin-summary__total {
  font-family: var(--font-display);
  color: var(--ink);
  font-size: var(--text-base);
}

/* --- Mobile layout for the gin board --- */
/* Intermediate viewport — tablet landscape and smaller laptops. Hand
   cards need to shrink from 96x144 so 7 cards across fit inside the
   available width (roughly 612px of hand container at 768px viewport:
   7*96 + 6*6 = 708px of grid blows through). 78x117 keeps the card
   ratio and fits 7-per-row through ~720px. */
@media (max-width: 960px) {
  .gin-hand .gin-card {
    width: 78px;
    height: 117px;
  }
  .gin-hand {
    min-height: 125px;
  }
}

@media (max-width: 720px) {
  .gin-wrapper {
    padding: var(--space-sm);
    gap: var(--space-sm);
  }
  .gin-board {
    padding: var(--space-sm);
    gap: var(--space-md);
    min-height: 0;
  }
  .gin-middle {
    gap: var(--space-md);
  }
  /* Scope the mobile pile-shrink to the STOCK pile only. The discard
     pile is sized by the --card-w / --row-h custom-property tiers
     defined on the container and must NOT be clobbered by this generic
     60x90 override — otherwise discard cards render at 60px wide while
     `margin-left: calc(-1 * var(--card-overlap))` still uses the
     tier's 22-32px overlap, making the pile overrun its container and
     overlap the hand / count badge. */
  .gin-pile--stock .gin-pile__cards {
    width: 60px;
    height: 90px;
  }
  .gin-pile--stock .gin-pile__cards .gin-card {
    width: 60px;
    height: 90px;
  }
  .gin-hand {
    padding: 10px 6px 6px;     /* top padding for selected-card lift */
    min-height: 110px;
    /* 8px gap — with a 6px selected lift on mobile, a 4px gap leaves
       row 2's lifted card overlapping row 1's lower edge. 8px is the
       smallest gap where the lift no longer collides. */
    gap: 8px;
  }
  .gin-hand .gin-card {
    width: 60px;
    height: 90px;
  }
  .gin-card--sm {
    /* Bumped from 36x54 so tap target ≥44px (WCAG / iOS guideline).
       36px wide cards were below the touch-target minimum and kept
       mis-firing on extend-meld clicks. */
    width: 44px;
    height: 66px;
  }
  .gin-meld__cards .gin-card {
    margin-left: -22px;
  }
  .gin-meld {
    padding: 6px;
  }
  .gin-melds-zone {
    min-height: 72px;
    padding: 6px;
  }
  .gin-take-count button {
    min-height: 40px;    /* tap target */
    padding: 6px 10px;
  }
  .gin-actions {
    gap: 8px;
  }
  .gin-actions .btn {
    flex: 1 1 40%;
    min-width: 0;
  }
  .gin-hint {
    font-size: var(--text-xs);
  }
  .game-header {
    padding: var(--space-xs) var(--space-sm);
  }
  /* On narrow screens, player chips stack vertically and the whole
     strip wraps onto its own row below the round/target pills. Two
     wide chips (name + D + score + +round) don't fit horizontally
     next to the left-side pills at 390px. Vertical stack with full
     container width avoids the chip-overflow clipping entirely. */
  .game-header {
    flex-wrap: wrap;
  }
  .game-header .header-right {
    /* Full-width override of the base `.header-right { flex: 1 }` rule
       further down in the file. Needs higher specificity (0,2,0 vs the
       default's 0,1,0) because source-order cascade would otherwise
       pick the later `flex: 1` over our media-query rule. */
    flex: 0 0 100%;
    min-width: 0;
  }
  #gin-players-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    justify-content: flex-start;
    width: 100%;
  }
  .gin-chip {
    padding: 3px 8px;
    font-size: 12px;
    flex: 0 1 auto;
    min-width: 0;
  }
  /* Opponents' hand count is visible via their on-table cards + round
     points; on narrow screens it's not worth the horizontal cost. */
  .gin-chip__hand {
    display: none;
  }
  .gin-chip__round {
    font-size: 10px;
    padding: 0 4px;
  }
  .gin-summary__row {
    grid-template-columns: 16px 1fr auto auto;
    font-size: var(--text-xs);
  }
  .gin-summary__penalty {
    display: none;    /* merge into total visually on tight screens */
  }
}

/* 520px, not 480px: 7x60 cards + 6*4 gap + 2*6 pad + container paddings
   pushes the single-row hand past ~503px of required width, which spills
   on real-world landscape phones 481-519px. Drop to the 4-column layout
   earlier so the hand always fits. */
@media (max-width: 520px) {
  .gin-hand .gin-card {
    width: 50px;
    height: 75px;
  }
  /* On mobile 7-across overflows. Drop to 4 columns — a fresh 7-card
     hand becomes 2 rows (4+3), pickups wrap to more. */
  .gin-hand {
    grid-template-columns: repeat(4, 50px);
    gap: 8px;                  /* clear the -6px selected-card lift */
    padding: 10px 6px 6px;     /* reserve top for -lift transforms */
  }
  /* Cards in the second row get a -10px selected lift that would otherwise
     cross into row 1's lower edge (gap is only 4px). Scale lift down so
     rows don't collide on narrow viewports. */
  .gin-card--selected {
    transform: translateY(-6px) !important;
  }
  /* Stock card scales down with the rest of the UI. */
  .gin-pile--stock .gin-pile__cards,
  .gin-pile--stock .gin-pile__cards .gin-card {
    width: 60px;
    height: 90px;
  }
  /* Discard pile also shrinks; `--card-overlap` drops too so cards don't
     fully cover each other at narrower widths (50 - 50 would mean 0 of
     the card behind is visible). */
  .gin-pile__cards--discard {
    --row-h: 75px;
    --row-pull: 50px;
    --card-w: 50px;
    --card-overlap: 30px;
  }
  .gin-pile__cards--discard--compact {
    --row-h: 60px;
    --row-pull: 42px;
    --card-w: 42px;
    --card-overlap: 25px;
  }
  .gin-pile__cards--discard--ultra {
    /* Widened from 34x48 to 40x60 so the rank glyph (sits in the top-left
       ~16px square of each card) isn't clipped by the next card's
       overlap. Overlap scales up correspondingly so rows still fit. */
    --row-h: 60px;
    --row-pull: 42px;
    --card-w: 40px;
    --card-overlap: 22px;
  }
  .game-type-picker {
    grid-template-columns: 1fr;
  }
}


/* ============ SETUP SCREEN ============ */

.setup-header {
  text-align: center;
  margin-bottom: 36px;
  padding-top: var(--space-md);
}

.brand {
  display: inline-block;
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 400;
  letter-spacing: 0.3em;
  color: var(--copper);
  text-transform: uppercase;
  margin-bottom: var(--space-lg);
  padding: 4px 10px;
  border: 1px solid var(--copper-lo);
  border-radius: 2px;              /* gummed-paper corner */
  background: transparent;
}

/* Logo — no gradient text (impeccable absolute ban). The display font's
   own presence does the work; a printed ink color + a short warm offset
   shadow sells "stamped on paper." */
.logo {
  font-family: var(--font-display);
  font-size: clamp(64px, 12vw, 128px);
  font-weight: 400;              /* Bungee is single-weight */
  letter-spacing: 0;
  line-height: 0.9;
  margin-bottom: 20px;
  color: var(--copper-hi);
  /* Ink-printed shadow: short, warm, defined — not a glow. */
  text-shadow:
    2px 2px 0 var(--copper-lo),
    4px 4px 0 oklch(0% 0 0 / 0.5);
}

.logo span {
  display: inline-block;
}

/* Tiny rotation variance per digit — hand-painted sign feel. */
.logo span:nth-child(1) { transform: rotate(-2deg); }
.logo span:nth-child(3) { transform: rotate(1.5deg); }
.logo span:nth-child(5) { transform: rotate(-1deg); }

.tagline {
  font-family: var(--font-body);
  font-size: var(--text-lg);
  color: var(--ink-dim);
  line-height: 1.5;
  font-style: italic;
  letter-spacing: -0.01em;
}

.tagline .g {
  color: var(--copper-hi);
  font-weight: 700;
  font-style: normal;
}

.setup-grid {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: 24px;
  align-items: start;
}

.setup-form {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.field-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-muted);
}

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}

.count-selector {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 8px;
}

.count-btn {
  padding: 13px 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--ink-dim);
  font-weight: 400;
  font-size: var(--text-base);
  font-family: var(--font-display);
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

@media (hover: hover) {
  .count-btn:hover {
    background: var(--surface-2);
    color: var(--ink);
    border-color: var(--border-strong);
  }
}

.count-btn.active {
  background: var(--copper);
  border-color: var(--copper);
  color: var(--copper-ink);
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}

.player-inputs {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.player-input {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 2px 14px 2px 14px;
  transition: border-color 0.15s, background 0.15s;
}

.player-input:focus-within {
  background: var(--surface-2);
  border-color: var(--copper);
  box-shadow: inset 0 0 0 1px var(--copper);
}

.player-num {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--copper);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  min-width: 20px;
}

.player-input input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  padding: 12px 0;
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--ink);
  min-width: 0;
}

.money-input {
  display: flex;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0 14px;
  transition: border-color 0.15s, background 0.15s;
}

.money-input:focus-within {
  background: var(--surface-2);
  border-color: var(--copper);
  box-shadow: inset 0 0 0 1px var(--copper);
}

.money-input .currency {
  color: var(--ink-muted);
  font-family: var(--font-display);
  font-size: var(--text-base);
  margin-right: 6px;
}

.money-input input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  padding: 12px 0;
  font-family: var(--font-display);
  font-size: var(--text-base);
  color: var(--ink);
  width: 100%;
  min-width: 0;
}

.money-input input::-webkit-outer-spin-button,
.money-input input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.money-input input[type="number"] {
  -moz-appearance: textfield;
}

.play-money-note {
  text-align: center;
  font-size: 12px;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}

/* ============ TOGGLE SWITCH ============ */

/* Grouped toggle + expandable explanation. Click the (i) to reveal. */
.toggle-group {
  display: flex;
  flex-direction: column;
}

.field > .toggle-group + .toggle-group .toggle-row {
  border-top: 1px solid var(--border);
}

.toggle-info {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  padding: 0;
  margin-right: 2px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--ink-muted);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  font-style: italic;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.toggle-info:hover,
.toggle-info[aria-expanded="true"] {
  color: var(--copper-hi);
  border-color: var(--copper);
  background: var(--copper-wash);
}

.toggle-info-text {
  margin: 6px 0 10px 0;
  padding: 10px 12px;
  background: var(--surface-2);
  border-left: 2px solid var(--copper);
  color: var(--ink-dim);
  font-size: 13px;
  line-height: 1.5;
  border-radius: 0 3px 3px 0;
}

.toggle-info-text[hidden] {
  display: none;
}

/* Flat row — no background or border. Lives inside its parent card.
   Multiple toggle-rows in a .field form a grouped list via a hairline
   divider between siblings. */
.toggle-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 0;
  background: transparent;
  border: none;
  border-radius: 0;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  transition: color 0.15s;
}

.field > .toggle-row + .toggle-row {
  border-top: 1px solid var(--border);
}

@media (hover: hover) {
  .toggle-row:hover .toggle-title {
    color: var(--copper-hi);
  }
}

.toggle-row input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}

.toggle-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.toggle-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.2;
}

.toggle-desc {
  font-size: 11px;
  color: var(--text-muted);
  line-height: 1.4;
}

.toggle-switch {
  width: 46px;
  height: 26px;
  background: oklch(100% 0 0 / 0.08);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  position: relative;
  flex-shrink: 0;
  transition: background 0.2s, border-color 0.2s;
}

.toggle-thumb {
  width: 18px;
  height: 18px;
  background: var(--text-muted);
  border-radius: 50%;
  position: absolute;
  top: 3px;
  left: 3px;
  transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1), background 0.22s;
  box-shadow: 0 1px 3px oklch(0% 0 0 / 0.3);
}

.toggle-row input[type="checkbox"]:checked ~ .toggle-switch {
  background: var(--gold-dim);
  border-color: var(--gold);
}

.toggle-row input[type="checkbox"]:checked ~ .toggle-switch .toggle-thumb {
  transform: translateX(20px);
  background: var(--copper);
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}

/* The toggle switch itself is the signal — no row-wide background wash. */
.toggle-row:has(input:checked) .toggle-title {
  color: var(--ink);
}

.toggle-row input[type="checkbox"]:focus-visible ~ .toggle-switch {
  outline: 2px solid var(--copper);
  outline-offset: 2px;
}

.rules-card {
  position: sticky;
  top: 24px;
}

.rules-title {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  margin-bottom: 18px;
  color: var(--text);
}

.rules-list {
  list-style: none;
  counter-reset: rule;
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-size: 14px;
  color: var(--text-dim);
  line-height: 1.5;
}

.rules-list li {
  position: relative;
  padding-left: 30px;
  counter-increment: rule;
}

.rules-list li::before {
  content: counter(rule);
  position: absolute;
  left: 0;
  top: 1px;
  width: 22px;
  height: 22px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  color: var(--text-dim);
}

.rules-list .one { color: var(--gold); }
.rules-list .four { color: var(--gold); }

.rng-badge {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: 0.01em;
}

.rng-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--emerald);
  box-shadow: 0 0 0 2px oklch(0% 0 0 / 0.4);
  animation: pulse 2.4s ease-in-out infinite;
  flex-shrink: 0;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* ============ GAME SCREEN ============ */

.game-wrapper {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 20px;
  /* Reserve bottom space for the fixed reactions-bar so Top/Bottom
     (and other action buttons at the bottom of the felt) aren't hidden
     under the bar on mobile. ~80px = 44px bar + 20px padding + margin. */
  padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px));
  gap: 18px;
  max-width: 1280px;
  width: 100%;
  margin: 0 auto;
}

.game-header {
  display: flex;
  justify-content: space-between;
  align-items: stretch;
  gap: 16px;
  flex-wrap: wrap;
}

.header-left {
  display: flex;
  gap: 12px;
  flex-shrink: 0;
}

/* Round + pot tags — paper labels pinned to the top of the table.
   Pot is the accent, Round is neutral. No glow on the pot. */
.round-pill, .pot-pill {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 8px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 3px;
  min-width: 88px;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4), 0 3px 6px -2px oklch(0% 0 0 / 0.3);
}

.pot-pill {
  background: var(--copper-wash);
  border-color: var(--copper);
}

.pill-label {
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 400;
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  line-height: 1;
  margin-bottom: 4px;
}

.pot-pill .pill-label { color: var(--copper-hi); }

.pill-value {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 400;
  color: var(--ink);
  line-height: 1;
}

.pot-pill .pill-value { color: var(--copper-hi); }

.header-right {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
}

.players-strip {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 4px;
  justify-content: flex-end;
  width: 100%;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}

.players-strip::-webkit-scrollbar { height: 4px; }
.players-strip::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }

/* Player chip — paper tag sitting on the table. Solid color, short
   offset shadow, active state gets a copper-ink outline (no glow). */
.player-chip {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  min-width: 96px;
  transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
  position: relative;
  flex-shrink: 0;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4), 0 3px 6px -2px oklch(0% 0 0 / 0.3);
}

.player-chip.active {
  background: var(--copper-wash);
  border-color: var(--copper);
  box-shadow:
    inset 0 0 0 1px var(--copper),
    0 2px 0 oklch(0% 0 0 / 0.45),
    0 5px 10px -3px oklch(0% 0 0 / 0.4);
}

.player-chip.done {
  opacity: 0.65;
}

.chip-name {
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 400;
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.3;
}

.player-chip.active .chip-name { color: var(--copper-hi); }

.chip-balance {
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 400;
  line-height: 1.2;
  color: var(--ink);
}

/* Score badge — a paper label stuck to the bottom of the chip, forest-ink. */
.chip-score {
  display: block;
  margin-top: var(--space-2xs);
  padding: 4px 8px;
  border-radius: 2px;
  background: var(--forest-wash);
  color: var(--forest-hi);
  border: 1px solid var(--forest);
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 400;
  line-height: 1;
  text-align: center;
  letter-spacing: 0.04em;
  animation: chip-score-pop 0.55s var(--ease-out-expo);
}

.chip-score.dq {
  background: var(--oxblood-wash);
  color: oklch(82% 0.08 25);
  border-color: var(--oxblood);
  font-size: var(--text-xs);
}

@keyframes chip-score-pop {
  0%   { transform: scale(0.3) translateY(-6px); opacity: 0; }
  55%  { transform: scale(1.15) translateY(0); opacity: 1; }
  100% { transform: scale(1) translateY(0); opacity: 1; }
}

/* ============ PRESENCE DOTS ============ */

/* Small status dot in the top-right of a player chip. Four states:
   online (green), idle (amber), reconnecting (red, pulsing), gone (gray). */
.presence-dot {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--surface);
  box-shadow: 0 0 0 1px oklch(0% 0 0 / 0.35);
  z-index: 2;
  pointer-events: none;
}

.presence-dot--online {
  background: var(--forest-hi);
  box-shadow: 0 0 0 1px oklch(0% 0 0 / 0.4);
}

.presence-dot--idle {
  background: var(--amber);
  box-shadow: 0 0 0 1px oklch(0% 0 0 / 0.4);
}

.presence-dot--reconnecting {
  background: var(--oxblood-hi);
  animation: presence-pulse 1s ease-in-out infinite;
}

.presence-dot--gone {
  background: oklch(45% 0.01 70);
  opacity: 0.6;
}

/* Reconnecting pulse — opacity blink on a solid ring, no expanding halo. */
@keyframes presence-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

/* When the chip is active (copper-tinted), match dot border to that bg */
.player-chip.active .presence-dot {
  border-color: oklch(30% 0.08 55);
}

/* Inline dot in the waiting room player rows */
.waiting-player {
  position: relative;
}
.waiting-player .presence-dot {
  position: static;
  display: inline-block;
  margin-right: 8px;
  vertical-align: middle;
  border-color: var(--surface);
}

/* ============ TABLE ============ */

.table {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Felt table — solid muted saloon green with fabric noise. No radial
   spotlight, no inset glow. A surface, not a light source. */
.table-felt {
  width: 100%;
  background-color: var(--felt-color);
  /* Two-layer noise: finer grain + coarser threads for fabric feel. */
  background-image:
    radial-gradient(oklch(0% 0 0 / 0.22) 0.8px, transparent 0.8px),
    radial-gradient(oklch(100% 0 0 / 0.03) 0.8px, transparent 0.8px);
  background-size: 4px 4px, 11px 11px;
  background-position: 0 0, 2px 3px;
  border-radius: var(--radius-lg);
  box-shadow: var(--felt-shadow);
  padding: var(--space-2xl) var(--space-xl) 52px;
  position: relative;
  overflow: hidden;
  /* Dark edge — the felt sits in a wooden frame. */
  border-top: 1px solid oklch(45% 0.04 150 / 0.5);
  border-bottom: 3px solid oklch(12% 0.03 55);
  border-inline: 2px solid oklch(20% 0.03 55);
}

/* Hairline top stitch — suggests a sewn felt edge, not a glow. */
.table-felt::before {
  content: '';
  position: absolute;
  left: var(--space-md);
  right: var(--space-md);
  top: var(--space-sm);
  height: 1px;
  background: oklch(100% 0 0 / 0.04);
  pointer-events: none;
}

.turn-panel {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 24px;
  align-items: center;
  /* Hard-lock the panel height so the felt never resizes between
     phases (pre-roll / rolling / selecting / tops-bottoms / tb-revealed
     / done / cooked) or role changes (spectator / active / winner).
     Content that doesn't fill the box just gets extra breathing room
     at the bottom. 620px covers the tallest layout (SELECTING with
     6 dice + TB choices + hint). */
  height: 620px;
  justify-content: flex-start;
}

@media (max-width: 600px) {
  .turn-panel {
    /* Tighter on mobile where there's less vertical room to give. */
    height: 560px;
  }
}

.current-player {
  text-align: center;
}

.current-label {
  font-size: 10px;
  font-weight: 700;
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  margin-bottom: 6px;
}

.current-name {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 6px;
  color: var(--ink);
  transition: font-size 0.2s var(--ease-out-quart);
}

.current-balance {
  font-size: 13px;
  color: var(--ink-muted);
  font-weight: 500;
}

.current-balance span {
  color: var(--ink);
  font-weight: 700;
  font-family: var(--font-display);
}

/* Composition stays constant across phases + roles. The previous
   spectator-specific tightening (smaller name, hidden balance, hidden
   SCORE placeholder) made the felt snap-resize every time the state
   transitioned — jarring and worse than a little redundancy. The
   SCORE chip keeps its em-dash placeholder when there's no score yet. */

/* =================================================================
   Cooked overlay — mathematically-impossible-to-win humiliation.
   Lands heavy, lingers, then fades so endTurn can advance.
   ================================================================= */
.cooked-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  background: oklch(12% 0.02 20 / 0.85);
  backdrop-filter: none;
  z-index: 1200;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.35s var(--ease-out-quart);
  padding: 20px;
}

.cooked-overlay--live {
  opacity: 1;
  animation: cooked-in 0.45s var(--ease-out-expo);
}

@keyframes cooked-in {
  0%   { transform: scale(1.08); opacity: 0; }
  60%  { transform: scale(0.985); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

.cooked-overlay__stamp {
  font-family: var(--font-display);
  font-size: clamp(56px, 12vw, 140px);
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--oxblood-hi);
  text-shadow: 4px 4px 0 oklch(0% 0 0 / 0.6);
  line-height: 1;
  transform: rotate(-3deg);
}

.cooked-overlay__emoji {
  font-size: clamp(44px, 9vw, 80px);
  filter: drop-shadow(0 4px 0 oklch(0% 0 0 / 0.5));
  animation: cooked-emoji-wobble 1.8s ease-in-out infinite;
}

@keyframes cooked-emoji-wobble {
  0%, 100% { transform: rotate(-4deg); }
  50%      { transform: rotate(4deg); }
}

.cooked-overlay__taunt {
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(16px, 2.2vw, 20px);
  color: var(--ink-dim);
  text-align: center;
  max-width: 520px;
  line-height: 1.4;
}

/* ============ QUALIFICATION STRIP ============ */

.qual-strip {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
}

/* Qualification tags — paper labels printed with ink of the relevant color.
   Filled state uses forest ink. No glow. */
.qual-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: oklch(0% 0 0 / 0.35);
  border: 1px solid oklch(55% 0.03 55 / 0.3);
  border-radius: 3px;
  transition: background 0.2s, border-color 0.2s;
}

.qual-chip.met {
  background: var(--forest-wash);
  border-color: var(--forest);
}

.qual-label {
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 400;
  color: oklch(80% 0.01 75 / 0.6);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.qual-chip.met .qual-label { color: var(--forest-hi); }

.score-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 5px 18px;
  background: oklch(0% 0 0 / 0.35);
  border: 1px solid oklch(55% 0.03 55 / 0.3);
  border-radius: 3px;
  min-width: 80px;
  transition: background 0.2s, border-color 0.2s;
}

.score-chip.qualified {
  background: var(--copper-wash);
  border-color: var(--copper);
}

.score-chip .qual-label { color: oklch(80% 0.01 75 / 0.5); }
.score-chip.qualified .qual-label { color: var(--copper-hi); }

.score-chip .score-value {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 400;
  color: var(--ink);
  line-height: 1;
  margin-top: 2px;
}

.score-chip.qualified .score-value { color: var(--copper-hi); }

/* ============ DICE AREA ============ */

.dice-area {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 18px;
  width: 100%;
  /* Room for the tb-badge which sits at top:-56px on the first
     .dice-section inside. Without this, the badge could extend above
     the dice-area's bounds and get clipped by the felt's overflow. */
  padding-top: 44px;
}

.dice-section {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.section-label {
  font-size: 10px;
  font-weight: 700;
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

.dice-row {
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
  min-height: 88px;
  align-items: center;
}

.dice-row--banked {
  min-height: 68px;
  gap: 10px;
}

/* ============ DICE ============ */

/* Dice — aged bone-ivory with warm black pressed pips. A physical object
   with real weight. Selection = lift + cast shadow, NOT a gold glow. */
.die {
  --size: 72px;
  --pip-size: 11px;
  --pip-pad: 12px;
  --pip-gap: 4px;
  width: var(--size);
  height: var(--size);
  border-radius: 12px;
  /* Warm bone ivory, ever so slightly warmer on top (key light). */
  background:
    linear-gradient(160deg,
      oklch(96% 0.015 80) 0%,
      oklch(90% 0.02 75) 60%,
      oklch(84% 0.025 70) 100%);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  padding: var(--pip-pad);
  gap: var(--pip-gap);
  /* Short defined shadow — the die sits on the felt, not floating in a void. */
  box-shadow:
    inset 0 -2px 2px oklch(60% 0.04 65 / 0.35),
    inset 0 1px 0 oklch(100% 0 0 / 0.9),
    0 3px 0 oklch(0% 0 0 / 0.45),
    0 8px 14px -4px oklch(0% 0 0 / 0.5);
  transition: transform 0.15s var(--ease-out-quart), box-shadow 0.15s;
  position: relative;
  flex-shrink: 0;
}

.die .pip {
  opacity: 0;
  background: radial-gradient(circle, oklch(22% 0.02 55) 0%, oklch(12% 0.015 55) 70%);
  border-radius: 50%;
  width: var(--pip-size);
  height: var(--pip-size);
  align-self: center;
  justify-self: center;
  box-shadow:
    inset 0 1px 2px oklch(0% 0 0 / 0.55),
    inset 0 -1px 1px oklch(100% 0 0 / 0.12);
}

/* Pip positions: grid is 1 2 3 / 4 5 6 / 7 8 9 */
.die[data-value="1"] .pip:nth-child(5),
.die[data-value="2"] .pip:nth-child(1),
.die[data-value="2"] .pip:nth-child(9),
.die[data-value="3"] .pip:nth-child(1),
.die[data-value="3"] .pip:nth-child(5),
.die[data-value="3"] .pip:nth-child(9),
.die[data-value="4"] .pip:nth-child(1),
.die[data-value="4"] .pip:nth-child(3),
.die[data-value="4"] .pip:nth-child(7),
.die[data-value="4"] .pip:nth-child(9),
.die[data-value="5"] .pip:nth-child(1),
.die[data-value="5"] .pip:nth-child(3),
.die[data-value="5"] .pip:nth-child(5),
.die[data-value="5"] .pip:nth-child(7),
.die[data-value="5"] .pip:nth-child(9),
.die[data-value="6"] .pip:nth-child(1),
.die[data-value="6"] .pip:nth-child(3),
.die[data-value="6"] .pip:nth-child(4),
.die[data-value="6"] .pip:nth-child(6),
.die[data-value="6"] .pip:nth-child(7),
.die[data-value="6"] .pip:nth-child(9) {
  opacity: 1;
}

.die--active {
  cursor: pointer;
}

@media (hover: hover) {
  .die--active:hover:not(.rolling) {
    transform: translateY(-4px);
    box-shadow:
      inset 0 -2px 2px oklch(60% 0.04 65 / 0.35),
      inset 0 1px 0 oklch(100% 0 0 / 0.9),
      0 5px 0 oklch(0% 0 0 / 0.5),
      0 14px 22px -6px oklch(0% 0 0 / 0.55);
  }
}

/* Selected die — lifted off the felt, longer cast shadow below.
   Physical, not radioactive. A thin copper outline reads as "picked up". */
.die--selected {
  cursor: pointer;
  transform: translateY(-10px);
  /* Warmer ivory — as if held in a hand under lamplight. */
  background:
    linear-gradient(160deg,
      oklch(97% 0.025 85) 0%,
      oklch(92% 0.035 75) 60%,
      oklch(86% 0.04 65) 100%);
  box-shadow:
    inset 0 -2px 2px oklch(50% 0.06 65 / 0.4),
    inset 0 1px 0 oklch(100% 0 0 / 0.95),
    0 0 0 2px var(--copper),
    0 8px 0 oklch(0% 0 0 / 0.45),
    0 18px 24px -6px oklch(0% 0 0 / 0.55);
}

.die--selected .pip {
  background: radial-gradient(circle, oklch(20% 0.05 45) 0%, oklch(10% 0.03 45) 70%);
}

/* Banked die — smaller, sits flat on the felt with a short grounded shadow. */
.die--banked {
  --size: 52px;
  --pip-size: 8px;
  --pip-pad: 9px;
  --pip-gap: 3px;
  border-radius: 8px;
  box-shadow:
    inset 0 -1px 2px oklch(60% 0.04 65 / 0.3),
    inset 0 1px 0 oklch(100% 0 0 / 0.85),
    0 2px 0 oklch(0% 0 0 / 0.5),
    0 5px 9px -2px oklch(0% 0 0 / 0.45);
}

.die--placeholder {
  background: transparent;
  border: 2px dashed oklch(55% 0.02 55 / 0.35);
  box-shadow: none;
}

.die--placeholder .pip { display: none; }

/* Mystery die — a blackened die face turned away. No glow ring;
   a short copper hairline edge and a slow rocking motion suggest
   "cast, but not shown." */
.die--mystery {
  background: oklch(22% 0.018 55);
  border: 1px solid var(--copper-lo);
  box-shadow:
    inset 0 -2px 2px oklch(0% 0 0 / 0.5),
    inset 0 1px 0 oklch(100% 0 0 / 0.08),
    0 3px 0 oklch(0% 0 0 / 0.5),
    0 10px 18px -6px oklch(0% 0 0 / 0.55);
  position: relative;
  animation: mystery-rock 1.6s ease-in-out infinite;
  padding: 0;
}

.die--mystery .pip { display: none; }

.die--mystery::before {
  content: '?';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: calc(var(--size) * 0.55);
  font-weight: 400;
  color: var(--copper-hi);
  text-shadow: 1px 1px 0 oklch(0% 0 0 / 0.7);
}

/* Slow rocking motion — the die cup shaking, not a breathing glow. */
@keyframes mystery-rock {
  0%, 100% { transform: translateY(0) rotate(-1deg); }
  50%      { transform: translateY(-2px) rotate(1deg); }
}

/* Revealed chosen die — thin copper outline, a little taller than normal.
   The "hero" of the reveal, without being radioactive. */
.die--revealed {
  box-shadow:
    inset 0 -2px 2px oklch(50% 0.06 65 / 0.4),
    inset 0 1px 0 oklch(100% 0 0 / 0.95),
    0 0 0 2px var(--copper),
    0 6px 0 oklch(0% 0 0 / 0.5),
    0 14px 22px -6px oklch(0% 0 0 / 0.55);
  animation: die-reveal 1s var(--ease-out-expo);
  transform-origin: center;
}

/* Bust reveal — same shape as revealed but an oxblood outline, no glow. */
.die--busted {
  background:
    linear-gradient(160deg,
      oklch(93% 0.03 25) 0%,
      oklch(85% 0.05 25) 60%,
      oklch(75% 0.07 25) 100%);
  box-shadow:
    inset 0 -2px 2px oklch(35% 0.14 25 / 0.4),
    inset 0 1px 0 oklch(100% 0 0 / 0.85),
    0 0 0 2px var(--oxblood),
    0 6px 0 oklch(0% 0 0 / 0.5),
    0 14px 22px -6px oklch(0% 0 0 / 0.55);
  animation: die-reveal 1s var(--ease-out-expo);
  transform-origin: center;
}

.die--busted .pip {
  background: radial-gradient(circle, oklch(30% 0.12 25) 0%, oklch(15% 0.06 25) 70%);
}

/* Ghost die — the path not taken. Desaturated, sitting flat, short shadow. */
.die--ghost {
  opacity: 0.38;
  filter: grayscale(0.75);
  box-shadow:
    inset 0 -1px 2px oklch(60% 0.04 65 / 0.3),
    inset 0 1px 0 oklch(100% 0 0 / 0.75),
    0 2px 0 oklch(0% 0 0 / 0.5),
    0 6px 10px -2px oklch(0% 0 0 / 0.4);
  animation: ghost-fade-in 0.6s 1.1s both ease-out;
}

@keyframes ghost-fade-in {
  from { opacity: 0; transform: scale(0.92); }
  to   { opacity: 0.38; transform: scale(1); }
}

@keyframes die-reveal {
  0%   { transform: scale(0.35) rotate(-12deg); opacity: 0; }
  55%  { transform: scale(1.12) rotate(3deg);  opacity: 1; }
  80%  { transform: scale(0.98) rotate(-1deg); opacity: 1; }
  100% { transform: scale(1)    rotate(0deg);  opacity: 1;   }
}

/* Dimmed not-chosen button shown next to the revealed choice */
.tb-choices .btn.not-chosen {
  opacity: 0.35;
  filter: grayscale(0.6);
  box-shadow: 0 4px 12px -4px oklch(0% 0 0 / 0.4);
}

.die.rolling {
  cursor: default;
  animation: shake 0.1s linear infinite;
}

.die.settling {
  animation: settle 0.4s ease-out;
}

.die.newly-banked {
  animation: bankFlash 0.6s ease-out;
}

.die-slot {
  --size: 52px;
  width: var(--size);
  height: var(--size);
  border-radius: 10px;
  border: 2px dashed oklch(100% 0 0 / 0.08);
  flex-shrink: 0;
}

/* Mini die for qualification chips */
.mini-die {
  --size: 22px;
  --pip-size: 3px;
  width: var(--size);
  height: var(--size);
  background: oklch(92% 0.018 80);
  border-radius: 4px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  padding: 3px;
  gap: 1px;
  flex-shrink: 0;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}

.mini-die .pip {
  opacity: 0;
  background: oklch(18% 0.02 55);
  border-radius: 50%;
  width: var(--pip-size);
  height: var(--pip-size);
  align-self: center;
  justify-self: center;
}

.mini-die[data-value="1"] .pip:nth-child(5),
.mini-die[data-value="4"] .pip:nth-child(1),
.mini-die[data-value="4"] .pip:nth-child(3),
.mini-die[data-value="4"] .pip:nth-child(7),
.mini-die[data-value="4"] .pip:nth-child(9) {
  opacity: 1;
}

/* ============ ACTIONS ============ */

.actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding-top: 6px;
  width: 100%;
  max-width: 320px;
  margin: 0 auto;
}

.actions > .btn {
  min-width: 240px;
  width: 100%;
}

.tb-choices {
  display: flex;
  gap: 10px;
  width: 100%;
  justify-content: center;
}

.tb-choices .btn {
  flex: 1 1 0;
  min-width: 0;
  padding-left: 14px;
  padding-right: 14px;
}

/* Top/Bottom buttons — two stamped keys. Top = copper, Bottom = forest.
   No gradients, no glow — flat ink on paper. */
.tb-choices .btn:first-child {
  background: var(--copper);
  color: var(--copper-ink);
}

.tb-choices .btn:last-child {
  background: var(--forest);
  color: var(--forest-ink);
  box-shadow:
    0 3px 0 0 oklch(30% 0.08 155),
    0 5px 10px -3px oklch(0% 0 0 / 0.5),
    inset 0 1px 0 oklch(100% 0 0 / 0.3);
}

@media (hover: hover) {
  .tb-choices .btn:last-child:hover:not(:disabled) {
    transform: translateY(-2px);
    background: var(--forest-hi);
    box-shadow:
      0 5px 0 0 oklch(30% 0.08 155),
      0 8px 14px -4px oklch(0% 0 0 / 0.55),
      inset 0 1px 0 oklch(100% 0 0 / 0.35);
  }
}

.tb-choices .btn:last-child:active:not(:disabled) {
  transform: translateY(2px);
  box-shadow:
    0 1px 0 0 oklch(30% 0.08 155),
    0 2px 6px -2px oklch(0% 0 0 / 0.5),
    inset 0 1px 0 oklch(100% 0 0 / 0.25);
}

.hint {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--ink-muted);
  text-align: center;
  min-height: 20px;
  font-style: italic;
}

/* ============ OVERLAY / MODAL ============ */

.overlay {
  position: fixed;
  inset: 0;
  background: oklch(12% 0.015 260 / 0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 100;
  animation: fadeIn 0.25s ease-out;
}

.overlay-card {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 20px;
  padding: 44px 48px;
  text-align: center;
  max-width: 420px;
  width: 100%;
  animation: slideUp 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.1);
  box-shadow: 0 30px 60px oklch(0% 0 0 / 0.6), 0 0 0 1px oklch(100% 0 0 / 0.05);
}

.overlay-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.overlay-name {
  font-family: var(--font-display);
  font-size: clamp(36px, 7vw, 52px);
  font-weight: 400;
  letter-spacing: 0;
  margin-bottom: 8px;
  color: var(--copper-hi);
  text-shadow: 2px 2px 0 var(--copper-lo), 4px 4px 0 oklch(0% 0 0 / 0.5);
  line-height: 1;
}

.overlay-hint {
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 28px;
}

/* =================================================================
   Results screen — sits on the same felt as the game table so the
   round transition feels continuous, not modal.
   ================================================================= */

[data-screen="results"] {
  background-color: var(--felt-color);
  background-image:
    radial-gradient(oklch(0% 0 0 / 0.22) 0.8px, transparent 0.8px),
    radial-gradient(oklch(100% 0 0 / 0.03) 0.8px, transparent 0.8px);
  background-size: 3px 3px, 5px 5px;
  background-position: 0 0, 1px 2px;
}

.results-wrapper {
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  padding: clamp(48px, 8vw, 96px) clamp(20px, 4vw, 36px) 28px;
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.results-header {
  text-align: left;
  max-width: 620px;
}

.results-eyebrow {
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 12px;
}

.results-eyebrow-sep {
  margin: 0 4px;
  opacity: 0.6;
}

/* Tie result — "Split pot" label replaces "Round N". Uses CSS content
   injection so we don't have to rebuild the eyebrow span-tree. */
.results-wrapper--tie .results-eyebrow {
  color: var(--copper-hi);
  visibility: hidden; /* hide originals (Round N · Pot X) */
}
.results-wrapper--tie .results-eyebrow::before {
  content: attr(data-tie-label);
  visibility: visible;
  display: block;
}
.results-wrapper--tie .results-title::before {
  content: "\uD83E\uDD1D ";
  margin-right: 2px;
}

/* Sentence headline, Literata italic. Left-aligned, NOT caps, no copper
   text shadow. Confident quiet over arcade loud. */
.results-title {
  font-family: var(--font-body);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(28px, 4.5vw, 44px);
  line-height: 1.15;
  letter-spacing: -0.005em;
  color: var(--ink);
  margin: 0;
}

/* Row list — hairlined, no cards. Winner gets a copper left stripe. */
.results-list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid oklch(100% 0 0 / 0.08);
  border-bottom: 1px solid oklch(100% 0 0 / 0.08);
  background: oklch(24% 0.02 150 / 0.4);
  border-radius: 3px;
  overflow: hidden;
}

.result-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 18px;
  padding: 16px 16px 16px 14px;
  border-left: 3px solid transparent;
  border-bottom: 1px solid oklch(100% 0 0 / 0.05);
  transition: background 0.2s;
}
.result-row:last-child { border-bottom: none; }

.result-row.winner {
  border-left-color: var(--copper);
  background: oklch(30% 0.04 55 / 0.5);
}

.result-rank {
  font-family: var(--font-display);
  font-size: 16px;
  letter-spacing: 0.02em;
  color: var(--ink-muted);
  text-align: right;
}
.result-row.winner .result-rank { color: var(--copper-hi); }

/* Name + streak share a single column so the streak wraps cleanly
   under the name instead of being crammed into the rank cell. */
.result-name-col {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.result-name {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--ink);
  line-height: 1.15;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.result-row.winner .result-name { color: var(--copper-hi); }

.result-streak {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-dim);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.result-dice {
  display: flex;
  gap: 3px;
  flex-shrink: 0;
}
.result-dice .die {
  --size: 26px;
  --pip-size: 3.5px;
  --pip-pad: 4px;
  --pip-gap: 1px;
  border-radius: 4px;
}

.result-score {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 400;
  min-width: 48px;
  text-align: right;
  color: var(--ink);
  letter-spacing: 0.02em;
}
.result-row.winner .result-score { color: var(--copper); }
.result-score.dq {
  color: var(--oxblood-hi);
  font-size: 14px;
  letter-spacing: 0.1em;
}

.results-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: stretch;
  margin-top: 8px;
}

.results-actions .btn {
  min-width: 0;
  width: 100%;
}

/* ============ ANIMATIONS ============ */

@keyframes shake {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
  20% { transform: translate3d(-2px, -3px, 0) rotate(-5deg); }
  40% { transform: translate3d(3px, 1px, 0) rotate(4deg); }
  60% { transform: translate3d(-2px, 3px, 0) rotate(-3deg); }
  80% { transform: translate3d(2px, -1px, 0) rotate(4deg); }
}

@keyframes settle {
  0% { transform: translateY(-6px) scale(1.05); }
  50% { transform: translateY(0) scale(0.97); }
  100% { transform: translateY(0) scale(1); }
}

@keyframes bankFlash {
  0% {
    transform: translateY(-56px) scale(1.25);
    filter: drop-shadow(0 12px 16px oklch(0% 0 0 / 0.5));
  }
  60% {
    transform: translateY(0) scale(1.08);
    filter: drop-shadow(0 4px 8px oklch(0% 0 0 / 0.4));
  }
  100% {
    transform: translateY(0) scale(1);
    filter: none;
  }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes slideUp {
  from { opacity: 0; transform: translateY(24px) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* ============ TOPS-OR-BOTTOMS REVEAL EFFECTS ============ */

/* Anchor for the absolute-positioned badge */
.dice-section {
  position: relative;
}

/* Paper sticker — slapped onto the felt. Solid ink color on solid paper,
   short offset shadow from the lamp above. No gradient fill, no glow. */
.tb-badge {
  position: absolute;
  /* Sits well clear of the dice — the pop animation scales up to 1.2x,
     and at top:-26px the badge's expanded bottom edge was landing on
     top of the revealed/ghost dice during TB reveal. -56px keeps it
     above the dice row even at the peak of the scale animation. */
  top: -56px;
  left: 50%;
  transform: translateX(-50%) rotate(-4deg) scale(1);
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 11px 22px;
  border-radius: 4px;            /* square-ish, like a gummed label */
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  z-index: 20;
  pointer-events: none;
  border: 1px solid oklch(0% 0 0 / 0.3);
  box-shadow:
    0 3px 0 oklch(0% 0 0 / 0.5),
    0 10px 18px -6px oklch(0% 0 0 / 0.55);
  animation: tb-badge-pop 0.55s var(--ease-out-expo);
}

.tb-badge.hidden {
  display: none;
}

.tb-badge__emoji {
  font-size: var(--text-xl);
  filter: drop-shadow(0 1px 0 oklch(0% 0 0 / 0.4));
}

.tb-badge__text {
  line-height: 1;
}

/* Per-outcome paper stickers — solid ink on solid paper, no gradients. */
.tb-badge--perfect {
  background: var(--copper);
  color: var(--copper-ink);
}

.tb-badge--saved {
  background: var(--forest);
  color: var(--forest-ink);
}

.tb-badge--bust {
  background: var(--oxblood);
  color: var(--oxblood-ink);
}

/* DQ — non-TB disqualification (banked 6 without a 1 or 4). Less
   dramatic than BUST (which is an active bad call), but still clearly
   a loss. Muted oxblood with a thin inset stroke to differentiate. */
.tb-badge--dq {
  background: oklch(36% 0.08 22);
  color: var(--oxblood-ink);
  box-shadow: inset 0 0 0 1px var(--oxblood);
}

.tb-badge--cooked {
  background: oklch(36% 0.08 22);
  color: var(--oxblood-ink);
  box-shadow: inset 0 0 0 1px var(--oxblood);
}

.tb-badge--lucky {
  background: var(--copper-hi);
  color: var(--copper-ink);
}

.tb-badge--ouch {
  background: oklch(70% 0.015 75);
  color: oklch(18% 0.02 55);
}

.tb-badge--score {
  background: var(--ink);
  color: oklch(22% 0.04 55);
  padding: 8px 26px;
}

.tb-badge--score .tb-badge__text {
  font-size: var(--text-2xl);
  letter-spacing: 0.02em;
}

@keyframes tb-badge-pop {
  0%   { transform: translateX(-50%) rotate(8deg)  scale(0);    opacity: 0; }
  55%  { transform: translateX(-50%) rotate(-7deg) scale(1.2);  opacity: 1; }
  80%  { transform: translateX(-50%) rotate(-3deg) scale(0.94); opacity: 1; }
  100% { transform: translateX(-50%) rotate(-5deg) scale(1);    opacity: 1; }
}

/* Flash overlay — scoped to the felt table area */
.tb-flash {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  z-index: 6;
  border-radius: var(--radius-lg);
}

.tb-flash.hidden {
  display: none;
}

.tb-flash.flashing {
  animation: tb-flash 0.55s ease-out;
}

/* Flash — a brief ink-wash over the entire felt, solid color with falloff
   toward the edges. No neon-radial spotlight. */
.tb-flash--gold {
  background: var(--copper);
  mix-blend-mode: overlay;
  opacity: 0.7;
}

.tb-flash--green {
  background: var(--forest);
  mix-blend-mode: overlay;
  opacity: 0.7;
}

.tb-flash--red {
  background: var(--oxblood);
  mix-blend-mode: overlay;
  opacity: 0.8;
}

@keyframes tb-flash {
  0%   { opacity: 0; }
  20%  { opacity: 1; }
  100% { opacity: 0; }
}

/* Table shake — applied on top of the felt's static transform */
.table-felt.shake-medium {
  animation: table-shake-medium 0.5s ease-in-out;
}

.table-felt.shake-hard {
  animation: table-shake-hard 0.55s ease-in-out;
}

@keyframes table-shake-medium {
  0%, 100% { transform: translate(0, 0); }
  15%      { transform: translate(-4px, -2px); }
  30%      { transform: translate(5px, 2px); }
  45%      { transform: translate(-3px, 2px); }
  60%      { transform: translate(4px, -2px); }
  75%      { transform: translate(-2px, 1px); }
}

@keyframes table-shake-hard {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  12%      { transform: translate(-9px, -4px) rotate(-0.5deg); }
  24%      { transform: translate(9px, 4px)  rotate(0.5deg); }
  36%      { transform: translate(-7px, 3px) rotate(-0.35deg); }
  48%      { transform: translate(7px, -3px) rotate(0.35deg); }
  60%      { transform: translate(-5px, 2px) rotate(-0.2deg); }
  72%      { transform: translate(5px, -2px) rotate(0.2deg); }
  85%      { transform: translate(-3px, 1px); }
}

/* ============ LOBBY SCREEN ============ */

.lobby-grid {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 24px;
  align-items: start;
}

.lobby-create,
.lobby-games {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px;
  /* Consistent vertical rhythm between direct children — no per-child margins. */
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.card-title {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
  margin: 0;
}

.lobby-input {
  width: 100%;
  padding: 14px 18px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-weight: 500;
  font-size: 15px;
  outline: none;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}

.lobby-input::placeholder {
  color: var(--text-muted);
}

.lobby-input:focus {
  background: var(--surface-2);
  border-color: var(--gold);
  box-shadow: 0 0 0 3px var(--gold-dim);
}

/* --- Name input row with shuffle button --- */
.name-input-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}

.name-input-row .lobby-input,
.name-input-row input {
  flex: 1;
  min-width: 0;
}

.name-shuffle-btn {
  width: 52px;
  min-height: 52px;
  padding: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  transition: transform 0.1s ease, background 0.15s ease, border-color 0.15s ease;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.name-shuffle-btn:hover {
  background: var(--surface-2);
  border-color: var(--emerald);
  transform: translateY(-1px);
}

.name-shuffle-btn:active {
  transform: translateY(0) rotate(90deg);
}

.name-shuffle-btn:focus-visible {
  outline: none;
  border-color: var(--emerald);
  box-shadow: 0 0 0 3px var(--emerald-dim);
}

@keyframes shuffle-spin {
  0%   { transform: rotate(0) scale(1); }
  50%  { transform: rotate(180deg) scale(1.2); }
  100% { transform: rotate(360deg) scale(1); }
}

.name-shuffle-btn.rolling {
  animation: shuffle-spin 0.4s ease-out;
}

.game-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 420px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}

.game-list::-webkit-scrollbar { width: 5px; }
.game-list::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }

.game-list-empty {
  text-align: center;
  padding: var(--space-2xl) var(--space-lg);
  color: var(--ink-muted);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-style: italic;
}

/* Section label — "OPEN GAMES" / "IN PROGRESS" header above each group */
.game-list-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.game-list-group + .game-list-group {
  margin-top: var(--space-md);
}

.game-list-group-label {
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 400;
  letter-spacing: 0.18em;
  color: var(--copper);
  text-transform: uppercase;
  padding: 0 2px;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

/* Each listing reads as a betting slip pinned to the bulletin board:
   a paper panel with a torn-top look via a dashed top edge, creator name
   in big display type, details in body serif, ticket stub on the right. */
.game-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-md);
  padding: 14px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 3px;
  box-shadow:
    0 1px 0 oklch(0% 0 0 / 0.4),
    0 3px 6px -2px oklch(0% 0 0 / 0.3);
  transition: background 0.15s, border-color 0.15s, transform 0.12s var(--ease-out-quart);
  position: relative;
}

/* Open games get a thin forest-ink left edge — not a thick stripe */
.game-list-group:first-child .game-row {
  border-left: 1px solid var(--forest);
}

@media (hover: hover) {
  .game-row:hover {
    background: var(--surface-2);
    border-color: var(--border-strong);
    transform: translateY(-1px);
  }
  .game-list-group:first-child .game-row:hover {
    border-color: var(--forest);
    border-left-color: var(--forest);
    background: var(--forest-wash);
  }
}

.game-row-info {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.game-row-creator {
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 400;
  color: var(--ink);
  letter-spacing: 0.03em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.game-row-details {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  color: var(--ink-muted);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

.game-row-players,
.game-row-ante {
  display: inline-flex;
  align-items: center;
}

/* Ante gets a tiny ticket-stub feel: a copper bullet before the amount */
.game-row-ante::before {
  content: '·';
  color: var(--ink-faint);
  margin-right: 10px;
}

/* House-rule badges — small stamped labels, one-line, copper ink */
.game-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border-radius: 2px;
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--copper-wash);
  border: 1px solid var(--copper-lo);
  color: var(--copper-hi);
}

.game-row-action {
  flex-shrink: 0;
  display: flex;
  align-items: center;
}

/* "Full" tag — oxblood ink, small */
.game-full {
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--oxblood-hi);
  padding: 6px 10px;
  border: 1px dashed var(--oxblood);
  border-radius: 2px;
}

/* "Playing" tag — muted ink */
.game-status-label {
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding: 6px 10px;
  border: 1px dashed var(--border-strong);
  border-radius: 2px;
}

/* Smaller join button for inline use on game rows */
.game-row .btn-small,
.btn-small {
  padding: 8px 14px;
  font-size: var(--text-xs);
  min-height: 36px;
  letter-spacing: 0.1em;
}

.join-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 9px 18px;
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--forest);
  color: var(--forest-ink);
  box-shadow:
    0 3px 0 0 oklch(30% 0.08 155),
    0 5px 10px -3px oklch(0% 0 0 / 0.5),
    inset 0 1px 0 oklch(100% 0 0 / 0.3);
  transition: transform 0.12s var(--ease-out-quart), box-shadow 0.12s, background 0.15s;
  flex-shrink: 0;
  cursor: pointer;
  border: none;
}

@media (hover: hover) {
  .join-btn:hover {
    transform: translateY(-2px);
    background: var(--forest-hi);
    box-shadow:
      0 5px 0 0 oklch(30% 0.08 155),
      0 8px 14px -4px oklch(0% 0 0 / 0.55),
      inset 0 1px 0 oklch(100% 0 0 / 0.35);
  }
}

.join-btn:active {
  transform: translateY(1px);
  box-shadow:
    0 1px 0 0 oklch(30% 0.08 155),
    0 2px 6px -2px oklch(0% 0 0 / 0.5),
    inset 0 1px 0 oklch(100% 0 0 / 0.25);
}

.lobby-status {
  text-align: center;
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 18px;
  font-weight: 500;
  letter-spacing: 0.01em;
}

/* ============ WAITING ROOM ============ */

/* =================================================================
   Waiting Room — bar-dice table preview
   ================================================================= */

.waiting-wrapper {
  max-width: 640px;
  width: 100%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.waiting-header {
  text-align: left;
}

.waiting-title {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 400;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 4px 0;
}

.waiting-count {
  font-family: var(--font-display);
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--copper-hi);
}

.waiting-tagline {
  margin: 6px 0 0 0;
  font-size: 14px;
  color: var(--ink-muted);
  font-style: italic;
}

/* --- Seat table: felt background + ring of seats --- */
.seat-table {
  position: relative;
  padding: 18px;
  border-radius: var(--radius);
  background: var(--felt-color);
  background-image:
    radial-gradient(oklch(0% 0 0 / 0.22) 0.8px, transparent 0.8px),
    radial-gradient(oklch(100% 0 0 / 0.03) 0.8px, transparent 0.8px);
  background-size: 3px 3px, 5px 5px;
  background-position: 0 0, 1px 2px;
  border: 1px solid var(--felt-edge);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.04),
    0 2px 0 oklch(0% 0 0 / 0.6),
    0 16px 32px -12px oklch(0% 0 0 / 0.55);
  overflow: hidden;
}

.seat-table-felt {
  position: relative;
  min-height: 72px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px 8px;
}

.seat-ring {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-bottom: 12px;
}

@media (min-width: 640px) {
  .seat-ring {
    grid-template-columns: repeat(4, 1fr);
  }
}

.seat {
  position: relative;
  padding: 14px 12px 12px;
  background: oklch(24% 0.015 55 / 0.85);
  border: 1px solid oklch(40% 0.02 55 / 0.6);
  border-radius: 4px;
  min-height: 74px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: border-color 0.15s, background 0.2s;
}

.seat--filled {
  background: oklch(28% 0.02 55 / 0.92);
  border-color: var(--copper-lo);
}

.seat--you {
  border-color: var(--copper);
  background: var(--copper-wash);
}

.seat--empty {
  background: transparent;
  border-style: dashed;
  border-color: oklch(42% 0.02 55 / 0.55);
  opacity: 0.75;
}

.seat-empty-label {
  margin: auto;
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.seat-name {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 400;
  color: var(--ink);
  line-height: 1.15;
  word-break: break-word;
}

.seat--you .seat-name { color: var(--copper-hi); }

.seat-pills {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.seat-pill {
  display: inline-flex;
  align-items: center;
  padding: 2px 6px;
  font-size: 9px;
  font-weight: 400;
  font-family: var(--font-display);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.3;
  border-radius: 2px;
  background: oklch(32% 0.015 55);
  color: var(--ink-dim);
  border: 1px solid var(--border);
}

.seat-pill--host { background: var(--copper-wash); color: var(--copper-hi); border-color: var(--copper-lo); }
.seat-pill--you  { background: var(--copper); color: var(--copper-ink); border-color: var(--copper); }

.seat-bank {
  margin-top: auto;
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--forest-hi);
  letter-spacing: 0.02em;
}

.seat .presence-dot {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 8px;
  height: 8px;
  border: 2px solid oklch(24% 0.015 55);
}

.seat--new {
  animation: seat-fill 0.5s var(--ease-out-expo);
}

@keyframes seat-fill {
  from { transform: scale(0.85); opacity: 0; }
  to   { transform: scale(1);    opacity: 1; }
}

/* --- Idle dice on the felt --- */
.idle-dice {
  display: flex;
  gap: 8px;
  justify-content: center;
}

.idle-die {
  width: 30px;
  height: 30px;
  border-radius: 5px;
  background: oklch(92% 0.018 80);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  padding: 4px;
  gap: 1px;
  flex-shrink: 0;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
  opacity: 0.9;
  transition: transform 0.3s var(--ease-out-quart);
}

.idle-die .pip {
  opacity: 0;
  background: oklch(18% 0.02 55);
  border-radius: 50%;
  width: 3px;
  height: 3px;
  align-self: center;
  justify-self: center;
}

.idle-die[data-value="1"] .pip:nth-child(5),
.idle-die[data-value="2"] .pip:nth-child(1), .idle-die[data-value="2"] .pip:nth-child(9),
.idle-die[data-value="3"] .pip:nth-child(1), .idle-die[data-value="3"] .pip:nth-child(5), .idle-die[data-value="3"] .pip:nth-child(9),
.idle-die[data-value="4"] .pip:nth-child(1), .idle-die[data-value="4"] .pip:nth-child(3),
.idle-die[data-value="4"] .pip:nth-child(7), .idle-die[data-value="4"] .pip:nth-child(9),
.idle-die[data-value="5"] .pip:nth-child(1), .idle-die[data-value="5"] .pip:nth-child(3), .idle-die[data-value="5"] .pip:nth-child(5),
.idle-die[data-value="5"] .pip:nth-child(7), .idle-die[data-value="5"] .pip:nth-child(9),
.idle-die[data-value="6"] .pip:nth-child(1), .idle-die[data-value="6"] .pip:nth-child(3), .idle-die[data-value="6"] .pip:nth-child(4),
.idle-die[data-value="6"] .pip:nth-child(6), .idle-die[data-value="6"] .pip:nth-child(7), .idle-die[data-value="6"] .pip:nth-child(9) {
  opacity: 1;
}

.idle-die.nudging {
  animation: idle-nudge 0.5s var(--ease-out-quart);
}

@keyframes idle-nudge {
  0%   { transform: translateY(0) rotate(0deg); }
  30%  { transform: translateY(-4px) rotate(-3deg); }
  60%  { transform: translateY(-1px) rotate(2deg); }
  100% { transform: translateY(0) rotate(0deg); }
}

/* --- Idle cards on the felt (gin waiting room) --- */
.idle-cards {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0;
  position: relative;
  height: 54px;
}

.idle-card {
  width: 36px;
  height: 54px;
  border-radius: 3px;
  box-shadow: var(--shadow-press);
  transition: transform 0.3s var(--ease-out-quart);
}

/* Fan the three cards — left tilted, right tilted, center flat */
.idle-card[data-idx="0"] { transform: rotate(-10deg) translateX(6px); }
.idle-card[data-idx="1"] { transform: rotate(0deg) translateX(0) translateY(-2px); z-index: 2; margin-inline: -6px; }
.idle-card[data-idx="2"] { transform: rotate(10deg) translateX(-6px); }

.idle-card.nudging {
  animation: idle-card-nudge 0.55s var(--ease-out-quart);
}

@keyframes idle-card-nudge {
  0%   { transform: translateY(0); }
  30%  { transform: translateY(-5px) rotate(-1deg); }
  60%  { transform: translateY(-1px) rotate(1deg); }
  100% { transform: translateY(0); }
}

.idle-card .gin-card__svg { border-radius: 3px; }

/* --- Settings tokens --- */
.waiting-tokens {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.waiting-token {
  display: flex;
  flex-direction: column;
  padding: 8px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 3px;
  min-width: 72px;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}

.waiting-token__label {
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  line-height: 1.2;
}

.waiting-token__value {
  font-family: var(--font-display);
  font-size: 14px;
  color: var(--ink);
  letter-spacing: 0.02em;
  margin-top: 2px;
}

.waiting-token--on .waiting-token__value  { color: var(--forest-hi); }
.waiting-token--off .waiting-token__value { color: var(--ink-faint); }

/* Inline-editable tokens — host clicks a token to edit in place. */
.waiting-token--editable {
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.12s;
}

.waiting-token--editable:hover {
  border-color: var(--copper);
}

.waiting-token--editable:hover .waiting-token__label {
  color: var(--copper-hi);
}

.waiting-token--editable:focus-visible {
  outline: 2px solid var(--copper);
  outline-offset: 2px;
}

.waiting-token--editing {
  border-color: var(--copper);
  background: var(--copper-wash);
}

.waiting-token__input {
  width: 4ch;
  padding: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 14px;
  letter-spacing: 0.02em;
  -moz-appearance: textfield;
}
.waiting-token__input::-webkit-outer-spin-button,
.waiting-token__input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.waiting-token__prefix {
  color: var(--ink);
  margin-right: 1px;
}

/* Kick button — progressive-disclosure × in the bottom-right of the seat.
   Hidden by default on hover-capable devices, revealed when the seat is
   hovered/focused so the seat card stays clean. Always visible on touch. */
.seat-kick-btn {
  position: absolute;
  bottom: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: oklch(20% 0.015 55 / 0.8);
  color: var(--ink-dim);
  border: 1px solid var(--border);
  border-radius: 50%;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  font-family: var(--font-body);
  transition: color 0.12s, background 0.12s, border-color 0.12s, opacity 0.12s;
  z-index: 2;
  opacity: 0;
}

@media (hover: hover) {
  .seat:hover .seat-kick-btn,
  .seat:focus-within .seat-kick-btn,
  .seat-kick-btn:focus-visible {
    opacity: 1;
  }
}

@media (pointer: coarse) {
  .seat-kick-btn { opacity: 1; }
}

.seat-kick-btn:hover,
.seat-kick-btn:focus-visible {
  color: var(--oxblood-ink);
  background: var(--oxblood);
  border-color: var(--oxblood);
  outline: none;
  opacity: 1;
}

/* Lobby toast — ephemeral notification for "You were removed", etc. */
.lobby-toast {
  position: fixed;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom) + 24px);
  transform: translate(-50%, 24px);
  padding: 12px 18px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  box-shadow: 0 2px 0 oklch(0% 0 0 / 0.45), 0 8px 18px -4px oklch(0% 0 0 / 0.5);
  font-size: 14px;
  z-index: 1000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s var(--ease-out-quart);
  max-width: 92vw;
}

.lobby-toast--visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* --- Inline edit panel (host-only, while waiting) --- */
.waiting-edit {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 18px 20px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
}

.waiting-edit-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  align-items: center;
}

.waiting-edit-row label {
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.waiting-edit-row input[type="number"] {
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 14px;
  outline: none;
}

.waiting-edit-row input[type="number"]:focus {
  border-color: var(--copper);
  box-shadow: inset 0 0 0 1px var(--copper);
}

.waiting-edit-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* --- Share section --- */
.waiting-share {
  padding: 14px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.share-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.share-label {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  flex-shrink: 0;
}

.share-link {
  flex: 1;
  min-width: 160px;
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--copper-hi);
  text-decoration: none;
  letter-spacing: 0.01em;
  word-break: break-all;
  user-select: all;
}

.share-link:hover { color: var(--copper); text-decoration: underline; }

.share-action-btn {
  position: relative;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  color: var(--ink-dim);
  border: 1px solid var(--border);
  border-radius: 3px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
  flex-shrink: 0;
}

.share-action-btn:hover { color: var(--copper-hi); border-color: var(--copper); }

.share-action-btn[aria-expanded="true"] {
  color: var(--copper-ink);
  background: var(--copper);
  border-color: var(--copper);
}

.share-action-btn__icon,
.share-action-btn__check {
  display: block;
  width: 16px;
  height: 16px;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

.share-action-btn__icon[data-icon="copy"] {
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='currentColor' d='M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='currentColor' d='M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z'/></svg>");
}

.share-action-btn__icon[data-icon="qr"] {
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='currentColor' d='M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zm8-12v8h8V3h-8zm6 6h-4V5h4v4zm0 10h2v2h-2zm-6-6h2v2h-2zm2 2h2v2h-2zm-2 2h2v2h-2zm2 2h2v2h-2zm2-2h2v2h-2zm0-4h2v2h-2zm2 2h2v2h-2z'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='currentColor' d='M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zm8-12v8h8V3h-8zm6 6h-4V5h4v4zm0 10h2v2h-2zm-6-6h2v2h-2zm2 2h2v2h-2zm-2 2h2v2h-2zm2 2h2v2h-2zm2-2h2v2h-2zm0-4h2v2h-2zm2 2h2v2h-2z'/></svg>");
}

.share-action-btn__check {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 16px;
  height: 16px;
  opacity: 0;
  transition: opacity 0.2s;
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='currentColor' d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='currentColor' d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/></svg>");
}

.share-action-btn--copied .share-action-btn__icon { opacity: 0; }
.share-action-btn--copied .share-action-btn__check { opacity: 1; color: var(--forest-hi); }

.share-qr-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 14px 10px 4px;
}

.share-qr-canvas {
  background: oklch(96% 0.01 80);
  padding: 10px;
  border-radius: 3px;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}

.share-qr-canvas svg,
.share-qr-canvas img {
  display: block;
  width: 168px;
  height: 168px;
}

.share-qr-caption {
  font-size: 12px;
  color: var(--ink-muted);
  margin: 0;
}

/* --- Footer: message, countdown, start button --- */
.waiting-footer {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: stretch;
}

.waiting-msg {
  margin: 0;
  font-size: 14px;
  color: var(--ink-muted);
  font-style: italic;
}

.waiting-countdown {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  background: var(--copper-wash);
  border: 1px solid var(--copper);
  border-radius: var(--radius-sm);
}

.waiting-countdown[hidden] { display: none; }
.share-qr-panel[hidden]    { display: none; }
.waiting-edit.hidden       { display: none; }

.countdown-text {
  font-family: var(--font-display);
  font-size: 14px;
  color: var(--copper-hi);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

#countdown-num {
  display: inline-block;
  min-width: 20px;
  text-align: center;
  font-size: 18px;
  color: var(--copper);
  animation: countdown-tick 1s infinite;
}

@keyframes countdown-tick {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.15); }
}

.btn.btn-small {
  padding: 8px 14px;
  min-height: 34px;
  font-size: 11px;
}

/* ============ REACTIONS BAR ============ */

.reactions-bar {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 10px;
  padding: 12px 20px;
  /* Warm surface matching the rest of the UI instead of the cool-cast
     near-black the bar used to have. */
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-bottom: none;
  border-radius: var(--radius) var(--radius) 0 0;
  /* Ink-print top edge — echoes the shadow language on dice, chips,
     and pills so the bar feels like a stamped drawer lip on the felt
     instead of a floating modern toolbar. */
  box-shadow:
    0 -2px 0 oklch(0% 0 0 / 0.55),
    0 -10px 22px -8px oklch(0% 0 0 / 0.5);
  z-index: 50;
}

.reaction-btn {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  cursor: pointer;
  /* Ink-print shadow echoing the dice + chips — pressed glyph on a
     bone button, not a flat modal pill. */
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.06),
    0 1px 0 oklch(0% 0 0 / 0.45);
  transition: transform 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
  user-select: none;
  -webkit-user-select: none;
}

@media (hover: hover) {
  .reaction-btn:hover {
    background: var(--surface-3);
    border-color: var(--copper-lo);
    transform: translateY(-2px);
    box-shadow:
      inset 0 1px 0 oklch(100% 0 0 / 0.08),
      0 3px 0 oklch(0% 0 0 / 0.45);
  }
}

.reaction-btn:active {
  transform: translateY(1px) scale(0.96);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.04),
    0 0 0 oklch(0% 0 0 / 0.45);
}

/* Button-press feedback — the emoji pops when you click it. */
.reaction-btn--pop {
  animation: reaction-btn-pop 0.28s var(--ease-out-expo);
}
@keyframes reaction-btn-pop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.35); }
  100% { transform: scale(1); }
}

/* Targeting-mode emphasis on player chips — host users can see they're
   now clickable targets. Other chrome fades to keep the eye on chips. */
body.reacting #players-strip .player-chip {
  cursor: crosshair;
  outline: 2px dashed var(--copper);
  outline-offset: 4px;
  transition: transform 0.12s ease;
}
body.reacting #players-strip .player-chip:hover {
  transform: translateY(-2px);
}
body.reacting .reactions-bar { opacity: 0.45; pointer-events: auto; }

.reaction-targeting-banner {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 14px);
  left: 50%;
  transform: translate(-50%, -20px);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 14px 10px 18px;
  background: var(--copper);
  color: var(--copper-ink);
  border-radius: 4px;
  box-shadow: 0 2px 0 oklch(0% 0 0 / 0.45), 0 8px 18px -4px oklch(0% 0 0 / 0.5);
  font-family: var(--font-display);
  font-size: 14px;
  letter-spacing: 0.04em;
  z-index: 900;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s, transform 0.18s var(--ease-out-quart);
}
.reaction-targeting-banner.visible {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, 0);
}
.reaction-targeting-banner__cancel {
  appearance: none;
  background: oklch(100% 0 0 / 0.22);
  color: inherit;
  border: 1px solid oklch(100% 0 0 / 0.35);
  border-radius: 3px;
  padding: 4px 10px;
  font-family: var(--font-display);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  cursor: pointer;
}

/* ============ REACTION RAIN ============ */
/* All emojis rain from the top of the screen. Non-interactive — must not
   block clicks on dice, buttons, or chips below. */
.reaction-rain {
  position: fixed;
  font-size: 28px;
  pointer-events: none;
  z-index: 700;
  animation: reaction-fall 2.3s linear forwards;
  animation-delay: var(--fall-delay, 0ms);
  will-change: transform, opacity;
}
@keyframes reaction-fall {
  0%   { transform: translate(0, 0) rotate(0deg); opacity: 0; }
  10%  { opacity: 1; }
  90%  { opacity: 1; }
  100% { transform: translate(var(--fall-dx, 0), 110vh) rotate(var(--fall-rot, 0)); opacity: 0; }
}

/* Chip "hit" — target's chip bounces when an emoji lands on them. */
.player-chip.chip--hit {
  animation: chip-hit 0.6s var(--ease-out-expo);
}
@keyframes chip-hit {
  0%   { transform: translateY(0); }
  20%  { transform: translateY(-4px) scale(1.04); }
  45%  { transform: translateY(2px) scale(0.98); }
  100% { transform: translateY(0) scale(1); }
}

/* ============ REACTION COOLDOWN ============ */

.reactions-bar--cooldown {
  opacity: 0.5;
  filter: grayscale(0.5);
}
.reactions-bar--cooldown .reaction-btn {
  cursor: not-allowed;
}
.reactions-bar::after {
  content: attr(data-cooldown);
  position: absolute;
  top: -22px;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--oxblood-hi);
  background: var(--surface);
  border: 1px solid var(--oxblood);
  border-radius: 3px;
  padding: 3px 8px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s;
}
.reactions-bar--cooldown::after {
  opacity: 1;
}
/* Blocked-click flash when the user presses while rate-limited. */
.reactions-bar--blocked {
  animation: reactions-bar-shake 0.4s ease-out;
}
@keyframes reactions-bar-shake {
  0%, 100% { transform: translateX(-50%); }
  20%      { transform: translateX(calc(-50% - 6px)); }
  40%      { transform: translateX(calc(-50% + 6px)); }
  60%      { transform: translateX(calc(-50% - 4px)); }
  80%      { transform: translateX(calc(-50% + 4px)); }
}

/* ============ COMBO REACTION ============ */
.combo-reaction {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.2);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  pointer-events: none;
  z-index: 999;
  opacity: 0;
  transition: opacity 0.25s, transform 0.3s var(--ease-out-expo);
}
.combo-reaction--live {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  animation: combo-reaction 1.6s ease-out forwards;
}
.combo-reaction__emoji {
  font-size: 180px;
  filter: drop-shadow(0 6px 0 oklch(0% 0 0 / 0.5));
}
.combo-reaction__label {
  font-family: var(--font-display);
  font-size: 28px;
  letter-spacing: 0.1em;
  color: var(--copper-hi);
  text-shadow: 0 2px 0 oklch(0% 0 0 / 0.6);
}
@keyframes combo-reaction {
  0%   { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  25%  { transform: translate(-50%, -50%) scale(1.12); }
  85%  { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  100% { transform: translate(-50%, -55%) scale(0.95); opacity: 0; }
}

/* ============ GHOST SCORE OVERLAY ============ */

.ghost-score {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-display);
  font-size: 120px;
  font-weight: 700;
  color: var(--ink);
  opacity: 0.12;
  pointer-events: none;
  z-index: 0;
  line-height: 1;
  user-select: none;
  -webkit-user-select: none;
  transition: opacity 0.4s ease;
}

.ghost-score.pulse {
  animation: ghost-pulse 1.4s ease-in-out infinite;
}

@keyframes ghost-pulse {
  0%, 100% { opacity: 0.12; }
  50% { opacity: 0.25; }
}

.ghost-score.beaten {
  animation: ghost-beaten 0.7s ease-out forwards;
}

@keyframes ghost-beaten {
  0% {
    opacity: 0.25;
    transform: translate(-50%, -50%) scale(1) rotate(0deg);
    color: var(--ink);
  }
  40% {
    opacity: 0.35;
    transform: translate(-50%, -50%) scale(1.08) rotate(-2deg);
    color: var(--ruby);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.6) rotate(8deg);
    color: var(--ruby);
  }
}

/* ============ RECAP TICKER ============ */

.recap-overlay {
  position: fixed;
  inset: 0;
  background: oklch(12% 0.015 260 / 0.92);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  z-index: 110;
  padding: 40px 20px;
  animation: fadeIn 0.3s ease-out;
}

.recap-line {
  font-family: var(--font-display);
  font-size: clamp(18px, 3.5vw, 28px);
  font-weight: 700;
  color: var(--text);
  text-align: center;
  letter-spacing: -0.01em;
  line-height: 1.4;
  opacity: 0;
  transform: translateY(12px);
  animation: recap-fade 2.4s ease forwards;
}

@keyframes recap-fade {
  0% {
    opacity: 0;
    transform: translateY(12px);
  }
  15% {
    opacity: 1;
    transform: translateY(0);
  }
  75% {
    opacity: 1;
    transform: translateY(0);
  }
  100% {
    opacity: 0;
    transform: translateY(-8px);
  }
}

/* ============ ODDS WHISPER ============ */

.odds-whisper {
  font-size: 12px;
  color: var(--text-muted);
  text-align: center;
  font-weight: 500;
  letter-spacing: 0.01em;
  margin-top: 2px;
  min-height: 18px;
  opacity: 0.8;
  transition: opacity 0.3s ease;
}

/* ============ WATCHING STATE ============ */

.watching-indicator {
  text-align: center;
  font-size: 14px;
  color: var(--text-dim);
  font-weight: 600;
  padding: 10px 0;
  letter-spacing: -0.01em;
  animation: watching-pulse 2s ease-in-out infinite;
}

@keyframes watching-pulse {
  0%, 100% { opacity: 0.7; }
  50% { opacity: 1; }
}

/* ============ RESULT READY BADGE ============ */

.result-ready-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin-left: 6px;
  border-radius: 50%;
  background: var(--forest);
  color: var(--forest-ink);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  vertical-align: middle;
}

/* Ready stripe only applies when the row isn't already the winner (the
   winner's copper stripe dominates). Uses box-shadow inset so it layers
   with the existing border-left without conflicting. */
.result-row--ready:not(.winner) {
  box-shadow: inset 3px 0 0 var(--forest);
}

/* ============ RESULT STREAKS ============ */

.result-streaks {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 8px;
}

.streak-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 12px;
  border-radius: 100px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: var(--gold-dim);
  color: var(--gold);
  border: 1px solid var(--copper-lo);
  animation: streak-pop 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.3);
}

@keyframes streak-pop {
  0%   { transform: scale(0); opacity: 0; }
  60%  { transform: scale(1.15); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* ============ RESPONSIVE ============ */

@media (max-width: 900px) {
  .setup-grid {
    grid-template-columns: 1fr;
  }

  .rules-card {
    position: static;
    order: -1;
  }

  .lobby-grid {
    grid-template-columns: 1fr;
  }

  /* Mobile: games list on top so people browse first, create below.
     The in-DOM order is create-then-games for desktop layout; we flip
     it here so the browser pattern reads "which table should I join?"
     before "I'll start my own." */
  .lobby-games { order: -1; }
  .lobby-create { order: 0; }

  .game-list {
    max-height: 320px;
  }

  .game-wrapper {
    padding: 14px;
    gap: 14px;
  }

  .game-header {
    gap: 10px;
  }

  .header-right {
    order: 2;
    width: 100%;
  }

  .players-strip {
    justify-content: flex-start;
  }

  .table-felt {
    padding: 28px 16px 32px;
  }

  .die {
    --size: 56px;
    --pip-size: 9px;
    --pip-pad: 9px;
    --pip-gap: 3px;
    border-radius: 11px;
  }

  .die--banked {
    --size: 44px;
    --pip-size: 7px;
    --pip-pad: 7px;
    --pip-gap: 2px;
    border-radius: 8px;
  }

  .die-slot {
    --size: 44px;
    border-radius: 8px;
  }

  .dice-row {
    gap: 10px;
    min-height: 70px;
  }

  .dice-row--banked {
    gap: 8px;
    min-height: 56px;
  }

  .current-name {
    font-size: 26px;
  }

  .qual-strip {
    gap: 8px;
  }

  .qual-chip, .score-chip {
    padding: 8px 12px;
  }

  .actions .btn {
    min-width: 200px;
  }

  .reaction-btn {
    width: 38px;
    height: 38px;
    font-size: 18px;
  }

  .reactions-bar {
    gap: 6px;
    padding: 8px 14px;
  }

  .ghost-score {
    font-size: 90px;
  }

  .waiting-wrapper {
    padding: 28px 22px;
  }

  .waiting-title {
    font-size: 24px;
  }
}

@media (max-width: 480px) {
  .container {
    padding: 20px 16px;
  }

  .setup-header {
    margin-bottom: 28px;
    padding-top: 24px;
  }

  .card {
    padding: 20px;
  }

  .field-row {
    grid-template-columns: 1fr;
    gap: 14px;
  }

  .count-selector {
    grid-template-columns: repeat(7, 1fr);
    gap: 6px;
  }

  .player-inputs {
    grid-template-columns: 1fr;
  }

  .count-btn {
    padding: 10px 0;
    font-size: 14px;
  }

  .table-felt {
    padding: 22px 12px 26px;
  }

  .die {
    --size: 46px;
    --pip-size: 7px;
    --pip-pad: 7px;
    --pip-gap: 2px;
    border-radius: 9px;
  }

  .die--banked {
    --size: 38px;
    --pip-size: 6px;
    --pip-pad: 6px;
    --pip-gap: 2px;
    border-radius: 7px;
  }

  .die-slot {
    --size: 38px;
    border-radius: 7px;
  }

  .dice-row {
    gap: 8px;
    min-height: 58px;
  }

  .dice-row--banked {
    gap: 6px;
    min-height: 50px;
  }

  .current-name {
    font-size: 22px;
  }

  .qual-chip {
    padding: 7px 10px;
    gap: 8px;
  }

  .qual-label {
    font-size: 10px;
  }

  .score-chip {
    padding: 5px 14px;
    min-width: 68px;
  }

  .score-chip .score-value {
    font-size: 18px;
  }

  .actions > .btn,
  .results-actions > .btn {
    min-width: 100%;
    width: 100%;
  }
  /* Top/Bottom buttons share a row inside .tb-choices and must not be
     forced to 100% width (that blows them past the viewport). */
  .actions .tb-choices .btn {
    min-width: 0;
    width: auto;
  }
  .overlay-card {
    padding: 32px 24px;
  }

  .overlay-name {
    font-size: 36px;
  }

  .lobby-create,
  .lobby-games {
    padding: 20px;
  }

  .game-listing {
    padding: 12px 14px;
    gap: 10px;
    flex-wrap: wrap;
  }

  .listing-host {
    font-size: 14px;
  }

  .join-btn {
    padding: 8px 16px;
    font-size: 12px;
  }

  .game-list {
    max-height: 260px;
  }

  .waiting-wrapper {
    padding: 24px 18px;
  }

  .waiting-title {
    font-size: 22px;
  }

  .waiting-player {
    padding: 10px 12px;
  }

  .waiting-share {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }

  .reaction-btn {
    width: 34px;
    height: 34px;
    font-size: 16px;
  }

  .reactions-bar {
    gap: 5px;
    padding: 7px 12px;
  }

  .reaction-float {
    font-size: 26px;
    bottom: 60px;
  }

  .ghost-score {
    font-size: 72px;
  }

  .recap-line {
    font-size: clamp(16px, 4vw, 22px);
  }

  .streak-badge {
    font-size: 11px;
    padding: 4px 10px;
  }

  .result-row {
    grid-template-columns: 28px 1fr auto;
    gap: 10px;
    padding: 14px 16px;
  }

  .result-rank {
    font-size: 18px;
  }

  .result-name {
    font-size: 15px;
  }

  .result-dice {
    grid-column: 1 / -1;
    justify-content: center;
    margin-top: 6px;
    flex-wrap: wrap;
  }

  .result-score {
    font-size: 26px;
  }
}

/* =================================================================
   Accessibility & input-mode adaptation
   ================================================================= */

/* Touch devices — WCAG 2.5.5 minimum tap target */
@media (pointer: coarse) {
  .mode-tab,
  .btn,
  .chip,
  .tab,
  button,
  [role="button"],
  a.btn,
  .game-row button,
  .reaction-btn {
    min-height: 44px;
  }

  .mode-tab,
  .btn,
  a.btn {
    min-width: 44px;
  }

  /* Small round icon buttons — opt out of the blanket min-height. Their
     visual size is intentional (20-22px circle); the blanket rule was
     stretching them to 20x44 ovals on mobile. Hit target is extended
     via an invisible pseudo-element instead so thumbs still reach them. */
  .toggle-info,
  .seat-kick-btn,
  .share-action-btn {
    min-height: 0 !important;
    min-width: 0 !important;
    position: relative;
  }
  .toggle-info::before,
  .seat-kick-btn::before,
  .share-action-btn::before {
    content: '';
    position: absolute;
    inset: -12px;
  }
}

/* =================================================================
   Connection status banner + lobby status row
   ================================================================= */

.connection-banner {
  position: fixed;
  left: 50%;
  top: calc(env(safe-area-inset-top) + 12px);
  transform: translate(-50%, -24px);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 14px 10px 16px;
  background: var(--oxblood);
  color: var(--oxblood-ink);
  border-radius: 4px;
  box-shadow: 0 2px 0 oklch(0% 0 0 / 0.45), 0 8px 18px -4px oklch(0% 0 0 / 0.5);
  font-family: var(--font-body);
  font-size: 14px;
  z-index: 1000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s var(--ease-out-quart);
}

.connection-banner--visible {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, 0);
}

.connection-banner__retry,
.lobby-status__retry {
  appearance: none;
  background: oklch(100% 0 0 / 0.14);
  color: inherit;
  border: 1px solid oklch(100% 0 0 / 0.3);
  border-radius: 3px;
  padding: 6px 10px;
  font-family: var(--font-display);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
  min-height: 32px;
}

.connection-banner__retry:hover,
.lobby-status__retry:hover {
  background: oklch(100% 0 0 / 0.22);
}

.lobby-status {
  min-height: 1.5em;
}

.lobby-status--alert {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--oxblood-hi);
  font-weight: 500;
}

/* =================================================================
   Sound toggle — persistent top-right corner button
   ================================================================= */

/* Persistent chrome bar — fixed strip at the top of every screen
   containing connection status, help, sound. Page content gets a top
   padding equal to the bar height so nothing renders BEHIND the
   chrome. Previously the cluster was fixed-positioned with no
   companion padding, which is why everything peeked out from
   underneath like a shitty overlay. */
.chrome-cluster {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 500;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;     /* pin to the right by default */
  gap: 8px;
  padding: calc(env(safe-area-inset-top) + 10px)
           calc(env(safe-area-inset-right) + 14px)
           10px
           calc(env(safe-area-inset-left) + 14px);
  background: linear-gradient(
    to bottom,
    oklch(18% 0.015 55 / 0.92) 0%,
    oklch(18% 0.015 55 / 0.78) 70%,
    oklch(18% 0.015 55 / 0) 100%
  );
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  pointer-events: none;          /* let clicks pass through gaps */
}
.chrome-cluster > * {
  pointer-events: auto;          /* but the buttons themselves stay clickable */
}
.chrome-cluster .sfx-toggle,
.chrome-cluster .conn-pill,
.chrome-cluster .chrome-btn {
  position: static;     /* override the .sfx-toggle fixed positioning */
}

/* Push every screen's content below the chrome bar so nothing renders
   underneath. Using body padding-top because the bar is fixed and lives
   outside the document flow; main + each .screen sit inside body. */
body {
  padding-top: calc(env(safe-area-inset-top) + 52px);
}
@media (max-width: 480px) {
  body {
    padding-top: calc(env(safe-area-inset-top) + 46px);
  }
}
/* In-game screens compact the game-header's top breathing room so we
   don't stack two 50+px bars before the play area. */
body[data-screen="game"] .game-header,
body[data-screen="gin-game"] .game-header {
  padding-top: var(--space-xs);
}
body[data-screen="gin-game"] .gin-wrapper {
  padding-top: var(--space-xs);
}
/* Shrink the chrome buttons a touch in-game so they don't swallow the
   top area. 32 instead of 36. */
body[data-screen="game"] .chrome-cluster .chrome-btn,
body[data-screen="game"] .chrome-cluster .sfx-toggle,
body[data-screen="gin-game"] .chrome-cluster .chrome-btn,
body[data-screen="gin-game"] .chrome-cluster .sfx-toggle {
  width: 32px;
  height: 32px;
}
body[data-screen="game"] .chrome-cluster,
body[data-screen="gin-game"] .chrome-cluster {
  padding: calc(env(safe-area-inset-top) + 6px)
           calc(env(safe-area-inset-right) + 12px)
           6px
           calc(env(safe-area-inset-left) + 12px);
}

/* Generic chrome button (matches sfx-toggle visual weight). */
.chrome-btn {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  color: var(--ink-dim);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  transition: color 0.15s, border-color 0.15s;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}
.chrome-btn:hover {
  color: var(--copper);
  border-color: var(--copper);
}

/* Connection pill — shows live socket status. Color-coded dot + label.
   The label collapses on narrow viewports (icon-only). */
.conn-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  height: 36px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 18px;
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-dim);
  cursor: default;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}
.conn-pill__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ink-faint);
  transition: background 0.15s;
}
.conn-pill[data-status="open"]         .conn-pill__dot { background: oklch(72% 0.15 145); }
.conn-pill[data-status="connecting"]   .conn-pill__dot { background: oklch(72% 0.14 80); animation: conn-pulse 1.2s ease-in-out infinite; }
.conn-pill[data-status="reconnecting"] .conn-pill__dot { background: oklch(72% 0.14 80); animation: conn-pulse 1.2s ease-in-out infinite; }
.conn-pill[data-status="closed"]       .conn-pill__dot,
.conn-pill[data-status="error"]        .conn-pill__dot { background: oklch(60% 0.18 25); }
.conn-pill[data-status="solo"]         .conn-pill__dot { background: var(--copper-hi); }
.conn-pill[data-status="solo"]         { color: var(--copper-hi); }
.conn-pill[data-status="open"]         { color: var(--ink); }
.conn-pill[data-status="reconnecting"],
.conn-pill[data-status="connecting"]   { color: oklch(72% 0.14 80); }
.conn-pill[data-status="closed"],
.conn-pill[data-status="error"]        { color: oklch(60% 0.18 25); }
@keyframes conn-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}
@media (max-width: 480px) {
  .conn-pill__label { display: none; }
  .conn-pill { padding: 0 10px; width: 36px; justify-content: center; }
}

/* Help / How-to-play overlay — full-screen scrim + centered panel. */
.help-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s var(--ease-out-quart);
}
.help-overlay--open {
  opacity: 1;
  pointer-events: auto;
}
.help-overlay__scrim {
  position: absolute;
  inset: 0;
  background: oklch(0% 0 0 / 0.55);
}
.help-overlay__panel {
  position: relative;
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-drop);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.help-overlay__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border);
}
.help-overlay__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  color: var(--copper-hi);
}
.help-overlay__close {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--ink-dim);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 20px;
  line-height: 1;
}
.help-overlay__close:hover {
  color: var(--copper);
  border-color: var(--copper);
}
.help-overlay__tabs {
  display: flex;
  gap: 0;
  padding: 0 var(--space-md);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.help-overlay__tab {
  background: transparent;
  border: none;
  color: var(--ink-dim);
  padding: 10px 14px;
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.help-overlay__tab--active {
  color: var(--copper-hi);
  border-bottom-color: var(--copper-hi);
}
.help-overlay__body {
  padding: var(--space-md) var(--space-lg);
  overflow-y: auto;
  font-family: var(--font-body);
  color: var(--ink);
  font-size: var(--text-base);
  line-height: 1.5;
}
.help-overlay__body h3 {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  color: var(--copper-hi);
  margin: var(--space-md) 0 var(--space-xs);
}
.help-overlay__body h3:first-child { margin-top: 0; }
.help-overlay__body p { margin: 0 0 var(--space-sm); }
.help-overlay__body .help-list {
  margin: 0 0 var(--space-sm);
  padding-left: var(--space-lg);
}
.help-overlay__body .help-list li { margin-bottom: 4px; }
.help-overlay__body kbd {
  font-family: var(--font-display);
  font-size: 11px;
  padding: 2px 6px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--copper-hi);
}
.help-overlay__body .help-pill {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.help-overlay__body .help-pill--set { background: var(--forest-wash); color: var(--forest-hi); border: 1px solid var(--forest); }
.help-overlay__body .help-pill--run { background: oklch(55% 0.14 240 / 0.15); color: oklch(58% 0.14 240); border: 1px solid oklch(55% 0.14 240); }

/* "Play solo vs computer" — secondary CTA inside the gin settings
   panel. Sits below the settings blurb, above the primary "Create
   Game" button. Ghost styling so it doesn't compete with the
   multiplayer flow. */
.solo-gin-btn {
  width: 100%;
  margin-top: var(--space-md);
  font-size: var(--text-sm);
}

/* Big primary "Share this table" CTA in the waiting room. */
.share-table-btn {
  width: 100%;
  margin-bottom: var(--space-sm);
  font-size: var(--text-base);
}
.share-table-btn--copied {
  background: var(--forest);
  border-color: var(--forest-hi);
}

.sfx-toggle {
  /* Legacy positioning preserved as a fallback for any context where the
     toggle is rendered OUTSIDE .chrome-cluster. Inside the cluster, the
     `.chrome-cluster .sfx-toggle { position: static }` rule above
     overrides this. */
  position: fixed;
  top: calc(env(safe-area-inset-top) + 14px);
  left: calc(env(safe-area-inset-left) + 14px);
  right: auto;
  bottom: auto;
  z-index: 500;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  color: var(--ink-dim);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  box-shadow: 0 1px 0 oklch(0% 0 0 / 0.4);
}

.sfx-toggle:hover {
  color: var(--copper);
  border-color: var(--copper);
}

.sfx-toggle__icon {
  display: block;
  width: 18px;
  height: 18px;
  background-color: currentColor;
  -webkit-mask: var(--sfx-on-icon) center/contain no-repeat;
  mask: var(--sfx-on-icon) center/contain no-repeat;
  --sfx-on-icon: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'><path d='M3 9v6h4l5 5V4L7 9H3zm13.5 3a4.5 4.5 0 00-2.5-4.03v8.06a4.5 4.5 0 002.5-4.03zm-2.5-9v2.06A7 7 0 0119 12a7 7 0 01-5 6.71V21a9 9 0 000-17.87z'/></svg>");
}

.sfx-toggle[aria-pressed="false"] .sfx-toggle__icon {
  --sfx-on-icon: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'><path d='M16.5 12a4.5 4.5 0 00-2.5-4.03v2.21l2.45 2.45c.03-.21.05-.42.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.9 8.9 0 0021 12a9 9 0 00-7-8.77v2.06A7 7 0 0119 12zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.17v2.06a9 9 0 003.69-1.81L19.73 21 21 19.73 12 10.73 4.27 3zM12 4L9.91 6.09 12 8.18V4z'/></svg>");
}

.sfx-toggle[aria-pressed="false"] {
  color: var(--ink-faint);
}

/* In-game: the toggle gets re-parented into .table-felt (see game.js
   showScreen) so it can sit absolutely positioned inside the playing
   surface — top-right of the board, safely below the player chips. */
.table-felt {
  position: relative;
}

.table-felt > .sfx-toggle {
  position: absolute;
  top: 14px;
  right: 14px;
  left: auto;
  bottom: auto;
  width: 32px;
  height: 32px;
  z-index: 5;
}

/* Respect reduced-motion preference — disables all animations/transitions.
   Applied last so it wins the cascade. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
