/* ── Layout ────────────────────────────────────────────────────── */

.app {
  /* A fixed height makes the middle row definite, which lets the table
     measure itself against the room actually left over. */
  height: 100dvh;
  /* Double-tapping the felt is how you check, so nothing on the table is
     selectable text: a tap-tap should never leave the board highlighted. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  /* the one column can never be wider than the app: a felt whose width is
     a percentage has an intrinsic size the grid would otherwise honour,
     and the whole stage slid off the right of a narrow phone */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-sm);
  /* On a home screen the page runs under the clock and the home bar, so
     the top and bottom never come in under the safe area. The sizes
     below set the three variables rather than the shorthand, which would
     throw the insets away. */
  --pad-t: 18px; --pad-x: 20px; --pad-b: 22px;
  padding: max(var(--pad-t), env(safe-area-inset-top, 0px)) var(--pad-x) max(var(--pad-b), env(safe-area-inset-bottom, 0px));
  max-width: 1180px;
  margin: 0 auto;
  /* the control deck's width, which is also the width the table refuses
     to come out narrower than */
  --panel-w: 640px;
}

/* ── Topbar ────────────────────────────────────────────────────── */

.topbar {
  /* above the rail drawer, so the button that opened it can close it */
  position: relative;
  z-index: 32;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.mark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  user-select: none;
}

/* four suits in the mark, for the four of a kind the name never promises */
.mark-glyph {
  display: grid;
  place-items: center;
  flex: none;
  width: 24px;
  height: 32px;
}
.mark-glyph .mark-logo { width: 100%; height: 100%; display: block; filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .18)); }

.mark-word {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 1.6px;
  color: var(--ink-3);
  white-space: nowrap;
}

.mark-variant {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-family: var(--font-text);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .7px;
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}
.mark-variant:empty { display: none; }

.topbar-actions { display: flex; gap: 8px; }

/* ── Felt ──────────────────────────────────────────────────────── */

.stage {
  display: grid;
  place-items: center;
  min-height: 0;
}

.felt {
  position: relative;
  /* --stage-h is the height actually left over once the top bar and the
     dock have taken theirs; app.js measures it. Width leads: the table
     takes the room the page has, and never comes out narrower than the
     control deck under it. Height follows from the ratio, capped at what
     is left, so the page never scrolls. */
  width: min(100%, max(var(--panel-w, 640px), var(--stage-h, 460px) * 15 / 9.2));
  aspect-ratio: 15 / 9.2;
  max-height: var(--stage-h, 460px);
  --card-w: clamp(26px, var(--felt-h, 420px) * .152, 62px);

  /* The felt has two dressings. Classic is the table this design started
     as, the same soft greys as everything else. Green is the card room.
     Everything drawn on the felt reads these three, so switching is one
     line and nothing downstream has to know. */
  --felt-line: var(--line);
  --felt-slot: rgba(0, 0, 0, .022);
  --on-felt-dim: var(--ink-4);
  --rail-w: 10px;

  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow:
    inset 0 0 0 var(--rail-w) var(--surface-2),
    inset 0 0 0 calc(var(--rail-w) + 1px) var(--line),
    var(--ring),
    var(--lift-lg);
  isolation: isolate;
}

html[data-felt="green"] .felt,
.swatch[data-felt="green"], .recap-felt[data-felt="green"] {
  --felt-line: rgba(255, 255, 255, .15);
  --felt-slot: rgba(0, 0, 0, .10);
  --on-felt-dim: var(--on-felt-3);
  background:
    radial-gradient(96% 128% at 50% -22%, var(--felt-hi) 0%, var(--felt) 46%, var(--felt-lo) 100%);
  box-shadow:
    /* the rail, then a hairline of light along its inside edge */
    inset 0 0 0 var(--rail-w) var(--rail),
    inset 0 0 0 calc(var(--rail-w) + 1px) rgba(255, 255, 255, .07),
    inset 0 14px 26px -14px rgba(0, 0, 0, .55),
    var(--lift-lg);
}

/* the sheen a felt takes under a card-room light, nothing more */
.felt-grain,
.swatch-grain,
.recap-grain {
  position: absolute;
  inset: var(--rail-w);
  border-radius: calc(var(--r-xl) - 7px);
  background: radial-gradient(78% 62% at 50% 44%, var(--bg) 0%, transparent 72%);
  opacity: .9;
  pointer-events: none;
}
html[data-felt="green"] .felt-grain,
.swatch[data-felt="green"] .swatch-grain, .recap-felt[data-felt="green"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(64% 52% at 50% 40%, rgba(255, 255, 255, .10) 0%, transparent 70%),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .30) 0%, transparent 62%);
}

/* ── The bought felts ──────────────────────────────────────────────
   Each one is a cloth and a rail, drawn in gradients, and the same rule
   dresses the little swatch in the shop so the preview is the real
   thing. Everything that sits on the felt - plates, pot, cards - brings
   its own background, so the felt only has to be a good floor: dark
   enough for a white card to pop, calm enough in the middle for the
   board to read. The three tokens are what the slots, the pile and the
   street label read. */

html[data-felt="midnight"] .felt, .swatch[data-felt="midnight"], .recap-felt[data-felt="midnight"],
html[data-felt="bordeaux"] .felt, .swatch[data-felt="bordeaux"], .recap-felt[data-felt="bordeaux"],
html[data-felt="slate"] .felt,    .swatch[data-felt="slate"], .recap-felt[data-felt="slate"],
html[data-felt="ocean"] .felt,    .swatch[data-felt="ocean"], .recap-felt[data-felt="ocean"],
html[data-felt="monaco"] .felt,   .swatch[data-felt="monaco"], .recap-felt[data-felt="monaco"],
html[data-felt="ember"] .felt,    .swatch[data-felt="ember"], .recap-felt[data-felt="ember"],
html[data-felt="neon"] .felt,     .swatch[data-felt="neon"], .recap-felt[data-felt="neon"],
html[data-felt="aurora"] .felt,   .swatch[data-felt="aurora"], .recap-felt[data-felt="aurora"] {
  --felt-line: rgba(255, 255, 255, .16);
  --felt-slot: rgba(0, 0, 0, .18);
  --on-felt-dim: rgba(255, 255, 255, .38);
}

/* Midnight: navy, a little lighter where the lamp is, stars at the edges */
html[data-felt="midnight"] .felt, .swatch[data-felt="midnight"], .recap-felt[data-felt="midnight"] {
  background:
    radial-gradient(90% 120% at 50% -10%, #223a68 0%, #14264a 45%, #0b1630 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #171c2b,
    inset 0 0 0 calc(var(--rail-w) + 1px) rgba(160, 190, 255, .16),
    inset 0 14px 26px -14px rgba(0, 0, 0, .6),
    var(--lift-lg);
}
html[data-felt="midnight"] .felt-grain, .swatch[data-felt="midnight"] .swatch-grain, .recap-felt[data-felt="midnight"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(1.5px 1.5px at 8% 22%,  rgba(255,255,255,.75), transparent 60%),
    radial-gradient(1px 1px at 17% 71%,     rgba(255,255,255,.55), transparent 60%),
    radial-gradient(1.5px 1.5px at 26% 12%, rgba(255,255,255,.6),  transparent 60%),
    radial-gradient(1px 1px at 34% 88%,     rgba(255,255,255,.5),  transparent 60%),
    radial-gradient(1px 1px at 61% 9%,      rgba(255,255,255,.6),  transparent 60%),
    radial-gradient(1.5px 1.5px at 73% 84%, rgba(255,255,255,.7),  transparent 60%),
    radial-gradient(1px 1px at 84% 27%,     rgba(255,255,255,.55), transparent 60%),
    radial-gradient(1.5px 1.5px at 92% 66%, rgba(255,255,255,.65), transparent 60%),
    radial-gradient(1px 1px at 47% 94%,     rgba(255,255,255,.45), transparent 60%),
    radial-gradient(1px 1px at 5% 50%,      rgba(255,255,255,.5),  transparent 60%),
    radial-gradient(1px 1px at 95% 45%,     rgba(255,255,255,.5),  transparent 60%),
    radial-gradient(60% 50% at 50% 42%, rgba(120, 150, 230, .10) 0%, transparent 70%),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .35) 0%, transparent 62%);
}

/* Bordeaux: wine on walnut */
html[data-felt="bordeaux"] .felt, .swatch[data-felt="bordeaux"], .recap-felt[data-felt="bordeaux"] {
  background:
    radial-gradient(92% 124% at 50% -16%, #8a2238 0%, #6a1529 46%, #430b1a 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #3b2419,
    inset 0 0 0 calc(var(--rail-w) + 1px) rgba(255, 200, 160, .14),
    inset 0 14px 26px -14px rgba(0, 0, 0, .6),
    var(--lift-lg);
}
html[data-felt="bordeaux"] .felt-grain, .swatch[data-felt="bordeaux"] .swatch-grain, .recap-felt[data-felt="bordeaux"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(62% 50% at 50% 40%, rgba(255, 190, 190, .10) 0%, transparent 70%),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .34) 0%, transparent 62%);
}

/* Slate: charcoal with a drafting grid on it */
html[data-felt="slate"] .felt, .swatch[data-felt="slate"], .recap-felt[data-felt="slate"] {
  --felt-line: rgba(255, 255, 255, .2);
  background:
    radial-gradient(90% 120% at 50% -10%, #3a4047 0%, #272c32 48%, #191d21 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #101214,
    inset 0 0 0 calc(var(--rail-w) + 1px) rgba(255, 255, 255, .12),
    inset 0 14px 26px -14px rgba(0, 0, 0, .6),
    var(--lift-lg);
}
html[data-felt="slate"] .felt-grain, .swatch[data-felt="slate"] .swatch-grain, .recap-felt[data-felt="slate"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(70% 60% at 50% 45%, rgba(255, 255, 255, .06) 0%, transparent 70%),
    repeating-linear-gradient(0deg,  rgba(255, 255, 255, .055) 0 1px, transparent 1px var(--grid, 26px)),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, .055) 0 1px, transparent 1px var(--grid, 26px)),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .3) 0%, transparent 62%);
}
.swatch[data-felt="slate"], .recap-felt[data-felt="slate"] { --grid: 7px; }

/* Ocean: teal, with light moving over it */
html[data-felt="ocean"] .felt, .swatch[data-felt="ocean"], .recap-felt[data-felt="ocean"] {
  background:
    radial-gradient(92% 124% at 50% -16%, #12606e 0%, #0b4552 46%, #052b36 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #0a1e24,
    inset 0 0 0 calc(var(--rail-w) + 1px) rgba(140, 230, 240, .16),
    inset 0 14px 26px -14px rgba(0, 0, 0, .55),
    var(--lift-lg);
}
html[data-felt="ocean"] .felt-grain, .swatch[data-felt="ocean"] .swatch-grain, .recap-felt[data-felt="ocean"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(38% 46% at 30% 30%, rgba(160, 240, 250, .16) 0%, transparent 70%),
    radial-gradient(34% 40% at 72% 66%, rgba(160, 240, 250, .13) 0%, transparent 70%),
    radial-gradient(26% 30% at 60% 22%, rgba(255, 255, 255, .07) 0%, transparent 70%),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .3) 0%, transparent 62%);
  background-size: 160% 160%, 160% 160%, 160% 160%, 100% 100%;
  animation: felt-ocean 16s ease-in-out infinite alternate;
}
@keyframes felt-ocean {
  0%   { background-position: 0% 0%,     100% 100%, 50% 0%,   50% 50%; }
  50%  { background-position: 100% 40%,  0% 60%,    20% 100%, 50% 50%; }
  100% { background-position: 30% 100%,  70% 0%,    100% 30%, 50% 50%; }
}

/* Monaco: emerald pinstripe under a gold rail */
html[data-felt="monaco"] .felt, .swatch[data-felt="monaco"], .recap-felt[data-felt="monaco"] {
  --felt-line: rgba(240, 210, 122, .28);
  --on-felt-dim: rgba(240, 220, 160, .5);
  background:
    radial-gradient(92% 124% at 50% -16%, #135a45 0%, #0c3e30 46%, #06261d 100%);
  box-shadow:
    inset 0 0 0 max(2px, calc(var(--rail-w) * .3)) #d4b15c,
    inset 0 0 0 var(--rail-w) #5e4614,
    inset 0 0 0 calc(var(--rail-w) + 1px) rgba(240, 210, 122, .55),
    inset 0 14px 26px -14px rgba(0, 0, 0, .55),
    var(--lift-lg);
}
html[data-felt="monaco"] .felt-grain, .swatch[data-felt="monaco"] .swatch-grain, .recap-felt[data-felt="monaco"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(64% 52% at 50% 40%, rgba(255, 240, 200, .09) 0%, transparent 70%),
    repeating-linear-gradient(135deg, rgba(240, 210, 122, .07) 0 1px, transparent 1px var(--pin, 9px)),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .32) 0%, transparent 62%);
}
.swatch[data-felt="monaco"], .recap-felt[data-felt="monaco"] { --pin: 4px; }

/* Ember: black felt lit from underneath, and it breathes */
html[data-felt="ember"] .felt, .swatch[data-felt="ember"], .recap-felt[data-felt="ember"] {
  --felt-line: rgba(255, 140, 60, .26);
  --felt-slot: rgba(0, 0, 0, .3);
  --on-felt-dim: rgba(255, 190, 140, .5);
  background:
    radial-gradient(90% 120% at 50% -10%, #2a1a14 0%, #160d0a 46%, #0a0504 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #0b0807,
    inset 0 0 0 calc(var(--rail-w) + 1px) rgba(255, 120, 50, .22),
    inset 0 0 40px 4px rgba(255, 90, 20, .12),
    var(--lift-lg);
}
html[data-felt="ember"] .felt-grain, .swatch[data-felt="ember"] .swatch-grain, .recap-felt[data-felt="ember"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(70% 70% at 50% 112%, rgba(255, 120, 40, .42) 0%, rgba(255, 60, 10, .16) 40%, transparent 72%),
    radial-gradient(40% 40% at 12% 100%, rgba(255, 90, 20, .2) 0%, transparent 70%),
    radial-gradient(40% 40% at 88% 100%, rgba(255, 90, 20, .2) 0%, transparent 70%),
    radial-gradient(60% 50% at 50% 40%, rgba(255, 200, 150, .05) 0%, transparent 70%);
  animation: felt-ember 7s ease-in-out infinite;
}
@keyframes felt-ember {
  0%, 100% { opacity: .62; }
  50%      { opacity: 1; }
}

/* Neon: black cloth, cyan rail light, magenta in the corners */
html[data-felt="neon"] .felt, .swatch[data-felt="neon"], .recap-felt[data-felt="neon"] {
  --felt-line: rgba(60, 235, 255, .32);
  --felt-slot: rgba(0, 0, 0, .3);
  --on-felt-dim: rgba(120, 240, 255, .55);
  background:
    radial-gradient(90% 120% at 50% -10%, #171724 0%, #0d0d16 46%, #06060b 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #0a0a10,
    inset 0 0 0 calc(var(--rail-w) + 1.5px) #35e3ff,
    inset 0 0 0 calc(var(--rail-w) + 3px) rgba(53, 227, 255, .28),
    inset 0 0 34px 2px rgba(53, 227, 255, .28),
    0 0 0 1px rgba(53, 227, 255, .25),
    0 0 40px -6px rgba(53, 227, 255, .35),
    var(--lift-lg);
}
html[data-felt="neon"] .felt-grain, .swatch[data-felt="neon"] .swatch-grain, .recap-felt[data-felt="neon"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(40% 46% at 0% 100%,   rgba(255, 50, 200, .38) 0%, transparent 70%),
    radial-gradient(40% 46% at 100% 100%, rgba(255, 50, 200, .38) 0%, transparent 70%),
    radial-gradient(34% 40% at 0% 0%,     rgba(120, 60, 255, .3) 0%, transparent 70%),
    radial-gradient(34% 40% at 100% 0%,   rgba(120, 60, 255, .3) 0%, transparent 70%),
    radial-gradient(56% 46% at 50% 44%, rgba(53, 227, 255, .06) 0%, transparent 70%);
  animation: felt-neon 5s ease-in-out infinite;
}
@keyframes felt-neon {
  0%, 100% { opacity: .8; }
  46%      { opacity: 1; }
  50%      { opacity: .86; }
  54%      { opacity: 1; }
}

/* Aurora: colour drifting across the felt, slowly */
html[data-felt="aurora"] .felt, .swatch[data-felt="aurora"], .recap-felt[data-felt="aurora"] {
  --felt-line: rgba(255, 255, 255, .2);
  background:
    radial-gradient(90% 120% at 50% -10%, #1a3a46 0%, #12253a 46%, #0b1424 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #0e1219,
    inset 0 0 0 calc(var(--rail-w) + 1px) rgba(180, 255, 230, .16),
    inset 0 14px 26px -14px rgba(0, 0, 0, .55),
    var(--lift-lg);
}
html[data-felt="aurora"] .felt-grain, .swatch[data-felt="aurora"] .swatch-grain, .recap-felt[data-felt="aurora"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(50% 60% at 20% 30%, rgba(40, 220, 160, .34) 0%, transparent 70%),
    radial-gradient(50% 60% at 80% 70%, rgba(140, 80, 255, .34) 0%, transparent 70%),
    radial-gradient(46% 50% at 60% 20%, rgba(40, 200, 255, .26) 0%, transparent 70%),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .3) 0%, transparent 62%);
  background-size: 220% 220%, 220% 220%, 220% 220%, 100% 100%;
  animation: felt-aurora 22s ease-in-out infinite alternate;
}
@keyframes felt-aurora {
  0%   { background-position: 0% 0%,     100% 100%, 50% 0%,    50% 50%; }
  33%  { background-position: 100% 30%,  0% 80%,    100% 60%,  50% 50%; }
  66%  { background-position: 60% 100%,  40% 0%,    0% 100%,   50% 50%; }
  100% { background-position: 0% 60%,    100% 20%,  70% 30%,   50% 50%; }
}

/* ── The felts that play along ─────────────────────────────────────
   app.js tells the felt three things: --heat, the pot as a share of
   sixty big blinds; data-turn="hero" while it is your move; and a
   data-mood="win" flash when a pot comes to you. Chips landing drop a
   .felt-ripple where they land. Every felt may read these; the two
   below are built around them. */
.felt { --heat: 0; --turn: 0; --felt-ripple: rgba(255, 255, 255, .22); }
.felt[data-turn="hero"] { --turn: 1; }
html[data-felt="classic"] .felt { --felt-ripple: rgba(0, 0, 0, .10); }
.felt-ripple {
  position: absolute; width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--felt-ripple);
  transform: translate(-50%, -50%) scale(.6);
  pointer-events: none; z-index: 0;
  animation: felt-ripple 1s cubic-bezier(.2, .7, .3, 1) forwards;
}
@keyframes felt-ripple {
  0%   { opacity: .9; transform: translate(-50%, -50%) scale(.6); }
  100% { opacity: 0;  transform: translate(-50%, -50%) scale(6.5); border-width: 1px; }
}
/* the win: gold along the inside of the rail, then gone */
.felt[data-mood="win"] .felt-grain { animation: felt-win 1.8s ease-out; }
@keyframes felt-win {
  0%   { box-shadow: inset 0 0 0 0 rgba(255, 210, 110, 0); }
  18%  { box-shadow: inset 0 0 70px 8px rgba(255, 210, 110, .45); }
  100% { box-shadow: inset 0 0 0 0 rgba(255, 210, 110, 0); }
}
@media (prefers-reduced-motion: reduce) { .felt-ripple { display: none; } .felt[data-mood="win"] .felt-grain { animation: none; } }

html[data-felt="inferno"] .felt, .swatch[data-felt="inferno"], .recap-felt[data-felt="inferno"],
html[data-felt="pulse"] .felt,   .swatch[data-felt="pulse"], .recap-felt[data-felt="pulse"],
html[data-felt="velvet"] .felt,  .swatch[data-felt="velvet"], .recap-felt[data-felt="velvet"],
html[data-felt="sunset"] .felt,  .swatch[data-felt="sunset"], .recap-felt[data-felt="sunset"],
html[data-felt="synth"] .felt,   .swatch[data-felt="synth"], .recap-felt[data-felt="synth"] {
  --felt-line: rgba(255, 255, 255, .18);
  --felt-slot: rgba(0, 0, 0, .2);
  --on-felt-dim: rgba(255, 255, 255, .42);
}

/* Inferno: black cloth over a fire that grows with the pot; the rail
   glows amber while it is your move */
html[data-felt="inferno"] .felt, .swatch[data-felt="inferno"], .recap-felt[data-felt="inferno"] {
  --felt-line: rgba(255, 150, 80, .3);
  --felt-slot: rgba(0, 0, 0, .32);
  --on-felt-dim: rgba(255, 200, 160, .55);
  --felt-ripple: rgba(255, 170, 90, .55);
  background:
    radial-gradient(90% 120% at 50% -10%, #241210 0%, #120806 46%, #070303 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #0a0605,
    inset 0 0 0 calc(var(--rail-w) + 1.5px) rgba(255, 140, 60, calc(.2 + .6 * var(--turn, 0))),
    inset 0 0 calc(26px + 80px * var(--heat, 0)) calc(2px + 10px * var(--heat, 0)) rgba(255, 80, 20, calc(.12 + .5 * var(--heat, 0))),
    var(--lift-lg);
  transition: box-shadow .7s var(--ease);
}
.swatch[data-felt="inferno"], .recap-felt[data-felt="inferno"] { --heat: .7; --turn: 1; }
html[data-felt="inferno"] .felt-grain, .swatch[data-felt="inferno"] .swatch-grain, .recap-felt[data-felt="inferno"] .recap-grain {
  opacity: calc(.4 + .6 * var(--heat, 0));
  background:
    radial-gradient(80% 70% at 50% 112%, rgba(255, 120, 30, .6) 0%, rgba(255, 50, 10, .22) 45%, transparent 74%),
    radial-gradient(45% 40% at 8% 100%, rgba(255, 80, 20, .3) 0%, transparent 70%),
    radial-gradient(45% 40% at 92% 100%, rgba(255, 80, 20, .3) 0%, transparent 70%),
    radial-gradient(60% 50% at 50% 40%, rgba(255, 200, 150, .05) 0%, transparent 70%);
  animation: felt-inferno 4.5s ease-in-out infinite;
  transition: opacity .8s var(--ease);
}
@keyframes felt-inferno {
  0%, 100% { filter: brightness(.85); }
  50%      { filter: brightness(1.2); }
}

/* Pulse: deep indigo; violet light breathes round the rail on your move */
html[data-felt="pulse"] .felt, .swatch[data-felt="pulse"], .recap-felt[data-felt="pulse"] {
  --felt-line: rgba(170, 150, 255, .3);
  --felt-slot: rgba(0, 0, 0, .3);
  --on-felt-dim: rgba(190, 180, 255, .55);
  --felt-ripple: rgba(170, 150, 255, .7);
  background:
    radial-gradient(90% 120% at 50% -10%, #1c1b3a 0%, #12112a 46%, #090816 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #0d0c1a,
    inset 0 0 0 calc(var(--rail-w) + 1.5px) rgba(150, 120, 255, calc(.22 + .6 * var(--turn, 0))),
    inset 0 0 calc(16px + 44px * var(--turn, 0)) 2px rgba(140, 110, 255, calc(.12 + .38 * var(--turn, 0))),
    var(--lift-lg);
  transition: box-shadow .5s var(--ease);
}
.swatch[data-felt="pulse"], .recap-felt[data-felt="pulse"] { --turn: 1; }
html[data-felt="pulse"] .felt-grain, .swatch[data-felt="pulse"] .swatch-grain, .recap-felt[data-felt="pulse"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(60% 50% at 50% 40%, rgba(160, 140, 255, .10) 0%, transparent 70%),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .35) 0%, transparent 62%);
}
html[data-felt="pulse"] .felt[data-turn="hero"] .felt-grain, .swatch[data-felt="pulse"] .swatch-grain, .recap-felt[data-felt="pulse"] .recap-grain {
  box-shadow: inset 0 0 40px 2px rgba(150, 120, 255, .25);
  animation: felt-pulse 1.5s ease-in-out infinite;
}
@keyframes felt-pulse {
  0%, 100% { box-shadow: inset 0 0 30px 0 rgba(150, 120, 255, .12); }
  50%      { box-shadow: inset 0 0 56px 6px rgba(150, 120, 255, .34); }
}

/* Velvet: plum, quilted, a gold rail */
html[data-felt="velvet"] .felt, .swatch[data-felt="velvet"], .recap-felt[data-felt="velvet"] {
  --felt-line: rgba(255, 220, 160, .24);
  --on-felt-dim: rgba(255, 225, 200, .5);
  --quilt: 16px;
  background:
    radial-gradient(90% 120% at 50% -10%, #4c1a3c 0%, #2f0f27 46%, #180814 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #2b1b0b,
    inset 0 0 0 calc(var(--rail-w) + 1.5px) rgba(232, 190, 90, .6),
    inset 0 14px 26px -14px rgba(0, 0, 0, .6),
    var(--lift-lg);
}
.swatch[data-felt="velvet"], .recap-felt[data-felt="velvet"] { --quilt: 7px; }
html[data-felt="velvet"] .felt-grain, .swatch[data-felt="velvet"] .swatch-grain, .recap-felt[data-felt="velvet"] .recap-grain {
  opacity: 1;
  background:
    repeating-linear-gradient(45deg,  rgba(255, 255, 255, .045) 0 1px, transparent 1px var(--quilt)),
    repeating-linear-gradient(-45deg, rgba(255, 255, 255, .045) 0 1px, transparent 1px var(--quilt)),
    radial-gradient(60% 50% at 50% 40%, rgba(255, 220, 240, .08) 0%, transparent 70%),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .35) 0%, transparent 62%);
}

/* Sunset: peach into rose into violet, a low sun on the cloth */
html[data-felt="sunset"] .felt, .swatch[data-felt="sunset"], .recap-felt[data-felt="sunset"] {
  --felt-line: rgba(255, 255, 255, .3);
  --felt-slot: rgba(0, 0, 0, .14);
  --on-felt-dim: rgba(255, 255, 255, .72);
  --felt-ripple: rgba(255, 255, 255, .45);
  background:
    linear-gradient(180deg, #f5a05a 0%, #e2566e 44%, #6b2d8a 80%, #2a1c5a 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #1c1420,
    inset 0 0 0 calc(var(--rail-w) + 1px) rgba(255, 200, 150, .28),
    inset 0 14px 26px -14px rgba(0, 0, 0, .4),
    var(--lift-lg);
}
html[data-felt="sunset"] .felt-grain, .swatch[data-felt="sunset"] .swatch-grain, .recap-felt[data-felt="sunset"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(34% 26% at 50% 20%, rgba(255, 245, 210, .5) 0%, rgba(255, 220, 160, .18) 50%, transparent 72%),
    radial-gradient(120% 90% at 50% 118%, rgba(0, 0, 0, .35) 0%, transparent 62%);
}

/* Synthwave: a magenta sun over a cyan grid that rolls toward you */
html[data-felt="synth"] .felt, .swatch[data-felt="synth"], .recap-felt[data-felt="synth"] {
  --felt-line: rgba(0, 230, 255, .36);
  --felt-slot: rgba(0, 0, 0, .3);
  --on-felt-dim: rgba(255, 180, 240, .62);
  --felt-ripple: rgba(0, 230, 255, .6);
  --cell: 22px;
  --col: 36px;
  background:
    linear-gradient(180deg, #12082b 0%, #2a0f57 44%, #4a1070 60%, #0b0620 100%);
  box-shadow:
    inset 0 0 0 var(--rail-w) #0a0618,
    inset 0 0 0 calc(var(--rail-w) + 1.5px) #ff3fb4,
    inset 0 0 30px 2px rgba(255, 63, 180, .22),
    0 0 34px -8px rgba(255, 63, 180, .45),
    var(--lift-lg);
}
.swatch[data-felt="synth"], .recap-felt[data-felt="synth"] { --cell: 6px; --col: 9px; }
html[data-felt="synth"] .felt-grain, .swatch[data-felt="synth"] .swatch-grain, .recap-felt[data-felt="synth"] .recap-grain {
  opacity: 1;
  background:
    radial-gradient(30% 36% at 50% 36%, rgba(255, 130, 70, .95) 0%, rgba(255, 60, 150, .75) 52%, transparent 58%),
    repeating-linear-gradient(180deg, transparent 0 calc(var(--cell) - 1px), rgba(0, 230, 255, .32) calc(var(--cell) - 1px) var(--cell)),
    repeating-linear-gradient(90deg,  transparent 0 calc(var(--col) - 1px),  rgba(0, 230, 255, .2)  calc(var(--col) - 1px)  var(--col)),
    linear-gradient(180deg, rgba(10, 6, 32, 0) 0%, rgba(10, 6, 32, .55) 100%);
  background-size: 100% 100%, 100% 46%, 100% 46%, 100% 46%;
  background-position: 0 0, 0 100%, 0 100%, 0 100%;
  background-repeat: no-repeat;
  animation: felt-synth 1.4s linear infinite;
}
@keyframes felt-synth {
  from { background-position: 0 0, 0 calc(100% - var(--cell)), 0 100%, 0 100%; }
  to   { background-position: 0 0, 0 100%, 0 100%, 0 100%; }
}

.center {
  position: absolute;
  left: 50%;
  /* the drop is measured: the pot must not sit under the seat above it */
  top: calc(var(--center-y, 49%) + var(--center-drop, 0px));
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(5px, var(--felt-h, 420px) * .036, 16px);
  width: 100%;
}

/* An emptier table draws a smaller board. The seats at the sides have
   wide plates on them - a name, a stack and whatever they just did - and
   with only two of them the board is the thing that can afford to give
   the room back. */
.felt[data-seats="2"] { --card-w: clamp(26px, var(--felt-h, 420px) * .140, 58px); }
.felt[data-seats="3"] { --card-w: clamp(24px, var(--felt-h, 420px) * .125, 52px); }

/* ── Chips ─────────────────────────────────────────────────────── */

.stack {
  display: inline-flex;
  align-items: center;
  margin-right: 6px;
}

.chip {
  position: relative;
  display: block;
  width: 14px;
  height: 14px;
  border-radius: var(--r-pill);
  background: var(--ink-4);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .18), 0 1px 1.5px rgba(0, 0, 0, .18);
}
.chip + .chip { margin-left: -5px; }
.chip[data-tone="v500"] { background: var(--chip-500); }
.chip[data-tone="v100"] { background: var(--chip-100); }
.chip[data-tone="v25"]  { background: var(--chip-25); }
.chip[data-tone="v5"]   { background: var(--chip-5); }
.chip[data-tone="v1"]   { background: var(--chip-1); }

/* six edge notches and one inner ring, enough to read as a chip at 14px */
.chip::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: repeating-conic-gradient(#fff 0deg 17deg, transparent 17deg 60deg);
  mask: radial-gradient(circle, transparent 62%, #000 64%);
  -webkit-mask: radial-gradient(circle, transparent 62%, #000 64%);
  opacity: .62;
}
.chip::after {
  content: "";
  position: absolute;
  inset: 22%;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, .45);
}

/* ── Pot ───────────────────────────────────────────────────────── */

.pot {
  display: inline-flex;
  align-items: center;
  gap: clamp(5px, var(--felt-h, 420px) * .024, 10px);
  height: clamp(26px, var(--felt-h, 420px) * .094, 40px);
  padding: 0 clamp(10px, var(--felt-h, 420px) * .04, 17px) 0 clamp(8px, var(--felt-h, 420px) * .031, 13px);
  border-radius: var(--r-pill);
  background: var(--bg);
  box-shadow: var(--ring-strong), var(--lift);
  transition: opacity var(--med) var(--ease), transform var(--med) var(--spring);
}
.pot[data-empty="true"] { opacity: 0; transform: scale(.9); }

.pot-chips { display: inline-flex; align-items: center; }
.pot-chips:empty { display: none; }
.pot-chips .chip { width: clamp(12px, var(--felt-h, 420px) * .042, 18px); height: clamp(12px, var(--felt-h, 420px) * .042, 18px); }
.pot-chips .chip + .chip { margin-left: -7px; }

.pot-label {
  font-family: var(--font-text);
  font-size: clamp(8px, var(--felt-h, 420px) * .024, 10px);
  font-weight: 700;
  letter-spacing: 1.3px;
  /* the tracking adds a trailing sliver; take it back so the gap is even */
  margin-right: -1.3px;
  color: var(--ink-3);
}
.pot-value {
  font-family: var(--font-display);
  font-size: clamp(14px, var(--felt-h, 420px) * .047, 20px);
  font-weight: 700;
  letter-spacing: -.4px;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.pot.is-bumped { animation: bump 420ms var(--spring); }

@keyframes bump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.13); }
  100% { transform: scale(1); }
}

/* The chip in the corner that says what is on the felt. It is a label
   first and a control second, so it reads as information rather than as
   another button to be wary of. */
.mark-variant {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 22px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  box-shadow: var(--ring);
  color: var(--ink-2);
  font-family: var(--font-text);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .7px;
  text-transform: uppercase;
  white-space: nowrap;
  min-width: 0;
}
.mark-variant b { font-weight: 700; }
/* the field is the quieter half of the same fact */
.mark-variant i {
  font-style: normal;
  font-weight: 600;
  color: var(--ink-3);
}
.mark-variant i:empty { display: none; }
.mark-variant i::before { content: "·"; margin-right: 6px; }
.mark-variant:empty { display: none; }

/* The name is the first thing to go when the bar runs out of room; what
   you are playing is worth more up there than the word SUITED FOURS. */
@media (max-width: 560px) {
  .mark-variant { height: 20px; padding: 0 8px; font-size: 9px; }
  .mark-variant i { display: none; }
}

/* ── The dealer's trays ────────────────────────────────────────── */

/* Burn and muck, in front of you, labelled.
 *
 * They are two piles in a real game and they were one here, which is why
 * neither made sense. And there is nowhere on the felt to put them: at
 * several window sizes the seats' hole cards come to within 18px of the
 * board, and the corners belong to the seats sitting in them. The space
 * either side of your own hand, though, is 200px of nothing on a desktop
 * and 60 on a phone - the only room on the table that is reliably empty.
 * One tray each side, matched widths, so your hand stays centred. */
/* Their own line, in front of your hand. Set either side of it they stole
   the width your cards needed, and a five-card game stopped laying out in
   a row - which is the one thing that row has to do. */
.dealt-row {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(16px, 4.5vw, 34px);
}

/* Label beside the pile, not under it. Stacked, the row was 60px of dock
   that the felt had to give up, and on a short window that is the
   difference between a table that lays out and one that does not. */
.dealt-away {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}
.dealt-away .pile { width: clamp(19px, 4.2vw, 28px); }
.pile {
  position: relative;
  width: 100%;
  aspect-ratio: 5 / 7;
  border-radius: 5px;
  box-shadow: inset 0 0 0 1px var(--felt-line);
}
.pile .card {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: .72;
  filter: saturate(.4) drop-shadow(0 1px 2px rgba(15, 18, 24, .16));
  transform: translate(var(--mx, 0), var(--my, 0)) rotate(var(--mr, 0deg));
}
.pile.is-bumped { animation: pile-bump 300ms var(--spring); }
@keyframes pile-bump {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.13); }
  100% { transform: scale(1); }
}
/* A card on its way to a pile.
 *
 * Somebody throwing a hand in does not slide it along the felt. It leaves
 * the hand fast, climbs, tumbles, and lands short of where it was aimed,
 * so the three parts are separated and given their own easing: one layer
 * carries it across, one gives it the arc, and the card itself spins. A
 * single transform could not do that - the horizontal travel has to hold
 * its speed while the vertical goes up slow and comes down quick, which is
 * what makes it read as thrown rather than sent.
 *
 * The card is built with dealt:false on purpose. `.card.is-dealing` is
 * declared later in this sheet, wins the cascade over the spin, and lands
 * the card in from the deck corner instead - which is what "the cards
 * spawn in the top right" was. */
.toss {
  position: absolute;
  z-index: 7;
  pointer-events: none;
  animation: toss-x var(--dur, 460ms) cubic-bezier(.16, .62, .34, 1) var(--delay, 0ms) both;
}
.toss-lift {
  width: 100%;
  height: 100%;
  animation: toss-lift var(--dur, 460ms) linear var(--delay, 0ms) both;
}
.toss .card {
  width: 100%;
  height: 100%;
  animation: toss-spin var(--dur, 460ms) cubic-bezier(.12, .66, .3, 1) var(--delay, 0ms) both;
}

@keyframes toss-x {
  to { transform: translateX(var(--tx, 0px)); }
}
/* up slowly, down quickly: the two halves of a throw, and the reason the
   arc reads as weight rather than as a curve somebody drew */
@keyframes toss-lift {
  0% {
    transform: translateY(0);
    filter: drop-shadow(0 2px 3px rgba(15, 18, 24, .20));
    animation-timing-function: cubic-bezier(.22, .62, .4, 1);
  }
  40% {
    transform: translateY(calc(var(--lift, 30px) * -1));
    /* the shadow is what says the card is off the felt rather than sliding
       along it, and on a table this pale it is also the only reason you
       can see a white card back crossing a white felt at all */
    filter: drop-shadow(0 18px 15px rgba(15, 18, 24, .22));
    animation-timing-function: cubic-bezier(.55, .06, .78, .54);
  }
  100% {
    transform: translateY(var(--ty, 0px));
    filter: drop-shadow(0 3px 5px rgba(15, 18, 24, .18));
  }
}
@keyframes toss-spin {
  from { transform: rotate(var(--r0, 0deg)) scale(1); }
  to   { transform: rotate(var(--spin, 300deg)) scale(var(--land, .7)); }
}

/* and it does not stop dead when it gets there */
.pile .card { animation: muck-land 260ms cubic-bezier(.2, .8, .3, 1) both; }
@keyframes muck-land {
  from {
    transform:
      translate(calc(var(--mx, 0px) + var(--skid, 8px)), calc(var(--my, 0px) - 3px))
      rotate(calc(var(--mr, 0deg) - 14deg));
    opacity: .9;
  }
  to {
    transform: translate(var(--mx, 0px), var(--my, 0px)) rotate(var(--mr, 0deg));
    opacity: .72;
  }
}

.pile-label {
  font-family: var(--font-text);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ink-4);
}

/* off until it earns its place */
html[data-muck="off"] .dealt-row { display: none; }

/* And gone on a short window whatever the setting says. The row costs the
   felt about thirty pixels of height, and on a short one that is the
   difference between six seats laying out and six seats sitting on each
   other. The table comes first.
   Keyed to the window and not to the felt's own size class: a 1280x860
   desktop has a felt under 380px tall - "compact" - with a whole screen
   around it, and hiding the piles there hid them from most desktops. */
@media (max-height: 700px) {
  .dealt-row { display: none; }
}

/* ── Community cards ───────────────────────────────────────────── */

/* stud deals no board: the middle is the pot, the street and the rules */
.center[data-family="stud"] .community,
.center[data-family="draw"] .community { display: none; }
.center[data-family="stud"],
.center[data-family="draw"] { gap: 10px; }

.community {
  display: flex;
  gap: clamp(3px, var(--felt-h, 420px) * .021, 9px);
  align-items: center;
  justify-content: center;
}

.slot {
  width: var(--card-w);
  aspect-ratio: 5 / 7;
  border-radius: calc(var(--card-w) * var(--card-r));
  background: var(--felt-slot);
  box-shadow: inset 0 0 0 1px var(--felt-line);
}

.street-tag {
  font-family: var(--font-text);
  font-size: clamp(8px, var(--felt-h, 420px) * .026, 11px);
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--on-felt-dim);
  transition: opacity var(--med) var(--ease);
}

/* ── Cards ─────────────────────────────────────────────────────── */

.card {
  --w: var(--card-w);
  position: relative;
  width: var(--w);
  aspect-ratio: 5 / 7;
  perspective: 900px;
  flex: none;
}

.card-inner {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: transform 520ms var(--ease);
  border-radius: calc(var(--w) * var(--card-r));
}
.card.is-faceup .card-inner { transform: rotateY(180deg); }

.card-face {
  position: absolute;
  inset: 0;
  border-radius: calc(var(--w) * var(--card-r));
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
}

.card-back {
  display: grid;
  place-items: center;
  background: var(--card-back-b);
  box-shadow: var(--ring), var(--lift);
}
.card-back::before {
  content: "";
  position: absolute;
  inset: 9%;
  border-radius: calc(var(--w) * .11);
  background: var(--card-back-a);
}
.back-mark {
  position: relative;
  width: calc(var(--w) * .3);
  height: calc(var(--w) * .3);
  fill: var(--card-back-b);
  opacity: .9;
}

.card-front {
  transform: rotateY(180deg);
  background: var(--card-bg);
  color: var(--card-ink);
  box-shadow: var(--ring-strong), var(--lift);
  display: grid;
  place-items: center;
  font-family: var(--font-display);
}

.card-front[data-color="red"] .card-suit,
.card-front[data-color="red"] .card-pip { color: var(--card-red); }
.card-suit { fill: currentColor; }

.card-rank {
  font-size: calc(var(--w) * .46);
  font-variant-numeric: lining-nums;
  font-weight: 700;
  letter-spacing: -.04em;
  line-height: 1;
  transform: translateY(calc(var(--w) * -.055));
}
.card-rank[data-long="true"] { font-size: calc(var(--w) * .355); letter-spacing: -.07em; }

.card-suit {
  position: absolute;
  bottom: calc(var(--w) * .075);
  right: calc(var(--w) * .095);
  width: calc(var(--w) * .245);
  height: calc(var(--w) * .245);
}

.card-pip {
  position: absolute;
  top: calc(var(--w) * .07);
  left: calc(var(--w) * .1);
  font-size: calc(var(--w) * .185);
  line-height: 1;
  opacity: .5;
  font-family: var(--font-text);
  font-weight: 700;
  letter-spacing: .4px;
}

/* deal-in */
.card.is-dealing { animation: deal-in 520ms var(--ease) backwards; }
@keyframes deal-in {
  from {
    opacity: 0;
    transform: translate(var(--dx, 0px), var(--dy, -180px)) rotate(var(--dr, -14deg)) scale(.82);
  }
  to { opacity: 1; transform: none; }
}

/* pitched away in Crazy Pineapple */
.card.is-pitched {
  animation: pitch 340ms var(--ease) forwards;
  pointer-events: none;
}
@keyframes pitch {
  to { opacity: 0; transform: translateY(-46px) rotate(-16deg) scale(.78); }
}

/* The winning five. They snap up one after another, left to right, and
   stay up until the next deal. The lift is a share of the card's own
   width, so a board card and a seat card both rise by the right amount. */
.card.is-highlight,
.card.is-lowlight {
  animation: pop 200ms var(--spring) both;
  animation-delay: calc(var(--hi, 0) * 60ms);
  z-index: 2;
}
/* No ring around them: standing up off the felt with the rest gone grey
   is the whole signal, and an outline on a card that already overlaps its
   neighbour only makes the overlap louder. The shadow is what sells the
   lift. A split pot lifts the low five a little less than the high, so
   the two hands read apart without another colour on the table. */
.card.is-highlight .card-inner,
.card.is-lowlight .card-inner {
  box-shadow: 0 calc(var(--w) * .14) calc(var(--w) * .34) rgba(0, 0, 0, .3);
}
.card.is-lowlight { --rise: .13; }
.card.is-dimmed {
  opacity: .26;
  filter: grayscale(1);
  transition: opacity var(--med) var(--ease), filter var(--med) var(--ease);
  transition-delay: 120ms;
}
@keyframes pop {
  0%   { transform: translateY(0) scale(1); }
  62%  { transform: translateY(calc(var(--w) * var(--rise, .24) * -1.4)) scale(1.13); }
  100% { transform: translateY(calc(var(--w) * var(--rise, .24) * -1)) scale(1.08); }
}

/* ── Seats ─────────────────────────────────────────────────────── */
/* Positions are set by app.js, which knows how many are playing and
   what shape the table is. Everything else lives here. */

.seats { position: absolute; inset: 0; }

.seat {
  position: absolute;
  left: var(--x, 50%);
  top: var(--y, 50%);
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(3px, var(--felt-h, 420px) * .019, 8px);
  transition: opacity var(--slow) var(--ease), filter var(--slow) var(--ease);
}

.seat[data-folded="true"] { opacity: .42; filter: saturate(.35); }

.seat-cards {
  display: flex;
  align-items: flex-start;
  --card-w: clamp(17px, var(--felt-h, 420px) * .092, 38px);
  transform-origin: center bottom;
  transition: transform var(--slow) var(--spring);
}
.seat-cards .card { transition: margin-left var(--med) var(--ease); }
.seat-cards .card + .card { margin-left: 4px; }
.seat-cards[data-count="3"] .card + .card { margin-left: calc(var(--card-w) * -.3); }
.seat-cards[data-count="4"] .card + .card,
.seat-cards[data-count="5"] .card + .card { margin-left: calc(var(--card-w) * -.44); }
/* stud: a seat holds up to seven, so the fan closes up and the cards
   come down a size to stay inside the seat */
.seat-cards[data-count="6"] .card + .card,
.seat-cards[data-count="7"] .card + .card { margin-left: calc(var(--card-w) * -.56); }
.felt[data-family="stud"] .seat-cards { --card-w: clamp(20px, var(--felt-h, 420px) * .09, 38px); }
/* Stud: the up cards are the point, so they fan open enough to read the
   corner of each, and the down cards tuck behind them as one back. */
.felt[data-family="stud"] .seat-cards .card { position: relative; }
.felt[data-family="stud"] .seat-cards .card.is-faceup { z-index: 1; }
.felt[data-family="stud"] .seat-cards .card + .card.is-faceup { margin-left: calc(var(--card-w) * -.36); }
.felt[data-family="stud"] .seat-cards .card + .card:not(.is-faceup) { margin-left: calc(var(--card-w) * -.9); }
.felt[data-family="stud"] .seat-cards .card:not(.is-faceup) + .card.is-faceup { margin-left: calc(var(--card-w) * -.2); }
.felt[data-family="stud"] .seat[data-showdown="true"] .seat-cards .card + .card { margin-left: calc(var(--card-w) * -.3); }

/* An empty table has room, so the cards on it are drawn at a size that
   sits properly beside the board instead of looking like a different
   deck. Heads-up they are nearly board-sized; by six-handed they are back
   to something that fits five plates round a felt.

   Only where there is width to spend: a phone rings its seats round a
   portrait felt and has its own sizes, which these would otherwise
   trample straight over. */
@media (min-width: 561px) {
  /* The multiplier is gentle so that a short window gives back the extra
     size rather than running the seat into the pot: it is spare room that
     pays for these, and a short felt has none. */
  #seats[data-count="2"] .seat-cards { --card-w: clamp(22px, var(--felt-h, 420px) * .100, 56px); }
  #seats[data-count="3"] .seat-cards { --card-w: clamp(21px, var(--felt-h, 420px) * .096, 50px); }
  #seats[data-count="4"] .seat-cards { --card-w: clamp(19px, var(--felt-h, 420px) * .092, 40px); }

  /* The seat directly above the pot used to have its cards shrunk so they
     could not touch the pot. Measured, there is 43px of clearance at the
     tightest window this layout is used at and 95px at a roomy one, so the
     shrink was buying nothing and costing the one thing you see every
     hand: one player at the table holding smaller cards than everybody
     else. Every seat is the same size now, and the clearance is the
     layout's job. */
}

.seat[data-showdown="true"] .seat-cards { transform: scale(1.26); }
.seat[data-showdown="true"] .seat-cards .card + .card { margin-left: 3px; }
/* seven shown at once: less of a lift, and the fan stays part-closed */
.seat[data-showdown="true"] .seat-cards[data-count="6"],
.seat[data-showdown="true"] .seat-cards[data-count="7"] { transform: scale(1.12); }
.seat[data-showdown="true"] .seat-cards[data-count="6"] .card + .card,
.seat[data-showdown="true"] .seat-cards[data-count="7"] .card + .card { margin-left: calc(var(--card-w) * -.28); }

.seat-plate {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(5px, var(--felt-h, 420px) * .022, 9px);
  height: clamp(27px, var(--felt-h, 420px) * .105, 44px);
  /* every seat reads the same width, and the action badge always has a
     lane of its own that never reaches the avatar */
  /* narrow enough to stay on a small felt, at every size of felt */
  min-width: clamp(72px, min(var(--felt-h, 420px) * .29, var(--felt-w, 900px) * .30), 122px);
  /* and never so wide, with an action on the end of it, that it reaches
     past the edge of the felt */
  max-width: min(var(--felt-w, 900px) * .23, 190px);
  padding: 0 clamp(8px, var(--felt-h, 420px) * .033, 14px) 0 clamp(3px, var(--felt-h, 420px) * .012, 5px);
  border-radius: var(--r-pill);
  background: var(--bg);
  box-shadow: var(--ring), var(--lift);
  transition: box-shadow var(--med) var(--ease), transform var(--med) var(--spring);
}
.seat-plate.is-compact { min-width: 0; padding: 5px; gap: 0; }

.seat[data-winner="true"] .seat-plate {
  box-shadow: inset 0 0 0 1.5px var(--accent), 0 8px 26px -14px var(--accent-glow);
}
.seat[data-winner="true"] { opacity: 1; filter: none; }

.seat[data-active="true"] .seat-plate {
  transform: scale(1.045);
  box-shadow: inset 0 0 0 1.5px var(--ink), var(--lift-lg);
}
/* Your own plate is a disc the size of the avatar, so an outline on it
   and the clock around it are two rings a hair apart, which is what made
   the timer look askew. The clock says it is your turn on its own. */
.seat[data-active="true"] .seat-plate.is-compact {
  box-shadow: var(--ring), var(--lift);
}
/* Your chip is already the house colour, so the clock around it is drawn
   in ink: a red ring round a red chip is one red blob. */
.seat-plate.is-compact .timer {
  inset: -4px;
  background: conic-gradient(var(--ink) calc(var(--p, 0) * 360deg), transparent 0);
  mask: radial-gradient(circle closest-side, transparent 84%, #000 88%);
  -webkit-mask: radial-gradient(circle closest-side, transparent 84%, #000 88%);
}

.avatar {
  --size: clamp(21px, var(--felt-h, 420px) * .081, 34px);
  display: grid;
  place-items: center;
  width: var(--size);
  height: var(--size);
  border-radius: var(--r-pill);
  background: var(--surface-3);
  color: var(--ink-2);
  font-family: var(--font-display);
  font-size: clamp(9px, var(--felt-h, 420px) * .031, 13px);
  font-weight: 700;
  letter-spacing: -.2px;
  box-shadow: inset 0 0 0 1px var(--line);
}

/* ── A seat's mark ─────────────────────────────────────────────────
   Whoever is in the seat, drawn rather than downloaded: a disc in the
   colour they picked with their mark on it. The regulars keep initials,
   because they have never chosen anything. */

.avatar.is-mark {
  --size: clamp(23px, var(--felt-h, 420px) * .088, 38px);
  background: var(--tone, var(--accent));
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, .18),
    inset 0 1px 0 rgba(255, 255, 255, .22),
    0 1px 2px rgba(0, 0, 0, .18);
}
.avatar.is-mark {
  /* the marks with a drawn line in them follow `color`, not `fill` */
  color: #fff;
}
.avatar.is-mark .ava-mark {
  width: 62%;
  height: 62%;
  fill: #fff;
  opacity: .96;
}

.seat-text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.seat-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-text);
  font-size: clamp(8px, var(--felt-h, 420px) * .026, 11px);
  font-weight: 600;
  letter-spacing: .2px;
  color: var(--ink-3);
}
.seat-stack {
  font-family: var(--font-display);
  font-size: clamp(10px, var(--felt-h, 420px) * .033, 14px);
  font-weight: 700;
  letter-spacing: -.3px;
  font-variant-numeric: tabular-nums;
}

.dealer-btn {
  position: absolute;
  /* Hung off the plate's corner, clear of the clock ring, which sits
     outside the avatar. How far off is a question of how much felt
     there is: on a phone the plates are already against the rail, and a
     badge that hangs a fixed nine pixels past one lands on the next
     seat or off the table entirely. */
  /* Up is always safe, because what is above a plate is that seat's own
     cards. Sideways it hangs off the end of the plate that faces the
     middle of the table - the way a dealer button sits - so it is never
     over the name, the stack or the action on the plate, and never off
     the rail on a phone, where the outside end is flush against it. */
  --post-out: clamp(0px, (var(--felt-h, 420px) - 240px) * .05, 9px);
  /* Sideways it clears the plate entirely where the felt is wide enough
     - so it is never over the action on the plate - and tucks back in
     on a phone, where the seats are stacked and the badge would land on
     the next plate up. Width is the measure, not height: a tablet held
     sideways is short but has the room. */
  --post-side: clamp(0px, (var(--felt-w, 900px) - 400px) * .13, 30px);
  right: calc(var(--post-side) * -1);
  top: calc(var(--post-out) * -1);
  /* over the action pill, which blankets the plate it hangs off */
  z-index: 4;
  display: grid;
  place-items: center;
  width: clamp(15px, var(--felt-h, 420px) * .048, 20px);
  height: clamp(15px, var(--felt-h, 420px) * .048, 20px);
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--bg);
  font-family: var(--font-display);
  font-size: clamp(8px, var(--felt-h, 420px) * .024, 10px);
  font-weight: 700;
  /* a hairline of the table around it, so it never merges into the clock
     ring behind it */
  box-shadow: 0 0 0 2px var(--surface), var(--lift);
  animation: pop 360ms var(--spring);
}

/* Everybody else's seat, in the same corner the button lives in. Three
   letters where the button has one, so the badge is a pill that grows
   rather than a circle that clips, and it is quieter than the button:
   the D is the thing on the table, the rest is only a name for a chair. */
.seat[data-edge="right"] .dealer-btn {
  right: auto;
  left: calc(var(--post-side) * -1);
}

.dealer-btn.is-post {
  width: auto;
  min-width: clamp(15px, var(--felt-h, 420px) * .048, 20px);
  padding: 0 clamp(3px, var(--felt-h, 420px) * .015, 6px);
  background: var(--surface-2);
  color: var(--ink-3);
  font-family: var(--font-text);
  font-size: clamp(7px, var(--felt-h, 420px) * .023, 9px);
  letter-spacing: .4px;
  box-shadow: 0 0 0 2px var(--surface), var(--ring);
}

/* The avatar and the clock around it, in one box. The ring used to be
   placed by its own offsets from the corner of the plate, which put it on
   the avatar's own edge rather than around it - two circles a hair apart,
   which reads as a mistake however carefully the numbers line up. */
.seat-face {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
}

.timer {
  position: absolute;
  /* A hair outside the face, so the ring surrounds it rather than sitting
     on its rim - but only a hair. At five pixels the ring was wider than
     the plate holding it and broke the line of the seat top and bottom,
     which read as the clock coming loose from its box. */
  inset: clamp(-3px, var(--felt-h, 420px) * -.006, -1.5px);
  border-radius: var(--r-pill);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--med) var(--ease);
  background: conic-gradient(var(--accent) calc(var(--p, 0) * 360deg), transparent 0);
  /* closest-side, or the gradient measures out to the corner and the ring
     lands outside the box entirely - which is why it went missing */
  mask: radial-gradient(circle closest-side, transparent 74%, #000 78%);
  -webkit-mask: radial-gradient(circle closest-side, transparent 74%, #000 78%);
}
.seat[data-active="true"] .timer { opacity: 1; }

/* The action a player just took takes their whole plate for as long as it
   is up, then hands it back. Sharing the plate does not work: a name, a
   four-figure stack and CALL 1,250 do not fit across a seat that has to
   stay narrow enough for six of them to ring a phone, so the stack was
   being squeezed to a five-pixel sliver of itself - the one number you
   most want to read. Covering it is honest, and it reads from further
   away. */
.seat-say {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  padding: 0 clamp(6px, var(--felt-h, 420px) * .024, 10px);
  border-radius: inherit;
  background: var(--ink);
  color: var(--bg);
  font-family: var(--font-text);
  font-size: clamp(8px, var(--felt-h, 420px) * .026, 11px);
  font-weight: 700;
  letter-spacing: .7px;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  animation: say-in 260ms var(--spring) both;
}
.seat-say.is-leaving { animation: say-out 200ms var(--ease) both; }
/* Your own plate is a disc the size of your avatar - there is no room for
   a word on it, and you have the controls and your own chips in front of
   you to tell you what you just did. */
.seat-plate.is-compact .seat-say { display: none; }
.seat-say[data-kind="fold"] { background: var(--surface-4); color: var(--ink-2); }
.seat-say[data-kind="raise"],
.seat-say[data-kind="bet"],
.seat-say[data-kind="allin"] { background: var(--accent); color: var(--accent-ink); }

@keyframes say-in {
  from { opacity: 0; transform: scale(.88); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes say-out {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(.94); }
}

/* what a player has pushed out this street */
.seat-bet {
  position: absolute;
  left: 50%;
  transform: translate(-50%, 0);
  display: inline-flex;
  align-items: center;
  height: clamp(17px, var(--felt-h, 420px) * .062, 26px);
  padding: 0 clamp(6px, var(--felt-h, 420px) * .026, 11px) 0 clamp(5px, var(--felt-h, 420px) * .019, 8px);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-family: var(--font-display);
  font-size: clamp(9px, var(--felt-h, 420px) * .029, 12px);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
  white-space: nowrap;
  animation: chip-in 300ms var(--spring);
  transition: opacity var(--slow) var(--ease);
}
.seat-bet .chip {
  width: clamp(9px, var(--felt-h, 420px) * .031, 13px);
  height: clamp(9px, var(--felt-h, 420px) * .031, 13px);
}
.seat-bet .chip + .chip { margin-left: -4px; }
.seat[data-side="top"] .seat-bet { top: calc(100% + clamp(3px, var(--felt-h, 420px) * .015, 7px)); }
.seat[data-side="bottom"] .seat-bet { bottom: calc(100% + clamp(4px, var(--felt-h, 420px) * .021, 9px)); }

/* The seat at top centre is positioned by its top edge rather than its
   middle, so however tall it is, two hole cards or five, it always
   clears the edge of the felt by the same margin. */
.seat[data-lane="center"] { transform: translate(-50%, 0); }

/* It also shares a lane with the pot, so its chips go out to the side
   instead of down. */
.seat[data-lane="center"] .seat-bet {
  top: 50%;
  left: auto;
  right: calc(100% + 8px);
  bottom: auto;
  transform: translateY(-50%);
  animation-name: chip-in-side;
}

@keyframes chip-in {
  from { opacity: 0; transform: translate(-50%, 6px) scale(.86); }
  to   { opacity: 1; transform: translate(-50%, 0) scale(1); }
}
@keyframes chip-in-side {
  from { opacity: 0; transform: translate(6px, -50%) scale(.86); }
  to   { opacity: 1; transform: translate(0, -50%) scale(1); }
}

/* On a short table the top seat's cards would run into the board if they
   sat above the plate, so they sit beside it instead. Hiding them, which
   is what used to happen here, just looks like a seat that was not dealt
   in, and one player at the table with no cards is worse than a tight
   corner. */
/* The seat at top centre is dealt in like every other seat: cards above
   the plate. It is anchored by its top edge instead of its middle, so
   however many cards it has it clears the rail by the same margin. On a
   short table it is the one seat with the board directly under it, so
   its cards come down a size rather than changing the arrangement. */
.app[data-table="compact"] .seat[data-lane="center"],
.app[data-table="tiny"] .seat[data-lane="center"] {
  gap: clamp(2px, var(--felt-h, 420px) * .01, 5px);
}

/* On a felt this small the street name is the first thing that can go:
   the board says which street it is, and so does the hand log. Six per
   cent of the table back is the difference between a seat that clears the
   pot and one that sits on it. */
.app[data-table="tiny"] .street-tag { display: none; }
/* Same on the small layouts: the centre seat holds what everybody else
   holds. If it ever has to give something back to clear the pot, it gives
   back position, not card size. */

/* ── Deck corner ───────────────────────────────────────────────── */

.deck-anchor {
  position: absolute;
  right: clamp(20px, var(--felt-h, 420px) * .095, 40px);
  top: clamp(16px, var(--felt-h, 420px) * .08, 34px);
  width: clamp(18px, var(--felt-h, 420px) * .071, 30px);
  height: clamp(25px, var(--felt-h, 420px) * .10, 42px);
  pointer-events: none;
}
/* The pile itself is gone: the top corners of the felt belong to the two
   seats that sit in them, and a stack of card backs behind someone's hole
   cards reads as a bug. The anchor stays, invisible, because it is where
   every card flies in from. */
.deck-card { display: none; }

/* ── FX layer (chips travelling to the pot) ───────────────────── */

.fx { position: absolute; inset: 0; pointer-events: none; z-index: 6; }

.fly {
  position: absolute;
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 10px 0 8px;
  border-radius: var(--r-pill);
  background: var(--bg);
  box-shadow: var(--ring), var(--lift);
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
  transform: translate(-50%, -50%);
  transition:
    left 560ms var(--ease),
    top 560ms var(--ease),
    opacity 260ms var(--ease) 320ms,
    transform 560ms var(--ease);
}
.fly .chip { width: 13px; height: 13px; }

/* ── Dock ──────────────────────────────────────────────────────── */

.dock {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

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

.hero-cards {
  display: flex;
  gap: 8px;
  --card-w: 74px;
  --n: 2;
}
.hero-cards[data-count="3"] { --card-w: 66px; }
.hero-cards[data-count="4"] { --card-w: 60px; }
.hero-cards[data-count="5"] { --card-w: 54px; gap: 7px; }
.hero-cards[data-count="6"] { --card-w: 50px; gap: 6px; }
.hero-cards[data-count="7"] { --card-w: 46px; gap: 5px; }

.hero-cards .card {
  --rot: calc((var(--i, 0) - (var(--n) - 1) / 2) * 3deg);
  transform: translate(var(--fx, 0px), var(--fy, 0px)) rotate(var(--rot));
  transition: transform var(--med) var(--spring);
}
.hero-cards:hover .card { transform: translate(var(--fx, 0px), var(--fy, 0px)) rotate(var(--rot)) translateY(-5px); }
/* While you are holding them they follow your hand, with no easing in the
   way, and they show they can be thrown. The offset is --fx/--fy, not
   --dx/--dy: those are each card's deal-in vector, and reusing them put
   every resting hand back on the deck. */
.hero-cards[data-grab="true"] { cursor: grab; touch-action: none; }
.hero-cards[data-grab="true"][data-held="true"] { cursor: grabbing; }
.hero-cards[data-held="true"] .card { transition: none; }
.hero-cards[data-held="true"]:hover .card { transform: translate(var(--fx, 0px), var(--fy, 0px)) rotate(var(--rot)); }

.hero-cards[data-picking="true"] .card { cursor: pointer; }
.hero-cards[data-picking="true"] .card:hover {
  transform: rotate(var(--rot)) translateY(-14px) scale(1.04);
}
.hero-cards[data-picking="true"] .card::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: calc(var(--w) * var(--card-r) + 4px);
  box-shadow: inset 0 0 0 2px transparent;
  transition: box-shadow var(--fast) var(--ease);
}
.hero-cards[data-picking="true"] .card:hover::after {
  box-shadow: inset 0 0 0 2px var(--accent);
}

/* stack and hand reading sit under your cards, centred with them */
.meta-pill {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 34px;
  padding: 0 15px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -.1px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.meta-pill .chip-dot { background: var(--accent); }
.meta-sep { width: 1px; height: 15px; background: var(--line-2); }
.meta-pill[data-read="false"] .meta-sep,
.meta-pill[data-read="false"] .hand-read { display: none; }

.hand-read {
  color: var(--ink-2);
  transition: color var(--med) var(--ease);
}
.meta-pill[data-strong="true"] .hand-read { color: var(--accent); }

/* The NUTS tag, worn only by hands that are the nuts right now. Red
   while the board is still coming, because the title is provisional;
   gold on a finished board, because nothing can take it away. */
.read-tag {
  display: inline-block;
  margin-right: 7px;
  padding: 2.5px 7px 1.5px;
  border-radius: var(--r-pill);
  font-size: 10px;
  font-weight: 800;
  font-style: normal;
  letter-spacing: .8px;
  vertical-align: 1.5px;
  animation: nuts-glow 2.4s var(--ease) infinite;
}
.read-tag[data-kind="nuts"] {
  --tag-glow: var(--gold-glow);
  background: var(--gold);
  color: var(--surface);
}
.read-tag[data-kind="nuts-now"] {
  --tag-glow: var(--accent-glow);
  background: var(--accent);
  color: var(--accent-ink);
}
.meta-pill[data-nuts="nuts"] { box-shadow: var(--ring), 0 0 20px -6px var(--gold-glow); }
.meta-pill[data-nuts="nuts-now"] { box-shadow: var(--ring), 0 0 20px -6px var(--accent-glow); }
@keyframes nuts-glow {
  50% { box-shadow: 0 0 14px -2px var(--tag-glow, var(--gold-glow)); }
}
@media (prefers-reduced-motion: reduce) {
  .read-tag { animation: none; }
}

/* What a name means. Hover the reading, or tap it, and the words get
   their footnote: what Pocket Rockets are, what the tag is claiming,
   how many cards fill the draw. */
#hero-meta { position: relative; }
#hero-meta[data-has-tip="true"] { cursor: help; }
.read-tip {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translate(-50%, 4px);
  width: max-content;
  max-width: 280px;
  padding: 9px 13px;
  border-radius: 12px;
  background: var(--surface-3);
  box-shadow: var(--ring), 0 12px 28px -14px rgba(0, 0, 0, .4);
  font-family: var(--font-text);
  font-size: 12.5px;
  font-weight: 450;
  letter-spacing: 0;
  line-height: 1.5;
  text-align: left;
  color: var(--ink);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--med) var(--ease), transform var(--med) var(--ease),
              visibility 0s var(--med);
  z-index: 40;
}
/* Hover only where there is a mouse. A phone that opened it on hover
   would swallow the tap that was meant to pin it, and then close it on
   the next touch: the footnote flashed and was gone. On a phone it opens
   on a tap and stays until a tap somewhere else. */
#hero-meta[data-tip="open"] .read-tip {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translate(-50%, 0);
  transition: opacity var(--med) var(--ease), transform var(--med) var(--ease);
}
@media (hover: hover) {
  #hero-meta[data-has-tip="true"]:hover .read-tip {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translate(-50%, 0);
    transition: opacity var(--med) var(--ease), transform var(--med) var(--ease);
  }
}

/* the control deck */
.panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 11px;
  width: 100%;
  max-width: var(--panel-w);
  padding: 12px 16px 14px;
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--ring), var(--lift);
}

.status {
  font-family: var(--font-text);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-3);
  letter-spacing: .1px;
  min-height: 20px;
  text-align: center;
  transition: color var(--med) var(--ease);
}
.status[data-tone="win"] { color: var(--accent); font-weight: 600; }

/* ── Controls ──────────────────────────────────────────────────── */

/* Both states share one grid cell: the tray keeps its height whether you
   are acting or waiting, so the dock never jumps. */
.tray {
  display: grid;
  width: 100%;
}
.tray > * {
  grid-area: 1 / 1;
  align-self: center;
  justify-self: center;
}

.controls {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  transition: opacity var(--med) var(--ease), transform var(--med) var(--ease);
}
/* Between turns the controls stay in place, dimmed: an empty control deck
   reads as broken, a dimmed one reads as waiting. */
.controls[data-state="waiting"] {
  opacity: .34;
  filter: saturate(.15);
  pointer-events: none;
}
.controls[data-state="off"] {
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
}
/* while the table is asking whether you meant it */
.controls[data-state="asking"] {
  opacity: 0;
  pointer-events: none;
}

.tray-note {
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--font-text);
  font-size: 13px;
  color: var(--ink-3);
  animation: rise 320ms var(--spring);
}
.tray-note[hidden] { display: none; }
.tray-note .keys { display: inline-flex; gap: 5px; }
.tray-note kbd {
  display: grid;
  place-items: center;
  min-width: 22px;
  height: 22px;
  padding: 0 5px;
  border-radius: 7px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 700;
  color: var(--ink-2);
}

/* one row, always, the three sharing the width as equal pills: a second
   row would move the table above it, and buttons sized to their words
   came out as circles of three sizes */
.actions { display: flex; gap: 10px; flex-wrap: nowrap; justify-content: center; width: 100%; }
.actions .btn { flex: 1 1 0; min-width: 0; max-width: 200px; }
.actions .btn[hidden] { display: none; }

.bet-row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  transition: opacity var(--med) var(--ease);
}
.bet-row[data-hidden="true"] { opacity: 0; pointer-events: none; }
/* a fixed-limit game never shows the slider, so the row is not there at all */
.app[data-fixed="true"] .bet-row { display: none; }

.quick { display: flex; gap: 6px; }
.quick-btn {
  height: 32px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-family: var(--font-text);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .3px;
  color: var(--ink-2);
  transition: transform var(--fast) var(--spring), background-color var(--med) var(--ease), color var(--med) var(--ease);
}
.quick-btn:hover { background: var(--surface-3); color: var(--ink); }
.quick-btn:active { transform: scale(.93); }
.quick-btn:disabled { opacity: .35; pointer-events: none; }

.quick-btn:focus-visible,
.icon-btn:focus-visible,
.slider:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-pill);
}

.slider-wrap {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.slider {
  -webkit-appearance: none;
  appearance: none;
  flex: 1;
  height: 32px;
  background: transparent;
  cursor: grab;
}
.slider:active { cursor: grabbing; }

.slider::-webkit-slider-runnable-track {
  height: 8px;
  border-radius: var(--r-pill);
  background: linear-gradient(to right, var(--ink) var(--fill, 0%), var(--surface-3) var(--fill, 0%));
  box-shadow: inset 0 0 0 1px var(--line);
}
.slider::-moz-range-track {
  height: 8px;
  border-radius: var(--r-pill);
  background: linear-gradient(to right, var(--ink) var(--fill, 0%), var(--surface-3) var(--fill, 0%));
  box-shadow: inset 0 0 0 1px var(--line);
}
.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 24px;
  height: 24px;
  margin-top: -8px;
  border-radius: var(--r-pill);
  background: var(--bg);
  box-shadow: var(--ring-strong), var(--lift);
  transition: transform var(--fast) var(--spring);
}
.slider::-moz-range-thumb {
  width: 24px;
  height: 24px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--bg);
  box-shadow: var(--ring-strong), var(--lift);
}
.slider:hover::-webkit-slider-thumb { transform: scale(1.12); }
.slider:active::-webkit-slider-thumb { transform: scale(.96); }
.slider:disabled { opacity: .4; }

/* The readout is a field. It looks like the number it always was, and
   you can tap it and type - the slider is a fine way to feel out a bet
   and a poor way to make it exactly 375. */
.bet-amount {
  width: 92px;
  height: 34px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  box-shadow: var(--ring);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.2px;
  text-align: center;
  appearance: textfield;
  -moz-appearance: textfield;
  transition: box-shadow var(--fast) var(--ease), background var(--fast) var(--ease);
}
.bet-amount:focus { outline: none; background: var(--bg); box-shadow: var(--ring-strong); }
.bet-amount::-webkit-inner-spin-button, .bet-amount::-webkit-outer-spin-button { display: none; }

.deal-btn { animation: rise 320ms var(--spring); }
.deal-btn[hidden] { display: none; }

@keyframes rise {
  from { opacity: 0; transform: translateY(7px) scale(.92); }
  to   { opacity: 1; transform: none; }
}

/* ── Tablets ───────────────────────────────────────────────────── */

@media (max-width: 860px) {
  .app { --pad-t: 14px; --pad-x: 14px; --pad-b: 18px; }

  .felt {
    width: min(100%, max(var(--panel-w, 640px), var(--stage-h, 320px) * 4 / 3.1));
    aspect-ratio: 4 / 3.1;
    max-height: var(--stage-h, 320px);
    border-radius: var(--r-lg);
    --rail-w: 8px;
  }

  .hero-cards { --card-w: 62px; }
  .hero-cards[data-count="3"] { --card-w: 56px; }
  .hero-cards[data-count="4"] { --card-w: 52px; }
  .hero-cards[data-count="5"] { --card-w: 46px; }
  .hero-cards[data-count="6"] { --card-w: 40px; gap: 6px; }
  .hero-cards[data-count="7"] { --card-w: 37px; gap: 5px; }

  /* seats, cards and pills all size themselves from the felt now, so the
     tablet layout only has to change what the felt itself does */
  .community { gap: clamp(3px, var(--felt-h, 420px) * .017, 6px); }
  .bet-row { flex-direction: column; align-items: stretch; gap: 8px; }
  .quick { justify-content: center; }
  .seat-name { display: none; }
  .btn { min-width: 82px; min-height: 48px; padding: 8px 16px; font-size: 15px; }
}

/* ── Phones ────────────────────────────────────────────────────────
   Six seats will not ring a landscape table this narrow, so the table
   turns portrait and the ring opens up vertically. */

@media (max-width: 560px) {
  /* the mark carries the identity; the wordmark and what you are playing
     cannot both share a row with four controls at this width, and what
     you are playing is the more useful of the two */
  .mark-word { display: none; }
  .icon-btn { width: 40px; height: 40px; }
  .topbar-actions { gap: 6px; }

  .stage { align-items: end; }
  .felt {
    /* On a phone the table takes the whole width, and as much of the
       height left above the cards as it can up to a slightly-taller-than-
       wide shape: a short control deck used to leave the felt floating in
       empty space, its seats no bigger for the room it had. */
    width: 100%;
    aspect-ratio: auto;
    height: min(var(--stage-h, 420px), calc((100vw - 2 * var(--pad-x, 14px)) * 1.3));
    max-height: var(--stage-h, 420px);
    --rail-w: 7px;
    /* The board is the thing you are looking at. Sized against the felt's
       height alone it came out at 55% of a phone's width; against both, it
       takes what the width has - five cards and their gaps with a margin. */
    --card-w: clamp(24px, min(var(--felt-h, 420px) * .112, (var(--felt-w, 390px) - 64px) / 5.6), 54px);
  }

  /* where the middle hangs is worked out from the seat count and the
     felt that is actually there; app.js sets it */
  .center { gap: clamp(6px, var(--felt-h, 420px) * .026, 11px); }
  /* five hole cards in a fan, on a phone, with a neighbour either side */
  .seat-cards { --card-w: clamp(16px, var(--felt-h, 420px) * .066, 30px); }
  /* stud: up to seven a seat, so a size down again, and the showdown
     fan stays closed rather than lifting - a side seat has no room to
     spread seven towards the rim */
  .felt[data-family="stud"] .seat-cards { --card-w: clamp(20px, var(--felt-h, 420px) * .085, 34px); }
  .felt[data-family="stud"] .seat[data-showdown="true"] .seat-cards { --card-w: 24px; transform: none; }
  .felt[data-family="stud"] .seat[data-showdown="true"] .seat-cards .card + .card { margin-left: calc(var(--card-w) * -.42); }
  .seat[data-showdown="true"] .seat-cards[data-count="6"],
  .seat[data-showdown="true"] .seat-cards[data-count="7"] { transform: scale(1); }
  .seat[data-showdown="true"] .seat-cards[data-count="6"] .card + .card,
  .seat[data-showdown="true"] .seat-cards[data-count="7"] .card + .card { margin-left: calc(var(--card-w) * -.5); }
  .community { gap: 5px; }

  .seat-plate { height: 34px; min-width: 92px; padding: 0 10px 0 3px; gap: 6px; }
  .avatar { width: 28px; height: 28px; font-size: 11px; }
  /* the clock is sized and centred by the face it rings, at every width */
  .seat-stack { font-size: 12px; }
  .seat-bet { height: 20px; padding: 0 8px 0 6px; font-size: 10.5px; }
  .seat[data-side="top"] .seat-bet { top: calc(100% + 6px); }
  .seat[data-side="bottom"] .seat-bet { bottom: calc(100% + 6px); }
  .seat-bet .chip { width: 11px; height: 11px; }
  .seat-say { font-size: 9px; letter-spacing: .5px; padding: 0 7px; }

  .pot { height: 34px; padding: 0 12px 0 10px; gap: 8px; }
  .pot-label { font-size: 9px; letter-spacing: 1.1px; }
  .pot-value { font-size: 16px; }
  .pot-chips .chip { width: 15px; height: 15px; }

  /* On a phone the seat directly above the pot has nowhere to put its own
     chip pill: below the plate it meets a full pot coming the other way,
     and out to the side it meets the neighbour. So it does without one.
     The pot counts those chips either way, and the badge on the plate
     still says what the player did. */
  .seat[data-lane="center"] .seat-bet { display: none; }

  .hero { gap: 9px; }
  .hero-cards { --card-w: 58px; }
  .hero-cards[data-count="3"] { --card-w: 52px; }
  .hero-cards[data-count="4"] { --card-w: 46px; }
  .hero-cards[data-count="5"] { --card-w: 40px; gap: 5px; }
  .hero-cards[data-count="6"] { --card-w: 34px; gap: 4px; }
  .hero-cards[data-count="7"] { --card-w: 32px; gap: 4px; }
  .hand-read { font-size: 13px; }
  .panel { gap: 8px; padding: 9px 12px 11px; }
  .bet-row { gap: 5px; }
  .quick-btn { height: 27px; }
  .slider { height: 27px; }
  .btn { min-height: 44px; }
}

/* ── Short phones ──────────────────────────────────────────────────
   On a 640pt screen the dock would eat the table. Everything in the
   dock tightens by a few pixels so the felt keeps a playable size. */

@media (max-width: 560px) and (max-height: 740px) {
  .app { --pad-t: 10px; --pad-x: 12px; --pad-b: 12px; gap: 8px; }
  .dock { gap: 8px; }

  .hero { gap: 7px; }
  .hero-cards { --card-w: 42px; }
  .hero-cards[data-count="3"] { --card-w: 40px; }
  .hero-cards[data-count="4"] { --card-w: 36px; }
  .hero-cards[data-count="5"] { --card-w: 32px; gap: 4px; }
  .hero-cards[data-count="6"] { --card-w: 28px; gap: 3px; }
  .hero-cards[data-count="7"] { --card-w: 26px; gap: 3px; }
  .meta-pill { height: 30px; padding: 0 12px; font-size: 13px; }
  .hand-read { font-size: 12px; }
  .status { min-height: 16px; font-size: 12px; }

  .panel { gap: 9px; padding: 10px 10px 11px; }
  .controls { gap: 8px; }
  .bet-row { gap: 6px; }
  .quick-btn { height: 28px; padding: 0 10px; }
  .slider { height: 28px; }
  .bet-amount { height: 30px; min-width: 64px; }
  .btn { min-height: 46px; min-width: 76px; padding: 6px 14px; font-size: 14px; }
}

/* ── The smallest phones ───────────────────────────────────────────
   Card backs and per-seat chip pills both stand down here: the pot
   already counts every chip in play, the badge still names the action,
   and the chips still fly from the seat when the street closes. */

@media (max-width: 560px) and (max-height: 680px) {
  .seat-cards { display: none; }
  .seat-bet { display: none; }

  /* height is the scarce dimension here, so the table stops being tall
     and spreads sideways instead */
  .felt {
    width: min(100%, var(--stage-h, 300px) * 4 / 3.5);
    aspect-ratio: 4 / 3.5;
    --card-w: clamp(24px, var(--felt-h, 320px) * .13, 44px);
  }
  .hero-cards { --card-w: 38px; }
}

/* ── The rail ──────────────────────────────────────────────────────
   On a wide screen there is room beside the table for the two things
   a player actually asks for out loud: what just happened, and what
   this opponent has been doing all night. */

.rail { display: none; }

@media (min-width: 1160px) {
  html[data-rail="on"] .app {
    max-width: 1500px;
    grid-template-columns: minmax(0, 1fr) clamp(258px, 20vw, 300px);
    grid-template-areas:
      "top   top"
      "stage rail"
      "dock  rail";
    column-gap: 20px;
  }
  html[data-rail="on"] .topbar { grid-area: top; }
  html[data-rail="on"] .stage  { grid-area: stage; }
  html[data-rail="on"] .dock   { grid-area: dock; }
  html[data-rail="on"] .rail   {
    grid-area: rail;
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-height: 0;
    /* level with the top of the table, which is centred in its own row */
    padding-top: var(--rail-top, 0px);
  }
}

.rail-block {
  display: flex;
  flex-direction: column;
  gap: 9px;
  min-height: 0;
  padding: 13px 14px 14px;
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--ring), var(--lift);
}
.rail-log { flex: 1 1 auto; min-height: 120px; }
/* The reads used to be given whatever was left and then clip whatever did
   not fit, so at a full table you could see two opponents out of five and
   nothing said there were more. It gets a share of the rail now, and what
   does not fit scrolls. */
.rail-reads { flex: 0 0 auto; min-height: 92px; max-height: 42%; }
.reads {
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-4) transparent;
  padding-bottom: 5px;
  mask-image: linear-gradient(to bottom, #000 calc(100% - 8px), transparent);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 8px), transparent);
}
.reads::-webkit-scrollbar { width: 5px; }
.reads::-webkit-scrollbar-thumb { background: var(--ink-4); border-radius: 9999px; }

.rail-title {
  margin: 0;
  font-family: var(--font-text);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ── Hand log ─────────────────────────────────────────────────── */

.log {
  margin: 0;
  padding: 0 2px 0 0;
  list-style: none;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-4) transparent;
  font-family: var(--font-text);
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ink-2);
  /* a line scrolling out of the top fades rather than being sliced */
  mask-image: linear-gradient(to bottom, transparent 0, #000 10px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 10px);
}
.log::-webkit-scrollbar { width: 5px; }
.log::-webkit-scrollbar-thumb { background: var(--ink-4); border-radius: 9999px; }

.log-line {
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 2.5px 0;
  animation: log-in 240ms var(--ease);
}
.log-line .log-who { align-self: center; }

/* Who won what, on one row: the name and the amount belong together, and
   the hand they did it with goes underneath rather than squeezing them. */
.log-line[data-kind="win"], .log-line[data-kind="lose"] { flex-wrap: wrap; }
.log-won {
  display: flex;
  align-items: baseline;
  gap: 7px;
  flex: 1 1 100%;
  min-width: 0;
}
.log-won .log-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.log-won .log-amt { flex: none; margin-left: auto; }
@keyframes log-in {
  from { opacity: 0; transform: translateY(3px); }
  to   { opacity: 1; transform: none; }
}

/* who acted, in their seat's initials */
.log-who {
  flex: none;
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: var(--r-pill);
  background: var(--surface-3);
  color: var(--ink-3);
  font-family: var(--font-display);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: -.1px;
}
.log-line[data-who="you"] .log-who { background: var(--ink); color: var(--bg); }

.log-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* the money, always in the same column on the right */
.log-amt {
  flex: none;
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* a hand number, ruled off from the one before */
.log-line[data-kind="hand"] {
  margin-top: 12px;
  padding-top: 0;
  font-family: var(--font-text);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ink-3);
}
.log-line[data-kind="hand"][data-first="true"] { margin-top: 0; }
.log-deck {
  flex: none;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 9px;
  letter-spacing: .3px;
  color: var(--ink-4);
}
/* what everybody had, after the hand */
.log-shown { display: flex; flex-direction: column; gap: 4px; width: 100%; }
.log-had-head { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-4); }
.log-had { display: flex; align-items: center; gap: 6px; min-width: 0; }
.log-had-who { flex: none; min-width: 52px; font-size: 12px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.log-had-cards { display: inline-flex; gap: 3px; }
.log-had-note { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-4); }
.log-had[data-folded="true"] { opacity: .55; }
.log-rule {
  flex: 1 1 auto;
  height: 1px;
  background: var(--line);
}

.log-line[data-kind="street"] {
  gap: 4px;
  margin: 4px 0 2px;
}
.log-street {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-right: 3px;
}
.log-card {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  height: 20px;
  padding: 0 5px;
  border-radius: var(--r-xs);
  background: var(--card-bg);
  box-shadow: var(--ring);
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 700;
  color: var(--card-ink);
}
.log-card[data-color="red"] { color: var(--card-red); }
html[data-deck="four"] .log-card[data-suit="d"] { color: var(--suit-d); }
html[data-deck="four"] .log-card[data-suit="c"] { color: var(--suit-c); }
.log-suit { width: 8px; height: 8px; fill: currentColor; }

.log-line[data-kind="raise"] .log-text,
.log-line[data-kind="bet"] .log-text,
.log-line[data-kind="allin"] .log-text { color: var(--ink); font-weight: 600; }
.log-line[data-kind="allin"] .log-who,
.log-line[data-kind="raise"] .log-who,
.log-line[data-kind="bet"] .log-who { background: var(--accent-soft); color: var(--accent); }
.log-line[data-kind="fold"] { opacity: .62; }
.log-line[data-kind="note"] .log-text { color: var(--ink-3); font-style: italic; }

/* the result of a hand, which is the line you scroll back to find */
.log-line[data-kind="win"],
.log-line[data-kind="lose"] {
  margin: 4px 0 2px;
  padding: 6px 9px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  box-shadow: var(--ring);
}
.log-line[data-kind="win"] { background: var(--accent-soft); box-shadow: none; }
.log-line[data-kind="win"] .log-text,
.log-line[data-kind="win"] .log-amt,
.log-line[data-kind="win"] .log-cup { color: var(--accent); }
.log-line[data-kind="win"] .log-text,
.log-line[data-kind="lose"] .log-text { font-weight: 600; color: var(--ink); }
.log-cup {
  flex: none;
  font-size: 8px;
  color: var(--ink-3);
  transform: translateY(-.5px);
}
.log-with {
  flex-basis: 100%;
  padding-left: 15px;
  font-size: 11px;
  color: var(--ink-3);
}

/* ── Reads ────────────────────────────────────────────────────── */

.reads {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.read {
  display: flex;
  flex-direction: column;
  gap: 0;
  transition: opacity var(--med) var(--ease);
}
.read[data-folded="true"] { opacity: .45; }

.read-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.read-name {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: -.2px;
  color: var(--ink);
}
.read-nums {
  flex: none;
  font-family: var(--font-text);
  font-size: 10.5px;
  font-weight: 500;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.read-nums b { color: var(--ink-2); font-weight: 700; }
.read-nums i { font-style: normal; }
.read-tell {
  font-family: var(--font-text);
  font-size: 11px;
  line-height: 1.35;
  color: var(--ink-3);
  /* two lines, whole words: a tell cut off mid-sentence is worse than
     one that takes a second line */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* ── Session strip ────────────────────────────────────────────── */

.session { display: flex; flex-direction: column; gap: 6px; }
.session-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  padding: 0 12px;
  min-height: 38px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-family: var(--font-text);
  font-size: 10.5px;
  font-weight: 500;
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
}
.session-strip span { display: inline-flex; align-items: baseline; min-width: 0; }
.session-strip b {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin-right: 3px;
}
.session-strip span[data-tone="up"] b { color: var(--accent); }
.session-recap {
  height: 32px; width: 100%; border: 0; border-radius: var(--r-pill);
  background: var(--ink); color: var(--bg);
  font-family: var(--font-text); font-size: 12px; font-weight: 600; cursor: pointer;
}

/* ── Four-colour deck ─────────────────────────────────────────── */

html[data-deck="four"] .card-front[data-suit="d"],
html[data-deck="four"] .log-card[data-suit="d"] { color: var(--suit-d); }
html[data-deck="four"] .card-front[data-suit="c"],
html[data-deck="four"] .log-card[data-suit="c"] { color: var(--suit-c); }
html[data-deck="four"] .card-front[data-suit="d"] .card-suit,
html[data-deck="four"] .card-front[data-suit="d"] .card-pip { color: var(--suit-d); }
html[data-deck="four"] .card-front[data-suit="c"] .card-suit,
html[data-deck="four"] .card-front[data-suit="c"] .card-pip { color: var(--suit-c); }

/* ── The hand reading stands down when you ask it to ──────────── */

html[data-read="off"] .meta-sep,
html[data-read="off"] .hand-read { display: none; }

/* ── Double-tap to check ──────────────────────────────────────── */

.tap-hint {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  height: 44px;
  padding: 0 22px;
  border-radius: var(--r-pill);
  background: var(--bg);
  box-shadow: var(--ring-strong), var(--lift-lg);
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  pointer-events: none;
  z-index: 8;
  animation: tap-hint 620ms var(--ease) forwards;
}
@keyframes tap-hint {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(.82); }
  22%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  70%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -58%) scale(1); }
}


/* ── Short windows ─────────────────────────────────────────────────
   A browser window on a tablet, or a laptop with a shallow viewport,
   leaves the table almost nothing once the dock has taken its share.
   These rules key off height alone, at any width: the deck gets
   tighter, and the felt keeps what it needs to stay a table. */

@media (max-height: 780px) {
  .app { --pad-t: 12px; --pad-x: 16px; --pad-b: 14px; gap: 10px; }
  .dock { gap: 9px; }
  .hero { gap: 7px; }
  .hero-cards { --card-w: 58px; }
  .hero-cards[data-count="3"] { --card-w: 52px; }
  .hero-cards[data-count="4"] { --card-w: 47px; }
  .hero-cards[data-count="5"] { --card-w: 42px; gap: 5px; }
  .hero-cards[data-count="6"] { --card-w: 36px; gap: 4px; }
  .hero-cards[data-count="7"] { --card-w: 34px; gap: 4px; }
  .meta-pill { height: 30px; font-size: 13px; }
  .panel { gap: 9px; padding: 10px 14px 12px; }
  .btn { min-height: 46px; }
}

@media (max-height: 640px) {
  .app { --pad-t: 9px; --pad-x: 12px; --pad-b: 11px; gap: 8px; }
  .topbar .icon-btn { width: 38px; height: 38px; }
  .hero-cards { --card-w: 46px; }
  .hero-cards[data-count="3"] { --card-w: 42px; }
  .hero-cards[data-count="4"] { --card-w: 38px; }
  .hero-cards[data-count="5"] { --card-w: 34px; gap: 4px; }
  .hero-cards[data-count="6"] { --card-w: 29px; gap: 3px; }
  .hero-cards[data-count="7"] { --card-w: 27px; gap: 3px; }
  .meta-pill { height: 27px; padding: 0 12px; font-size: 12px; }
  .status { min-height: 15px; font-size: 12px; }
  .panel { gap: 7px; padding: 8px 12px 10px; }
  .controls { gap: 8px; }
  .quick-btn { height: 27px; padding: 0 10px; }
  .slider { height: 27px; }
  .bet-amount { height: 29px; min-width: 62px; }
  .btn { min-height: 40px; min-width: 76px; padding: 5px 14px; font-size: 14px; }
}

@media (max-height: 520px) {
  .hero-cards { --card-w: 38px; }
  .hero-cards[data-count="3"] { --card-w: 35px; }
  .hero-cards[data-count="4"] { --card-w: 32px; }
  .hero-cards[data-count="5"] { --card-w: 29px; }
  .hero-cards[data-count="6"] { --card-w: 25px; gap: 3px; }
  .hero-cards[data-count="7"] { --card-w: 23px; gap: 3px; }
  .meta-pill { height: 24px; font-size: 11px; }
  .btn { min-height: 36px; font-size: 13px; }
  .status { min-height: 0; }
}

/* Somebody sitting on a winner, letting the table wait for it. */
.seat[data-stalling="true"] .seat-plate {
  animation: stall 900ms var(--ease) infinite;
}
@keyframes stall {
  0%, 100% { box-shadow: var(--ring), var(--lift); }
  50%      { box-shadow: inset 0 0 0 1.5px var(--accent), 0 8px 26px -14px var(--accent-glow); }
}


/* ── The rail, on a screen too narrow to give it a column ──────────
   It slides over the table instead, from the button in the top bar. */

.rail-btn { display: none; }

@media (max-width: 1159px) {
  .rail-btn { display: grid; }

  .rail {
    position: fixed;
    right: 0;
    top: 0;
    bottom: 0;
    z-index: 30;
    width: min(330px, 88vw);
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 56px 14px 14px;
    background: color-mix(in srgb, var(--bg) 86%, transparent);
    backdrop-filter: blur(16px) saturate(1.1);
    -webkit-backdrop-filter: blur(16px) saturate(1.1);
    box-shadow: -1px 0 0 0 var(--line), var(--lift-lg);
    transform: translateX(101%);
    transition: transform var(--slow) var(--ease);
  }
  html[data-rail-open="true"] .rail { transform: none; }
  .rail-log { max-height: 52vh; }
}

/* the button says which way it will go */
html[data-rail-open="true"] .rail-btn {
  background: var(--ink);
  color: var(--bg);
}

html[data-rail="off"] .rail,
html[data-rail="off"] .rail-btn { display: none; }

/* the log is the one place worth being able to select and copy */
.log, .reads { user-select: text; -webkit-user-select: text; }

/* ── Callouts ──────────────────────────────────────────────────────
   The moments worth a beat of their own: the pot you just won, and a
   stack going in. They rise off the felt and are gone. */

.callout {
  position: absolute;
  z-index: 9;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 15px;
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--bg);
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.2px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  box-shadow: var(--lift-lg);
  transform: translate(-50%, -50%);
  pointer-events: none;
  animation: callout 1500ms var(--ease) forwards;
}
.callout[data-kind="win"] { background: var(--accent); color: var(--accent-ink); }
.callout[data-kind="allin"] {
  font-size: 17px;
  letter-spacing: .6px;
  text-transform: uppercase;
}

@keyframes callout {
  0%   { opacity: 0; transform: translate(-50%, -30%) scale(.7); }
  16%  { opacity: 1; transform: translate(-50%, -50%) scale(1.06); }
  26%  { transform: translate(-50%, -50%) scale(1); }
  74%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -110%) scale(.96); }
}

/* ── Rail tabs ─────────────────────────────────────────────────── */

.rail-tabs {
  display: flex;
  align-items: center;
  gap: 4px;
}

.rail-tab {
  position: relative;
  padding: 3px 9px;
  border-radius: var(--r-pill);
  font-family: var(--font-text);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ink-3);
  transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.rail-tab[data-on="true"] { background: var(--surface-3); color: var(--ink); }
.rail-tab[hidden] { display: none; }

/* how many lines you have not read, on the tab that holds them */
.rail-tab[data-tab="chat"]:not([data-unread="0"])::after {
  content: attr(data-unread);
  margin-left: 5px;
  display: inline-grid;
  place-items: center;
  min-width: 15px;
  height: 15px;
  padding: 0 4px;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 9px;
  letter-spacing: 0;
}

/* ── Chat ──────────────────────────────────────────────────────── */

.chat {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1 1 auto;
  min-height: 0;
}
.chat[hidden] { display: none; }

.chat-lines {
  flex: 1 1 auto;
  margin: 0;
  padding: 0 2px 0 0;
  list-style: none;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-family: var(--font-text);
  font-size: 12.5px;
  line-height: 1.4;
}

.chat-line {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 9px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  box-shadow: var(--ring);
  animation: log-in 240ms var(--ease);
  overflow-wrap: anywhere;
}
.chat-line[data-who="you"] { background: var(--accent-soft); box-shadow: none; }

.chat-name {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .3px;
  color: var(--ink-3);
}
.chat-line[data-who="you"] .chat-name { color: var(--accent); }
.chat-text { color: var(--ink); }

.chat-quick {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.chat-quick-btn {
  height: 24px;
  padding: 0 9px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-family: var(--font-text);
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-2);
  white-space: nowrap;
  transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.chat-quick-btn:hover { background: var(--surface-3); color: var(--ink); }
.chat-quick-btn:active { transform: scale(.95); }

.chat-form { display: flex; gap: 6px; }
.chat-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 34px;
  font-size: 13px;
}
.chat-send { flex: none; height: 34px; }

/* ── What somebody just said, beside their seat ────────────────── */

.seat-bubble {
  position: absolute;
  z-index: 7;
  width: max-content;
  max-width: min(220px, 40vw);
  padding: 6px 11px;
  border-radius: var(--r-md);
  background: var(--ink);
  color: var(--bg);
  font-family: var(--font-text);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.35;
  text-align: center;
  box-shadow: var(--lift-lg);
  pointer-events: none;
  overflow-wrap: anywhere;
  animation: bubble-in 260ms var(--spring) backwards, bubble-out 300ms var(--ease) 4.3s forwards;
}
.seat-bubble[data-side="above"] { transform: translate(-50%, -100%); }
.seat-bubble[data-side="below"] { transform: translate(-50%, 0); }

@keyframes bubble-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes bubble-out {
  to { opacity: 0; }
}

/* ── Blinds, in grey above the chips ───────────────────────────── */

.app[data-blinds="true"] .seat-stack[data-bb],
.app[data-blinds="true"] .pot-value[data-bb],
.app[data-blinds="true"] #hero-stack-value[data-bb] {
  position: relative;
  padding-top: 8px;
  line-height: 1;
}
.app[data-blinds="true"] .seat-stack[data-bb]::before,
.app[data-blinds="true"] .pot-value[data-bb]::before,
.app[data-blinds="true"] #hero-stack-value[data-bb]::before {
  content: attr(data-bb);
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  font-family: var(--font-text);
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: .2px;
  line-height: 1;
  color: var(--ink-3);
  text-align: center;
  white-space: nowrap;
}

/* ── Out of the hand: skip the bots' business ──────────────────── */

/* Out of the hand, the deck is put away and the tray says so, the same
   way sitting out does; the pane shares the tray's cell so nothing moves. */
.out-pane {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: 100%;
  text-align: center;
}
.app[data-out="true"] .out-pane { display: flex; }
.app[data-out="true"] .controls { visibility: hidden; opacity: 0; pointer-events: none; }

/* the draw shares the tray with the deck, the same way sitting out does */
.app[data-drawing="true"] .draw-pane { display: flex; }
.app[data-drawing="true"] .controls { visibility: hidden; opacity: 0; pointer-events: none; }
.app[data-drawing="true"] .out-pane:not(.draw-pane) { display: none; }
/* a card on its way back to the dealer: face down, tipped, and out of the way */
.hero-cards[data-picking="many"] .card { cursor: pointer; }
.card[data-tossing="true"] {
  transform: translateY(calc(var(--w) * .18)) scale(.9) rotate(-4deg);
  opacity: .5;
  filter: grayscale(1);
}
.card[data-tossing="true"] .card-inner { box-shadow: none; }

/* ── The line to the table ─────────────────────────────────────── */

.link {
  position: fixed;
  top: max(10px, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  padding: 6px 13px;
  border-radius: 999px;
  font-family: var(--font-text);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .1px;
  background: var(--ink);
  color: var(--ink-inv);
  box-shadow: var(--lift);
  pointer-events: none;
  animation: link-in .25s var(--ease);
}
.link[data-state="on"] { display: none; }
.link[data-state="off"] { background: var(--accent); color: var(--accent-ink); }
@keyframes link-in { from { opacity: 0; transform: translate(-50%, -6px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* ── Stepping away ─────────────────────────────────────────────── */

.away-btn {
  align-self: center;
  height: 28px;
  padding: 0 14px;
  color: var(--ink-3);
}
.away-btn[hidden] { display: none; }
.away-btn[data-on="true"] {
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: none;
}

/* a seat nobody is sitting in at the moment */
/* A seat that is not playing dims as a whole - cards, plate and all -
   and the word for why takes the place of the stack, rather than being
   stamped over it. */
.seat[data-away="true"] { opacity: .55; }
.seat[data-away="true"] .seat-stack { position: relative; color: transparent; }
.seat[data-away="true"] .seat-stack::after {
  content: attr(data-why);
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--font-text);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}

/* While you are out the deck is put away and one thing is on the tray:
   the way back in. The pane shares the tray's cell with the controls, so
   the dock, and the felt above it, keep their size. */
.app[data-away="true"] .controls { visibility: hidden; opacity: 0; pointer-events: none; }
.app[data-away="true"] #btn-away { visibility: hidden; }
.app[data-away="true"] .status { visibility: hidden; }
.away-pane {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: 100%;
  text-align: center;
}
.app[data-away="true"] .away-pane { display: flex; }
.away-title { font-family: var(--font-display); font-size: 18px; font-weight: 700; color: var(--ink); letter-spacing: -.2px; }
.away-note { font-family: var(--font-text); font-size: 12.5px; color: var(--ink-3); }
.away-back { width: min(100%, 300px); min-height: 50px; margin-top: 10px; font-size: 16px; }


/* ── Pot odds ──────────────────────────────────────────────────────
   For anyone who would rather be shown the price than work it out with
   a clock running. Off unless asked for. */

.odds {
  margin: 0 0 2px;
  font-family: var(--font-text);
  font-size: 11.5px;
  line-height: 1.4;
  text-align: center;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.odds b { color: var(--ink-2); font-weight: 700; }

@media (max-width: 560px) {
  .odds { font-size: 10.5px; }
}


/* ── The deck, sealed and checked ──────────────────────────────────
   A fingerprint before the deal and the seed after it, so the hand can
   be taken apart by anyone who wants to. */

.log-line[data-kind="deal"], .log-line[data-kind="sealed"],
.log-line[data-kind="broken"] { opacity: .75; }
.log-line[data-kind="sealed"] { color: var(--ink-2); opacity: 1; }
.log-line[data-kind="broken"] { color: var(--card-red); opacity: 1; }

.log-seal {
  flex: none;
  font-family: var(--font-text);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.log-line[data-kind="sealed"] .log-seal { color: var(--accent); font-size: 12px; }
.log-line[data-kind="broken"] .log-seal { color: var(--card-red); font-size: 12px; }

.log-hash {
  flex: none;
  margin-left: auto;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 9.5px;
  letter-spacing: -.2px;
  color: var(--ink-4);
}


/* ── Putting it all in ─────────────────────────────────────────────
   The one decision that ends the hand for you either way, so it takes
   the tray rather than quietly relabelling a button. */

.shove {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  width: 100%;
  padding: 14px 16px;
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: inset 0 0 0 1.5px var(--accent), var(--lift-lg);
  animation: shove-in 220ms var(--spring);
}
.shove-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.shove-title {
  font-family: var(--font-display);
  font-size: clamp(17px, 2.4vw, 22px);
  font-weight: 700;
  letter-spacing: -.3px;
  color: var(--ink);
}
.shove-title b { color: var(--accent); font-weight: 700; }
.shove-note {
  font-family: var(--font-text);
  font-size: 12.5px;
  color: var(--ink-3);
}
.shove-actions { display: flex; gap: 10px; flex: none; }
.shove-actions .btn { min-width: 104px; min-height: 52px; }

@keyframes shove-in {
  from { opacity: 0; transform: translateY(6px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 560px) {
  .shove { flex-direction: column; align-items: stretch; gap: 12px; padding: 12px; }
  .shove-actions .btn { flex: 1; min-width: 0; }
  .shove-title { font-size: 18px; }
}


/* ── A phone on its side ───────────────────────────────────────── */

/* Stacked, a landscape phone gave the felt whatever was left under the
   top bar and above the controls: seventy pixels on an iPhone, forty on a
   small one. Sideways there is width to spare and no height at all, so
   the table takes the left and the full height, and your hand and the
   controls stand in a column on the right. */
@media (orientation: landscape) and (max-height: 500px) and (min-width: 561px) {
  .app {
    padding: 8px 12px 10px;
    gap: 8px 12px;
    grid-template-columns: minmax(0, 1fr) clamp(250px, 36vw, 340px);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "top top" "stage dock";
  }
  .topbar { grid-area: top; }
  .stage { grid-area: stage; }
  /* the control tray takes its column; it shares a name (.panel) and a
     width variable with the felt's floor, so both are said here outright */
  .panel { width: 100%; max-width: 100%; min-width: 0; padding: 10px 12px 12px; gap: 8px; }
  .dock {
    grid-area: dock;
    justify-content: center;
    gap: 8px;
    min-width: 0;
    min-height: 0;
    /* if a phone is shorter than the hand and the controls, the column
       scrolls; the page never does */
    overflow-y: auto;
  }
  .mark-word { display: none; }
  .icon-btn { width: 36px; height: 36px; }
  .topbar-actions { gap: 6px; }
  .rail, .dealt-row { display: none !important; }

  .felt {
    /* no control deck under it now, so no width floor to honour: the
       table is as wide as its height allows, capped by the column */
    width: min(100%, var(--stage-h, 320px) * 15 / 9.2);
    --rail-w: 7px;
    /* a short, wide felt with seats down both sides: the board is sized
       against the width so it stays between them */
    --card-w: clamp(20px, var(--felt-w, 600px) * .080, 42px);
  }
  .seat-cards { --card-w: clamp(15px, var(--felt-h, 320px) * .068, 28px); }

  .hero-cards { --card-w: 52px; }
  .hero-cards[data-count="3"] { --card-w: 46px; }
  .hero-cards[data-count="4"] { --card-w: 40px; }
  .hero-cards[data-count="5"] { --card-w: 36px; gap: 5px; }
  .hero-cards[data-count="6"] { --card-w: 31px; gap: 4px; }
  .hero-cards[data-count="7"] { --card-w: 29px; gap: 4px; }
  .meta-pill { height: 30px; padding: 0 12px; font-size: 13px; }

  .controls { gap: 8px; }
  .status { font-size: 13px; }
  .odds { font-size: 11px; }
  .bet-row { flex-direction: column; align-items: stretch; gap: 6px; }
  .quick { justify-content: center; flex-wrap: wrap; }
  .quick-btn { height: 28px; padding: 0 10px; font-size: 12px; }
  .slider-wrap { width: 100%; gap: 8px; }
  .bet-amount { width: 76px; height: 30px; font-size: 13px; }
  .actions { gap: 6px; }
  .actions .btn { height: 42px; padding: 0 14px; }
}

/* The smallest phones on their side: under 400px there is no slack at
   all, so the hand and the controls give up a little more. */
@media (orientation: landscape) and (max-height: 400px) and (min-width: 561px) {
  .app { --pad-t: 6px; --pad-x: 10px; --pad-b: 8px; gap: 6px 10px; }
  .icon-btn { width: 32px; height: 32px; }
  .dock { gap: 6px; }
  .hero-cards { --card-w: 44px; }
  .hero-cards[data-count="3"] { --card-w: 40px; }
  .hero-cards[data-count="4"] { --card-w: 36px; }
  .hero-cards[data-count="5"] { --card-w: 32px; gap: 4px; }
  .hero-cards[data-count="6"] { --card-w: 28px; gap: 3px; }
  .hero-cards[data-count="7"] { --card-w: 26px; gap: 3px; }
  .meta-pill { height: 28px; font-size: 12px; }
  .panel { padding: 8px 10px 10px; gap: 6px; }
  .status { font-size: 12px; }
  .controls { gap: 6px; }
  .quick-btn { height: 26px; padding: 0 9px; font-size: 11px; }
  .bet-amount { height: 28px; }
  .actions .btn { height: 38px; padding: 0 12px; }
}

/* ── the rating ─────────────────────────────────────────────────── */
.meta-rating {
  margin-left: 8px;
  padding: 1px 7px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .3px;
  color: var(--ink-2);
  box-shadow: inset 0 0 0 1px var(--line-2);
  transition: color .3s var(--ease), box-shadow .3s var(--ease);
}
.meta-rating[data-move="up"] { color: var(--accent); box-shadow: inset 0 0 0 1px currentColor; }
.meta-rating[data-move="down"] { color: var(--ink-3); box-shadow: inset 0 0 0 1px currentColor; }
/* a seat's rating: an outlined pill like the hero's, so it never reads as chips */
.seat-rating {
  margin-left: 5px;
  padding: 0 4px;
  border-radius: var(--r-pill);
  font-size: 9px;
  font-weight: 800;
  color: var(--ink-3);
  letter-spacing: .2px;
  box-shadow: inset 0 0 0 1px var(--line-2);
  vertical-align: 1px;
}

/* what the table calls the board, on the street line of the log */
.log-talk {
  margin-left: auto;
  padding: 1px 6px;
  border-radius: var(--r-pill);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .3px;
  color: var(--ink-3);
  box-shadow: inset 0 0 0 1px var(--line-2);
  white-space: nowrap;
  cursor: help;
}

/* ── the rating, in the header ─────────────────────────────────── */
.mark-rating {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  margin-left: 8px;
  padding: 3px 9px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  box-shadow: var(--ring);
  font: inherit;
  cursor: pointer;
  transition: box-shadow .3s var(--ease), color .3s var(--ease);
}
.mark-rating b { font-family: var(--font-display); font-size: 13px; font-weight: 800; letter-spacing: -.2px; color: var(--ink); }
.mark-rating i { font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase; color: var(--ink-3); }
.mark-rating[data-move="up"] b { color: var(--accent); }
.mark-rating[data-move="down"] b { color: var(--ink-3); }
.mark-rating[data-move="up"], .mark-rating[data-move="down"] { box-shadow: var(--ring), 0 0 0 2px var(--line-2); }
.mark-rating[hidden] { display: none; }
/* On a phone the header cannot hold the game tag, the rating and five
   buttons at once, and a header wider than the screen makes the browser
   zoom the whole page out. The rating stays; the game tag goes, the
   felt says what is being played; and the mark clips rather than pushes. */
@media (max-width: 480px) {
  .mark { flex: 1 1 auto; overflow: hidden; }
  .mark-variant { display: none; }
  .mark-rating i { display: none; }
  .mark-rating { margin-left: 4px; padding: 2px 7px; flex: none; }
}

.rating-tip {
  position: absolute;
  top: calc(100% + 8px);
  left: var(--pad-x, 16px);
  z-index: 60;
  width: min(360px, calc(100vw - 2 * var(--pad-x, 16px)));
  padding: 14px 16px;
  border-radius: var(--r-lg);
  background: var(--surface-3);
  box-shadow: var(--ring), 0 18px 40px -18px rgba(0, 0, 0, .5);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.rating-tip[hidden] { display: none; }
.rating-tip h3 { margin: 0 0 6px; font-family: var(--font-display); font-size: 15px; color: var(--ink); }
.rating-tip p { margin: 0 0 8px; }
.rating-tip b { color: var(--ink); }
.rating-scale { list-style: none; margin: 4px 0 10px; padding: 0; display: flex; flex-direction: column; gap: 3px; }
.rating-scale li { display: flex; align-items: baseline; gap: 8px; padding: 4px 8px; border-radius: 8px; }
.rating-scale li[data-you="true"] { background: var(--surface-2); box-shadow: var(--ring); }
.rating-scale li b { width: 64px; font-size: 12px; color: var(--ink); }
.rating-scale li span { color: var(--ink-3); }
.rating-scale li i { margin-left: auto; font-style: normal; font-weight: 700; color: var(--accent); }
.rating-tip-foot { color: var(--ink-3); font-size: 11.5px; }
.topbar { position: relative; }

/* the judge's line in the log */
.log-line[data-kind="judge"] { font-size: 11px; color: var(--ink-3); padding-left: 22px; }
.log-line[data-kind="judge"] .log-amt[data-tone="up"] { color: var(--accent); }
.log-line[data-kind="judge"] .log-amt[data-tone="down"] { color: var(--ink-3); }

/* Nothing parked off to the side may widen the page: on a 360px phone the
   rail's drawer sat at x=408 and the browser zoomed the whole layout out
   to 391px to fit it. Clip does not make a scroll container, so the felt
   and the tray behave as before. */
html, body, .app { overflow-x: clip; }
/* and the header itself has to fit 360: six buttons and the mark */
@media (max-width: 380px) {
  .topbar .icon-btn { width: 34px; height: 34px; }
  .topbar-actions { gap: 4px; }
  .topbar { padding-left: 10px; padding-right: 10px; }
}

/* ── the runout, the verdict, the countdown ───────────────────────── */
/* each showing hand's chance, over its seat, while the board runs out */
.seat[data-eq]::after {
  content: attr(data-eq);
  /* under the plate, not over the cards: the cards are the thing to see */
  position: absolute; left: 50%; top: auto; bottom: -13px; transform: translateX(-50%);
  padding: 2px 8px; border-radius: var(--r-pill);
  background: var(--ink); color: var(--surface);
  font-family: var(--font-display); font-size: 12px; font-weight: 800; letter-spacing: .2px;
  box-shadow: var(--lift);
  pointer-events: none; z-index: 3;
}
.seat[data-eq="100%"]::after { background: var(--accent); color: var(--accent-ink); }
.seat[data-eq="0%"]::after { opacity: .55; }
/* the judge's word after a move */
.coach[data-tone="down"] b { color: var(--card-red); }
.coach[data-tone="up"] b { color: var(--accent); }
/* the deal button fills as the next hand approaches */
.deal-btn { position: relative; overflow: hidden; }
.deal-btn[data-counting="true"]::before {
  content: ""; position: absolute; inset: 0;
  width: calc(var(--p, 0) * 100%);
  background: rgba(255, 255, 255, .16);
  pointer-events: none;
}
.deal-btn > span { position: relative; }
/* fold is irreversible: a little air between it and check */


/* ── House rules ───────────────────────────────────────────────────
   The optional extras a table can switch on. Everything here is quiet:
   a chip under the street name, a badge on a plate, a second board. */

/* which rules this table plays, under the street tag */
.rules-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 6px;
  max-width: min(92%, 420px);
}
.rules-chips:empty { display: none; }
.rule-chip {
  display: inline-flex;
  align-items: center;
  height: clamp(13px, var(--felt-h, 420px) * .04, 17px);
  padding: 0 7px;
  border-radius: var(--r-pill);
  box-shadow: inset 0 0 0 1px var(--felt-line);
  font-family: var(--font-text);
  font-size: clamp(7px, var(--felt-h, 420px) * .02, 8px);
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--on-felt-dim);
  white-space: nowrap;
}
.rule-chip[data-live="true"] { color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
/* same call as the street tag: on a felt this small the board says enough */
.app[data-table="tiny"] .rules-chips { display: none; }

/* the second board, when the hand is run twice: stacked under the first */
.community-2 { opacity: .92; }
.community-2[hidden] { display: none; }
.center[data-boards="2"] { --card-w: clamp(22px, var(--felt-h, 420px) * .131, 53px); }
.center[data-boards="2"] .street-tag { order: 3; }
.center[data-boards="2"] .rules-chips { order: 4; }
/* On a phone the band between the top seat's plate and the bottom seats'
   cards is about 140px on a 353px felt, and a full table leaves 115px.
   Two boards there mean smaller cards, a tighter column, the pot pill
   without its chip stack, and no tag or chips row: the boards say enough.
   The column also sits a little higher, since the band does. Measured
   against 2, 4 and 6 seats at 390x844, 4 seats at 360x640 and 844x390. */
.app[data-table="compact"] .center[data-boards="2"],
.app[data-table="tiny"] .center[data-boards="2"] {
  --card-w: clamp(20px, var(--felt-h, 420px) * .082, 44px);
  gap: clamp(4px, var(--felt-h, 420px) * .014, 6px);
}
.app[data-table="compact"] .center[data-boards="2"] { top: calc(var(--center-y, 49%) + var(--center-drop, 0px) - 2%); }
.app[data-table="compact"] .center[data-boards="2"] .street-tag,
.app[data-table="compact"] .center[data-boards="2"] .rules-chips { display: none; }
.app[data-table="compact"] .center[data-boards="2"] .pot,
.app[data-table="tiny"] .center[data-boards="2"] .pot { height: 22px; }
.app[data-table="compact"] .center[data-boards="2"] .pot-value,
.app[data-table="tiny"] .center[data-boards="2"] .pot-value { font-size: 13px; }
.app[data-table="compact"] .center[data-boards="2"] .pot-chips,
.app[data-table="tiny"] .center[data-boards="2"] .pot-chips { display: none; }

/* the cards that never came: seen, but never dealt */
.card.is-rabbit { opacity: .55; filter: saturate(.6); }

/* Seat badges for the rules that mark a chair: on a kill, still standing,
   posted the straddle. The corner opposite the position badge, so the two
   never meet. */
.seat .seat-plate::after {
  content: none;
  position: absolute;
  z-index: 4;
  left: calc(clamp(0px, (var(--felt-w, 900px) - 400px) * .13, 30px) * -1);
  bottom: calc(clamp(0px, (var(--felt-h, 420px) - 240px) * .05, 9px) * -1);
  display: grid;
  place-items: center;
  height: clamp(15px, var(--felt-h, 420px) * .048, 20px);
  min-width: clamp(15px, var(--felt-h, 420px) * .048, 20px);
  padding: 0 clamp(3px, var(--felt-h, 420px) * .015, 6px);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-3);
  font-family: var(--font-text);
  font-size: clamp(7px, var(--felt-h, 420px) * .023, 9px);
  font-weight: 700;
  letter-spacing: .4px;
  box-shadow: 0 0 0 2px var(--surface), var(--ring);
  animation: pop 360ms var(--spring);
  pointer-events: none;
}
.seat[data-edge="right"] .seat-plate::after { left: auto; right: calc(clamp(0px, (var(--felt-w, 900px) - 400px) * .13, 30px) * -1); }
.seat[data-straddle="true"] .seat-plate::after { content: "STR"; }
.seat[data-standing="true"] .seat-plate::after { content: "NIT"; }
.seat[data-standing="true"] .seat-plate { outline: 1.5px dotted var(--accent); outline-offset: 2px; }
.seat[data-kill="true"] .seat-plate::after { content: "KILL"; background: var(--accent); color: var(--accent-ink); }
/* your own plate is a bare avatar: the badge hangs off its lower right,
   clear of the position badge at the top */
.seat[data-hero="true"] .seat-plate::after { left: auto; right: -14px; bottom: -6px; }

/* the row of quick buttons under the tray: sit out, straddle, rabbit */
.quick-row { display: flex; justify-content: center; gap: 6px; }
.quick-row:not(:has(> :not([hidden]))) { display: none; }
/* while a rule can put a button here (straddle, rabbit), the row keeps its
   line even when empty, so the table above does not move */
.app[data-quick-row="true"] .quick-row:not(:has(> :not([hidden]))) { display: flex; min-height: 32px; }

/* the house rules' own question in the tray */
.ask-tray[hidden] { display: none; }
.ask-tray { position: relative; overflow: hidden; }
.ask-bar {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 3px;
  width: 100%;
  background: var(--accent);
  transform-origin: left;
  opacity: .65;
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}
.ask-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(106px, 1fr));
  gap: 6px;
  margin-top: 10px;
}
.ask-tiles[hidden] { display: none; }
.ask-tiles .quick-btn {
  height: 36px;
  justify-content: center;
  font-size: 12px;
  letter-spacing: .2px;
}
.ask-tiles .quick-btn[data-on="true"] { background: var(--accent); color: var(--accent-ink); box-shadow: none; }

/* when the tray carries tiles (dealer's choice) the buttons go under the
   grid instead of beside it, so the games have the full width */
.ask-tray:has(.ask-tiles:not([hidden])) {
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}
.ask-tray:has(.ask-tiles:not([hidden])) .shove-actions {
  justify-content: flex-end;
}

/* two boards in a hand's history row */
.hand-boards { display: inline-flex; flex-direction: column; gap: 3px; }
.hand-boards > span { display: inline-flex; gap: 3px; }

/* ── Three buttons on the bar ──────────────────────────────────────
   Table, Hands, You. New game lives in the table sheet, settings behind
   You, and every hand ever behind the rail's last tab. The history
   button itself only shows on a wide screen with the rail switched off,
   where there is no rail to reach it from. */
.history-btn { display: none; }
@media (min-width: 1160px) {
  html:not([data-rail="on"]) .history-btn { display: grid; }
}
.rail-tab-history { margin-left: auto; }

/* the recap's felt is smaller than the table: its patterns come down a size */
.recap-felt[data-felt="synth"] { --cell: 14px; --col: 22px; }
.recap-felt[data-felt="velvet"] { --quilt: 12px; }
.recap-felt[data-felt="slate"] { --grid: 12px; }
.recap-felt[data-felt="monaco"] { --pin: 7px; }

/* the rule chips are a way to the rules: tappable, and the fold-away one reads as a button */
.rules-chips { cursor: pointer; pointer-events: auto; }
.rule-chip[data-rule="more"] { background: rgba(255, 255, 255, .12); box-shadow: none; font-weight: 700; }

/* ── the dock keeps its height ─────────────────────────────────────
   The table is sized from what the dock leaves it, so anything under
   the table that comes and goes must keep its room while it is gone:
   the judge's line, the pot-odds line while that setting is on, and
   the hero's cards between hands. */
/* (a global [hidden] rule is display:none, so these two go invisible
   with data-off rather than hidden, and keep their lines) */
/* The coach speaks in the status line's place rather than on a line of
   its own: it sits over the status, which steps aside while it talks, so
   it costs the table nothing. One line, a pill, never clipped. */
.status { height: 24px; min-height: 24px; line-height: 24px; width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.status[data-coach="true"] b { color: var(--ink); font-weight: 700; }
.status[data-coach="true"][data-tone="down"] b { color: var(--card-red); }
.status[data-coach="true"][data-tone="up"] b { color: var(--accent); }
.coach { display: none !important; }
.odds { min-height: 0; height: 15px; line-height: 15px; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%; max-width: 100%; }
.odds[data-off="true"] { visibility: hidden; }
.app:not([data-odds="true"]) .odds { display: none; }
.hero-cards { min-height: calc(var(--card-w) * 1.4); }

/* the amount under Call and Raise keeps its line while it is empty, so the
   buttons are the same height before a hand as during one */
.actions .btn-sub:empty { display: block; }
.actions .btn-sub:empty::before { content: "\00a0"; }
