/* ─────────────────────────────────────────────────────────────────
   Aegis Casino — Activity styles.

   The look is a card room, not a control panel: green felt under warm
   overhead light, brass trim, and the Aegis gold as the accent. The
   dashboard's navy is deliberately absent — this is meant to feel like a
   different place, not another settings page.

   Mobile-first: the base rules target a phone, and wide screens get a real
   two-column table rather than a stretched column with dead space either
   side. Nothing depends on hover, and tap targets stay at 44px.
   ───────────────────────────────────────────────────────────────── */

:root {
  --gold: #D4A017;
  --gold-lit: #f0c548;
  --gold-dim: #8a6a10;

  /* Felt. Three greens rather than one so the table reads as lit from
     above instead of a flat fill. */
  --felt-deep: #06180f;
  --felt: #0c2a1b;
  --felt-lit: #14402a;

  /* Aegis blue — the same navy family the dashboard uses. Green is the room;
     blue is what the tables are topped with. */
  --blue-deep: #080c18;
  --blue: #0f1628;
  --blue-lit: #1a2440;
  --blue-edge: #1e2d4a;
  --text: #f6f1e4;
  --muted: #9aa79c;
  --win: #6ee787;
  --lose: #ff6b6b;
  --radius: 16px;
  --fade: 420ms;

  /* One symbol cell. Every reel measurement derives from this, so the
     machine scales by changing one number. Sized so five reels of three fit
     a phone without the window running past the fold. */
  --cell: 52px;
  --rows: 3;

  /* How long the amount that just moved stays on screen. app.js reads the same
     figure from CHIP_MS to land the balance's bump on the end of it, so the
     two have to be changed together. */
  --delta-ms: 1500ms;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* [hidden] must beat any display rule set on the same element. Without this
   a `.screen { display: flex }` silently wins over the attribute and the
   element stays on screen. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  min-height: 100%;
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
  touch-action: manipulation;
}

/* The room: a pool of light over the table, falling off to dark at the
   edges. The repeating diagonal threads are the felt weave — barely visible,
   but it stops large areas reading as flat colour. */
body {
  background:
    radial-gradient(ellipse 90% 55% at 50% 0%, rgba(240,197,72,.10), transparent 62%),
    repeating-linear-gradient(45deg, rgba(255,255,255,.014) 0 2px, transparent 2px 4px),
    radial-gradient(ellipse 130% 95% at 50% 42%, var(--felt-lit) 0%, var(--felt) 42%, var(--felt-deep) 100%);
  background-attachment: fixed;
}

#app {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  padding:
    calc(env(safe-area-inset-top) + 12px)
    calc(env(safe-area-inset-right) + 14px)
    calc(env(safe-area-inset-bottom) + 14px)
    calc(env(safe-area-inset-left) + 14px);
  max-width: 560px;
  margin: 0 auto;
  width: 100%;
}
#screens { flex: 1; display: flex; flex-direction: column; padding-top: 54px; }

/* ── Screen transitions ────────────────────────────────────────── */
.screen {
  flex: 1;
  display: flex;
  flex-direction: column;
  opacity: 1;
  transform: none;
  transition: opacity var(--fade) ease, transform var(--fade) cubic-bezier(.22,.7,.3,1);
}
.screen.entering { opacity: 0; transform: translateY(16px) scale(.985); }
.screen.leaving  { opacity: 0; transform: translateY(-10px) scale(.99); }

/* ── Boot screen ───────────────────────────────────────────────── */
/* Full-bleed and above everything. The floor is built underneath while this
   is still covering the viewport, so revealing it is a fade rather than a
   load — nothing pops in. */
.boot-screen {
  position: fixed;
  inset: 0;
  z-index: 50;
  background:
    radial-gradient(ellipse 70% 50% at 50% 32%, rgba(240,197,72,.13), transparent 65%),
    radial-gradient(ellipse 130% 95% at 50% 45%, var(--felt-lit) 0%, var(--felt) 45%, var(--felt-deep) 100%);
  display: grid;
  place-items: center;
  padding: 32px 24px;
  opacity: 1;
  transition: opacity var(--fade) ease;
}
.boot-screen.leaving { opacity: 0; }
.boot-inner { text-align: center; max-width: 360px; }
.boot-mark {
  width: 78px; height: 78px; margin: 0 auto 20px;
  display: block; object-fit: contain;
  filter: drop-shadow(0 8px 22px rgba(212,160,23,.45));
  animation: floaty 3s ease-in-out infinite;
}
@keyframes floaty {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-7px); }
}
.boot-inner h1 {
  margin: 0 0 8px; font-size: 1.45rem; letter-spacing: .01em; font-weight: 800;
}
.boot-inner p { margin: 0; color: var(--muted); font-size: .95rem; line-height: 1.5; }
.boot-spinner {
  width: 26px; height: 26px; margin: 22px auto 0;
  border: 2px solid rgba(212,160,23,.2);
  border-top-color: var(--gold);
  border-radius: 50%;
  animation: spin 800ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
#boot-retry { margin-top: 20px; }

/* ── Floating chrome ───────────────────────────────────────────── */
/* Fixed rather than in the flow, so the balance sits in exactly the same
   place on every screen and never shifts the content beneath it. */
.float-balance {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 12px);
  right: calc(env(safe-area-inset-right) + 14px);
  z-index: 30;
  background: linear-gradient(180deg, rgba(12,42,27,.94), rgba(6,24,15,.94));
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(212,160,23,.45);
  border-radius: 999px;
  padding: 7px 16px;
  text-align: right;
  box-shadow: 0 6px 22px rgba(0,0,0,.5), inset 0 1px 0 rgba(240,197,72,.18);
  animation: pillIn var(--fade) ease;
}
@keyframes pillIn {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}
.balance-label {
  display: block; font-size: .66rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); line-height: 1;
}
.balance-amount {
  font-size: 1.1rem; font-weight: 800; color: var(--gold-lit);
  font-variant-numeric: tabular-nums; display: inline-block; line-height: 1.25;
  text-shadow: 0 0 14px rgba(240,197,72,.35);
}
/* Flashes when the balance changes so a win registers even if you were
   watching the reels. Also fires when currency moves outside the Activity,
   which is how you notice a /daily landing. */
.balance-amount.bump { animation: bump .45s ease; }
@keyframes bump {
  0% { transform: scale(1); }
  40% { transform: scale(1.2); }
  100% { transform: scale(1); }
}

/* The amount that just moved. Starts under the balance and flies up into it,
   landing as the total starts counting — so the change is legible on its own
   before it is folded into the running figure. */
.float-balance { position: fixed; }
.balance-delta {
  position: absolute;
  top: 100%;
  right: 16px;
  margin-top: 2px;
  font-size: .82rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
  animation: deltaFly var(--delta-ms) cubic-bezier(.4,0,.25,1) forwards;
}
.balance-delta.up { color: var(--win); text-shadow: 0 0 10px rgba(110,231,135,.6); }
.balance-delta.down { color: var(--lose); text-shadow: 0 0 10px rgba(255,107,107,.55); }
@keyframes deltaFly {
  0%   { opacity: 0; transform: translateY(6px) scale(.9); }
  8%   { opacity: 1; transform: translateY(0) scale(1.06); }
  14%  { opacity: 1; transform: translateY(0) scale(1); }
  /* The hold. Most of the gesture is the number sitting still and legible —
     it used to fly almost as soon as it arrived, which read as a flicker. */
  82%  { opacity: 1; transform: translateY(0) scale(1); }
  /* Up into the balance, shrinking as it goes, so it reads as being absorbed
     rather than drifting off the top. */
  100% { opacity: 0; transform: translateY(-24px) scale(.72); }
}

.float-back {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 12px);
  left: calc(env(safe-area-inset-left) + 14px);
  z-index: 30;
  width: 40px; height: 40px; min-width: 40px; min-height: 40px;
  border: 1px solid rgba(212,160,23,.3);
  border-radius: 50%;
  background: rgba(6,24,15,.9);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--text);
  font: inherit; font-size: 1.6rem; line-height: 1; padding: 0 0 4px;
  cursor: pointer;
  transition: transform .08s ease, background .15s ease;
  animation: pillIn var(--fade) ease;
}
.float-back:active { transform: scale(.93); }

/* Discord's mobile client draws its own control row (the leave button and
   friends) over the top of the activity, and safe-area insets say nothing
   about it — they describe the hardware, not Discord's chrome. The class is
   set from sdk.platform rather than a width query, because a narrow desktop
   window is not a phone and should not get phone spacing. */
body.is-mobile .float-balance,
body.is-mobile .float-back { top: calc(env(safe-area-inset-top) + 62px); }
body.is-mobile #screens { padding-top: 104px; }

/* ── Tables ────────────────────────────────────────────────────── */
/* Every surface a player interacts with is the same object: Aegis blue felt
   under a thick brass edge, sitting on the green room. Defining it once is
   what keeps the machine, the placards and the side panel looking like
   furniture from the same room rather than three unrelated boxes. */
.table-surface,
.machine,
.table-side,
/* Poker's controls sit in the main column, under the table, rather than inside
   the side panel — so they need a panel of their own or they are the one block
   on the page not wearing one. Only poker's: every other table keeps them in
   the side column. */
#screen-poker .table-main .controls,
.paytable {
  background:
    radial-gradient(ellipse 90% 70% at 50% 0%, rgba(240,197,72,.07), transparent 70%),
    repeating-linear-gradient(45deg, rgba(255,255,255,.012) 0 2px, transparent 2px 4px),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 55%, var(--blue-deep) 100%);
  border: 3px solid var(--gold-dim);
  border-radius: var(--radius);
  box-shadow:
    inset 0 0 0 1px rgba(240,197,72,.35),
    inset 0 2px 26px rgba(0,0,0,.6),
    0 10px 30px rgba(0,0,0,.45);
}

/* ── Casino floor ──────────────────────────────────────────────── */
/* A marquee rather than a page heading: brass rules above and below, suits
   in the corners, the sort of sign that hangs over an entrance. */
.floor-head {
  margin-bottom: 18px;
  padding: 14px 16px 13px;
  position: relative;
  text-align: center;
  border-top: 1px solid rgba(212,160,23,.35);
  border-bottom: 1px solid rgba(212,160,23,.35);
  background: linear-gradient(180deg, rgba(240,197,72,.07), transparent 70%);
}
.floor-head::before,
.floor-head::after {
  position: absolute; top: 50%; transform: translateY(-50%);
  font-size: .9rem; letter-spacing: 4px;
  color: rgba(212,160,23,.28);
  pointer-events: none;
}
.floor-head::before { content: '♠ ♦'; left: 12px; }
.floor-head::after  { content: '♥ ♣'; right: 12px; }
.floor-head h1 {
  margin: 0 0 3px;
  font-size: 1.15rem; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--gold-lit);
  text-shadow: 0 0 18px rgba(240,197,72,.3);
}
.floor-sub { margin: 0; color: var(--muted); font-size: .82rem; }

.game-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 11px; }

/* Each game is a table placard: felt and wood, brass edge, and a soft inner
   light so it sits on the table rather than floating above it. */
.game-card {
  position: relative;
  background:
    radial-gradient(ellipse 90% 70% at 50% 0%, rgba(240,197,72,.06), transparent 70%),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 60%, var(--blue-deep) 100%);
  border: 2px solid var(--gold-dim);
  border-radius: 14px;
  padding: 14px 12px;
  text-align: left;
  color: var(--text);
  font: inherit;
  cursor: pointer;
  min-height: 116px;
  display: flex; flex-direction: column; gap: 4px;
  box-shadow: inset 0 0 0 1px rgba(240,197,72,.18), 0 5px 16px rgba(0,0,0,.4);
  transition: transform .12s ease, border-color .18s ease, box-shadow .18s ease, opacity .15s ease;
}
.game-card:active { transform: scale(.97); }
.game-card.playable {
  border-color: var(--gold);
  box-shadow: inset 0 0 0 1px rgba(240,197,72,.4), 0 6px 20px rgba(0,0,0,.45);
}
.game-card.playable:hover {
  border-color: var(--gold);
  box-shadow: inset 0 1px 0 rgba(240,197,72,.2), 0 8px 26px rgba(0,0,0,.5), 0 0 22px rgba(212,160,23,.18);
  transform: translateY(-2px);
}
.game-card:disabled { opacity: .38; cursor: not-allowed; }
.game-card:disabled:hover { transform: none; }
.game-emoji { font-size: 1.7rem; line-height: 1; }
.game-name {
  font-weight: 800; font-size: .92rem;
  letter-spacing: .03em; text-transform: uppercase; color: var(--gold-lit);
}
.game-card:disabled .game-name { color: var(--text); }
.game-blurb { color: var(--muted); font-size: .84rem; line-height: 1.35; }
.game-foot { margin-top: auto; display: flex; align-items: center; gap: 6px; }
.game-players {
  font-size: .80rem; font-weight: 800; color: var(--win);
  display: inline-flex; align-items: center; gap: 5px;
}
.game-players .dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--win);
  box-shadow: 0 0 8px rgba(110,231,135,.8);
  animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
.game-tag {
  font-size: .70rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}

/* ── The machine ───────────────────────────────────────────────── */
/* A cabinet: dark wood body, brass bezel, warm light spilling from inside. */
.machine {
  position: relative;
  padding: 20px 16px 6px;
}
/* Brass trim strip across the top of the cabinet. */
.machine::before {
  content: '';
  position: absolute; left: 14%; right: 14%; top: -1px; height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--gold-lit), transparent);
  box-shadow: 0 0 12px rgba(240,197,72,.5);
}
.reels { display: flex; gap: 6px; justify-content: center; position: relative; }

/* The winning-line overlay. Sits above the reels and their curvature shading,
   and takes no pointer events so it can never block the machine. */
.payline-layer {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  z-index: 3;
  overflow: visible;
}
.payline-box, .payline-path {
  filter: drop-shadow(0 0 5px currentColor);
  animation: paylineIn 260ms ease-out both;
}
.payline-path { opacity: .85; }
@keyframes paylineIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Each reel is a viewport three cells tall. The strip inside is many cells
   long and slides upward; only the cells in the window are visible, which is
   what makes it read as a spinning drum rather than a fade. */
.reel {
  flex: 1;
  height: calc(var(--cell) * var(--rows));
  overflow: hidden;
  border-radius: 10px;
  background: linear-gradient(180deg, #04070f, #010308);
  border: 2px solid rgba(212,160,23,.4);
  box-shadow: inset 0 0 20px rgba(0,0,0,.95);
  position: relative;
}
/* Curvature: darker towards the top and bottom of the window so symbols
   appear to roll around a drum rather than slide up a flat surface. Shallow,
   because across three rows a deep gradient swallows the outer two. */
.reel::before, .reel::after {
  content: ''; position: absolute; left: 0; right: 0; height: 15%;
  pointer-events: none; z-index: 2;
}
.reel::before { top: 0; background: linear-gradient(rgba(0,0,0,.8), transparent); }
.reel::after { bottom: 0; background: linear-gradient(transparent, rgba(0,0,0,.8)); }

.strip { will-change: transform; }
.sym {
  height: var(--cell);
  display: grid;
  place-items: center;
  font-size: calc(var(--cell) * 0.62);
  line-height: 1;
  user-select: none;
}

/* A symbol that was part of a paying line. Across five lines a win is
   genuinely hard to spot otherwise — a run along the V reads as scatter until
   the symbols that paid are picked out. */
.sym.hit {
  animation: hitPulse 900ms ease-in-out infinite;
  filter: drop-shadow(0 0 10px rgba(240,197,72,.95)) drop-shadow(0 0 22px rgba(240,197,72,.55));
}
@keyframes hitPulse {
  0%, 100% { transform: scale(1.14); }
  50%      { transform: scale(1.0); }
}

.table-main .machine.win, .machine.win { animation: winGlow 1s ease; }
@keyframes winGlow {
  0%, 100% { box-shadow: inset 0 0 0 1px rgba(240,197,72,.35), inset 0 2px 26px rgba(0,0,0,.6), 0 10px 30px rgba(0,0,0,.45); }
  50% { box-shadow: inset 0 0 0 1px rgba(240,197,72,.35), inset 0 2px 26px rgba(0,0,0,.6), 0 0 36px rgba(110,231,135,.6), 0 10px 30px rgba(0,0,0,.45); }
}
/* A loss gets the same treatment in red. Wins glowing while losses passed in
   silence made every spin feel like a near-miss. */
.machine.lose { animation: loseGlow 1s ease; }
@keyframes loseGlow {
  0%, 100% { box-shadow: inset 0 0 0 1px rgba(240,197,72,.35), inset 0 2px 26px rgba(0,0,0,.6), 0 10px 30px rgba(0,0,0,.45); }
  50% { box-shadow: inset 0 0 0 1px rgba(240,197,72,.35), inset 0 2px 26px rgba(0,0,0,.6), 0 0 32px rgba(255,107,107,.45), 0 10px 30px rgba(0,0,0,.45); }
}

/* ── Result + session ──────────────────────────────────────────── */
.result {
  text-align: center; margin: 16px 0 12px;
  font-size: 1rem; min-height: 1.4em; color: var(--muted);
  transition: color .2s ease;
}
.result.win { color: var(--win); font-weight: 800; text-shadow: 0 0 16px rgba(110,231,135,.4); }
.result.lose { color: var(--lose); }

.session-bar {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  background: rgba(8,12,24,.75);
  border: 1px solid rgba(212,160,23,.28);
  border-radius: 10px; padding: 8px 12px; margin-bottom: 14px;
}
.session-label, .session-spins {
  font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.session-net {
  font-size: 1.05rem; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--muted);
}
.session-net.up { color: var(--win); }
.session-net.down { color: var(--lose); }

/* ── Controls ──────────────────────────────────────────────────── */
.controls { margin-top: auto; }
.bet-row { display: flex; align-items: stretch; gap: 10px; margin-bottom: 10px; }
.bet-display {
  flex: 1;
  background: rgba(8,12,24,.75);
  border: 1px solid rgba(212,160,23,.3);
  border-radius: 12px; display: grid; place-items: center; padding: 8px;
}
.bet-label {
  font-size: .70rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.bet-amount {
  font-size: 1.3rem; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--gold-lit);
}

.btn {
  font: inherit; border-radius: 12px; cursor: pointer;
  background: linear-gradient(180deg, var(--blue-lit), var(--blue));
  border: 1px solid rgba(212,160,23,.3);
  color: var(--text);
  /* 44px is the smallest reliable thumb target; every control clears it. */
  min-height: 44px; min-width: 44px;
  transition: transform .08s ease, opacity .15s ease, background .15s ease,
              color .15s ease, border-color .15s ease;
}
.btn:active { transform: scale(.96); }
.btn:disabled { opacity: .4; cursor: not-allowed; transform: none; }
.btn-step { font-size: 1.5rem; font-weight: 700; width: 56px; }
.btn-primary {
  background: linear-gradient(180deg, var(--gold-lit), var(--gold-dim));
  border-color: var(--gold);
  color: #221800; font-weight: 800; padding: 0 20px;
}

.quick-row { display: flex; gap: 8px; margin-bottom: 12px; }
.btn-quick { flex: 1; font-size: .8rem; font-weight: 700; color: var(--muted); }
.btn-quick.active {
  color: #221800;
  background: linear-gradient(180deg, var(--gold-lit), var(--gold));
  border-color: var(--gold-lit);
}

.spin-row { display: flex; gap: 10px; }
/* The lever. Brass, lit, and the only thing on screen with a real shine. */
.btn-spin {
  flex: 1; min-height: 60px; font-size: 1.18rem; font-weight: 900;
  letter-spacing: .08em; text-transform: uppercase;
  background: linear-gradient(180deg, var(--gold-lit) 0%, var(--gold) 45%, var(--gold-dim) 100%);
  border-color: var(--gold-lit);
  color: #221800;
  box-shadow: 0 6px 20px rgba(212,160,23,.28), inset 0 1px 0 rgba(255,255,255,.35);
}
.btn-spin:disabled {
  background: linear-gradient(180deg, var(--blue-lit), var(--blue));
  color: var(--muted); box-shadow: none;
}
.btn-auto {
  min-height: 60px; min-width: 92px; padding: 0 12px;
  flex: 0 0 auto;
  font-size: .82rem; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}
.btn-auto[aria-pressed="true"] {
  background: linear-gradient(180deg, var(--win), #35a457);
  border-color: var(--win); color: #04220f;
  box-shadow: 0 0 18px rgba(110,231,135,.35);
}

/* ── Wide screens ──────────────────────────────────────────────── */
/* The single column left most of a desktop window empty. Wide layouts get a
   proper table instead: more games across, and the machine sitting beside
   its stats and controls rather than stacked above them. */
@media (min-width: 760px) {
  :root { --cell: 74px; }
  #app { max-width: 1180px; }
  #screens { padding-top: 60px; }

  .floor-head { padding: 18px 20px 16px; margin-top: 20px; margin-bottom: 22px; }
  .floor-head h1 { font-size: 1.5rem; }
  .floor-head::before, .floor-head::after { font-size: 1.1rem; }
  .game-grid { grid-template-columns: repeat(4, 1fr); gap: 14px; }
  .game-card { min-height: 148px; padding: 16px 14px; }
  .game-emoji { font-size: 2rem; }

  /* Every table screen, by class rather than by name. The old list of ids
     had to be edited by hand for each new table, and roulette was added
     without it — so it never got the two-column layout and stacked into
     one very tall page. */
  .table-screen {
    display: grid;
    align-content: center;
    gap: 0 26px;
  }
  .machine { padding: 26px 22px 8px; }
  .table-side { padding: 20px; }
  .controls { margin-top: 0; }
  .session-bar { margin-bottom: 18px; }
}

@media (min-width: 1100px) {
  #app { max-width: 1360px; }
}

/* Respect a stated preference for less motion: the reels still land on the
   right symbols, they just stop moving to get there, and screens swap rather
   than crossfade. The result is never conveyed by animation alone — the text
   and the balance always say it. */
/* A table nobody is sat at should still look like a table somebody could sit
   at, so the empty chairs breathe very slightly rather than being dead. */
.pkr-seat.empty .pkr-seat-empty { transition: opacity .4s ease; }

@media (prefers-reduced-motion: reduce) {
  .bj-seat.turn, .bj-seat.struck, .bj-seat-cards .bj-card.drew, .bj-seat-foot.settled,
  .pkr-seat.acting .pkr-plate,
  .pkr-plate.struck, .pkr-seat-stack.up, .pkr-seat.won .pkr-plate,
  .pkr-committed.bumped, .pkr-last.said, .pkr-pot-value.grew { animation: none !important; }
}

@media (prefers-reduced-motion: reduce) {
  .strip { transition: none !important; }
  .screen { transition: none !important; }
  .screen.entering, .screen.leaving { opacity: 1; transform: none; }
  .machine.win, .machine.lose, .balance-amount.bump,
  .game-players .dot, .boot-mark, .boot-spinner { animation: none; }
  /* The glow stays — it is how a winning line is identified — but it stops
     pulsing. */
  .sym.hit { animation: none; transform: scale(1.1); }
  /* Cards arrive in place; the collect sequence skips itself entirely. */
  .bj-card { transition: none !important; }
  .game-card.playable:hover { transform: none; }
}

/* ── Table layout ──────────────────────────────────────────────── */
/* The two halves are the fix for the uneven panels in the screenshot: they
   were different heights because the side column spanned two grid rows while
   the machine spanned one. Now each half is a single stretched cell, so they
   start and end together whatever is inside them. */
.table-main, .table-side {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.table-main .machine { margin: 0; }
.table-side .controls { margin-top: auto; }


/* ── Paytable ──────────────────────────────────────────────────── */
.paytable {
  padding: 12px 14px 14px;
}
.paytable-head {
  margin: 0 0 10px;
  font-size: .76rem; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--gold-lit);
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
}
.paytable-note {
  font-size: .74rem; letter-spacing: .08em; color: var(--muted); font-weight: 600;
  margin: 0 0 10px;
}
.paytable-at {
  font-size: .70rem; letter-spacing: .1em; color: var(--muted);
  text-transform: none; font-weight: 700;
}
.paytable-at #paytable-bet { color: var(--gold-lit); }
/* One row per symbol, its run lengths across the columns — the layout a real
   machine's glass uses, and the only one that stays readable now that every
   symbol pays four different amounts. */
.paytable-rows {
  display: grid;
  grid-template-columns: 1fr;
  gap: 4px;
}
.paytable-row {
  display: grid;
  grid-template-columns: 1.6rem repeat(4, 1fr);
  align-items: center; gap: 6px;
  padding: 4px 8px;
  background: rgba(8,12,24,.6);
  border: 1px solid rgba(212,160,23,.16);
  border-radius: 8px;
}
/* The header carries the run lengths, so it needs no plate of its own. */
.paytable-head-row {
  background: none; border-color: transparent; padding-bottom: 0;
}
.paytable-head-row .pay-col {
  color: var(--muted); font-size: .72rem; letter-spacing: .04em;
  font-weight: 700; line-height: 1.15;
}
.pay-symbols { font-size: 1rem; line-height: 1; }
.pay-mult {
  font-size: .82rem; font-weight: 800; color: var(--gold-lit);
  font-variant-numeric: tabular-nums;
}
.pay-col { text-align: right; }
/* A run length this symbol doesn't pay on. Dimmed rather than blank so the
   columns still line up and the gap reads as deliberate. */
.pay-none { color: rgba(255,255,255,.2); }

/* ── Crash ─────────────────────────────────────────────────────── */
.rocket { padding-bottom: 14px; }
.rocket-graph {
  position: relative;
  height: calc(var(--cell) * var(--rows));
  border-radius: 10px;
  overflow: hidden;
  background: linear-gradient(180deg, #04070f, #010308);
  border: 2px solid rgba(212,160,23,.4);
  box-shadow: inset 0 0 20px rgba(0,0,0,.95);
}
.crash-curve { position: absolute; inset: 0; width: 100%; height: 100%; }
.crash-line {
  fill: none;
  stroke: var(--win);
  stroke-width: 2.5;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 6px rgba(110,231,135,.65));
}
.crash-fill { fill: rgba(110,231,135,.10); stroke: none; }
/* Burst: the whole trace flips red, which is the clearest possible statement
   that the round is over and the stake is gone. */
.is-bust .crash-line { stroke: var(--lose); filter: drop-shadow(0 0 8px rgba(255,107,107,.7)); }
.is-bust .crash-fill { fill: rgba(255,107,107,.12); }

/* Multiplier scale down the left edge. The values are redrawn as the graph
   rescales, so the climb is always read against round numbers. */
.crash-axis {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 2;
}
.crash-axis-tick {
  position: absolute;
  left: 6px;
  transform: translateY(-50%);
  font-size: .72rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: rgba(255,255,255,.42);
  text-shadow: 0 1px 6px rgba(0,0,0,.95);
}
/* A hairline across the plot at each labelled multiple. */
.crash-axis-tick::after {
  content: '';
  position: absolute;
  left: 100%; top: 50%;
  margin-left: 6px;
  width: 100vw; height: 1px;
  background: rgba(255,255,255,.07);
}

.crash-ship {
  position: absolute;
  font-size: 1.25rem;
  transform: translate(-50%, -50%) rotate(-18deg);
  transition: none;
  pointer-events: none;
  z-index: 3;
  filter: drop-shadow(0 0 8px rgba(240,197,72,.55));
}
.is-bust .crash-ship { animation: shipPop 420ms ease-out forwards; }
@keyframes shipPop {
  0%   { transform: translate(-50%, -50%) rotate(-18deg) scale(1); opacity: 1; }
  100% { transform: translate(-50%, -50%) rotate(24deg) scale(1.9); opacity: 0; }
}

.crash-readout {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  pointer-events: none;
  gap: 2px;
}
.crash-mult {
  font-size: 2.4rem; font-weight: 900; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  text-shadow: 0 2px 18px rgba(0,0,0,.9);
}
.crash-mult.bust { color: var(--lose); }
.crash-mult.won { color: var(--win); }
.crash-cashval {
  font-size: .78rem; font-weight: 700; letter-spacing: .06em;
  color: var(--muted);
  text-shadow: 0 1px 8px rgba(0,0,0,.9);
}

/* Where recent rounds burst. */
/* Who is on the rocket. A row per player: their face, where they got off, and
   what it was worth. Everybody in a server rides the same round, so these are
   cash-outs off the climb on screen rather than numbers from somewhere else. */
.crash-rider { align-items: center; gap: 8px; }
.crash-rider-who { display: flex; align-items: center; gap: 7px; min-width: 0; flex: 1 1 auto; }
.crash-face { --face: 20px; }
.crash-rider-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 700;
}
.crash-rider.you .crash-rider-name { color: var(--gold-lit); }
.crash-rider-at {
  flex: 0 0 auto;
  font-size: .78rem; font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  min-width: 3.6rem; text-align: right;
}
.crash-rider.cashed .crash-rider-at { color: var(--win); }
.crash-rider.busted .crash-rider-at { color: var(--lose); }
/* Still up there. Breathing, because whether they get out is the thing worth
   watching while it is happening.
   NOT poker's waiting animation: that one works on a border, and these have no
   border to work on — it drew a box round the word instead. */
.crash-rider.riding .crash-rider-at {
  color: var(--gold-lit);
  animation: crashRiding 1.5s ease-in-out infinite;
}
@keyframes crashRiding {
  0%, 100% { opacity: 1; }
  50%      { opacity: .48; }
}
.crash-rider.busted { opacity: .62; }
.crash-riders {
  font-size: .74rem; font-weight: 900; color: var(--gold-lit);
  font-variant-numeric: tabular-nums;
}

.crash-history { display: flex; flex-wrap: wrap; gap: 5px; }
.crash-chip {
  font-size: .78rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  padding: 3px 7px;
  border-radius: 999px;
  background: rgba(8,12,24,.6);
  border: 1px solid rgba(212,160,23,.16);
  color: var(--muted);
}
.crash-chip.low { color: #ff9f9f; }
.crash-chip.good { color: var(--win); }
.crash-chip.gold { color: var(--gold-lit); border-color: rgba(212,160,23,.5); }
/* A round the player was actually in and banked. */
.crash-chip.cashed { box-shadow: inset 0 0 0 1px rgba(110,231,135,.55); }

/* Auto cash-out. */
.auto-cash {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 0 0 12px;
  padding: 8px 10px;
  background: rgba(8,12,24,.5);
  border: 1px solid rgba(212,160,23,.16);
  border-radius: 10px;
}
.auto-cash-label {
  font-size: .70rem; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.auto-cash-field { display: flex; align-items: center; gap: 3px; }
.auto-cash-input {
  width: 68px;
  background: rgba(0,0,0,.4);
  border: 1px solid rgba(212,160,23,.3);
  border-radius: 7px;
  color: var(--text);
  font: inherit; font-size: .88rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  text-align: right;
  padding: 5px 7px;
}
.auto-cash-input:disabled { opacity: .45; }
.auto-cash-x { font-size: .88rem; font-weight: 800; color: var(--gold-lit); }

/* ── Provably fair ─────────────────────────────────────────────── */
.fair-btn {
  font: inherit;
  font-size: .74rem; font-weight: 800; letter-spacing: .06em;
  text-transform: none;
  color: var(--gold-lit);
  background: rgba(212,160,23,.1);
  border: 1px solid rgba(212,160,23,.35);
  border-radius: 999px;
  padding: 6px 12px;
  cursor: pointer;
}
.fair-btn:active { transform: scale(.95); }

.proof-panel {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(2,8,5,.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center;
  padding: 20px 16px;
  overflow-y: auto;
  animation: pillIn var(--fade) ease;
}
.proof-card {
  width: 100%; max-width: 560px;
  max-height: 100%;
  overflow-y: auto;
  padding: 20px 22px 24px;
  border-radius: var(--radius);
  border: 3px solid var(--gold-dim);
  background: linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 55%, var(--blue-deep) 100%);
  box-shadow: 0 24px 60px rgba(0,0,0,.7);
}
.proof-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-bottom: 4px;
}
.proof-head h2 { margin: 0; font-size: 1.05rem; font-weight: 800; }
.proof-close {
  font: inherit; font-size: 1.5rem; line-height: 1;
  background: none; border: none; color: var(--muted);
  cursor: pointer; padding: 0 4px;
}
.proof-card h3 {
  margin: 18px 0 6px;
  font-size: .74rem; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--gold-lit);
}
.proof-card p, .proof-card li {
  margin: 0 0 8px; font-size: .8rem; line-height: 1.55; color: var(--text);
}
.proof-card ul { margin: 0; padding-left: 18px; }
.proof-code {
  margin: 0;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(0,0,0,.45);
  border: 1px solid rgba(212,160,23,.2);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .80rem; line-height: 1.6;
  color: #cfe9d6;
  /* A formula must never be silently reflowed — it would stop being the
     formula. */
  white-space: pre;
  overflow-x: auto;
}
.proof-body { display: flex; flex-direction: column; gap: 6px; }
.proof-row {
  display: flex; flex-direction: column; gap: 2px;
  padding: 7px 10px;
  background: rgba(8,12,24,.6);
  border: 1px solid rgba(212,160,23,.16);
  border-radius: 8px;
}
.proof-key {
  font-size: .66rem; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.proof-val {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .78rem; color: var(--gold-lit);
  /* Seeds are 64 characters and must be readable and copyable in full. */
  word-break: break-all;
}
.proof-note { color: var(--muted); font-size: .78rem; }

/* Cash Out takes over the bet button mid-round, and has to read as a
   different, more urgent action than the one that placed the bet. */
.btn-cash {
  background: linear-gradient(180deg, #4ade80, #16a34a) !important;
  color: #04180c !important;
  animation: cashPulse 1.1s ease-in-out infinite;
}
@keyframes cashPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(74,222,128,.5); }
  50%      { box-shadow: 0 0 0 8px rgba(74,222,128,0); }
}

/* ── Recent spins ──────────────────────────────────────────────── */
/* Hidden by default. On a stacked layout the side column has no gap to fill,
   and this would just be more to scroll past. */
.history { display: none; }
.history-head {
  margin: 0 0 8px;
  font-size: .72rem; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--gold-lit);
}
.history-rows {
  display: flex; flex-direction: column; gap: 3px;
  /* The list is capped by the space available rather than a row count, so it
     fills the column exactly and never pushes the controls off the bottom. */
  flex: 1; min-height: 0; overflow: hidden;
}
.history-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 3px 8px;
  background: rgba(8,12,24,.5);
  border: 1px solid rgba(212,160,23,.12);
  border-radius: 6px;
  flex: 0 0 auto;
  animation: historyIn 220ms ease-out both;
}
@keyframes historyIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
.history-what {
  font-size: .82rem; font-weight: 700; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.history-none { color: var(--muted); }
.history-amount {
  font-size: .82rem; font-weight: 800;
  font-variant-numeric: tabular-nums; color: var(--muted);
}
.history-amount.up { color: var(--win); }
.history-amount.down { color: var(--lose); }
.history-empty {
  margin: 0; font-size: .76rem; color: var(--muted);
}

/* ── Payline legend ────────────────────────────────────────────── */
.paylines-card { padding: 12px 14px 14px; }
.payline-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.payline-item {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 6px 4px;
  background: rgba(8,12,24,.6);
  border: 1px solid rgba(212,160,23,.16);
  border-radius: 8px;
}
/* A miniature of the board: five columns, three rows, the lit dots tracing
   the route. */
.payline-mini {
  display: grid;
  grid-template-columns: repeat(5, 7px);
  grid-template-rows: repeat(3, 7px);
  gap: 2px;
}
.payline-dot {
  border-radius: 2px;
  background: rgba(255,255,255,.09);
}
.payline-name {
  font-size: .72rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}

/* ── Mute ──────────────────────────────────────────────────────── */
.float-right {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 12px);
  right: calc(env(safe-area-inset-right) + 14px);
  z-index: 30;
  display: flex; align-items: center; gap: 8px;
}
/* The balance moves into that row, so it no longer positions itself. */
.float-right .float-balance { position: static; top: auto; right: auto; }
.float-mute {
  width: 38px; height: 38px; min-width: 38px;
  border: 1px solid rgba(212,160,23,.3);
  border-radius: 50%;
  background: rgba(8,12,24,.85);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  font-size: 1rem; line-height: 1; cursor: pointer;
  transition: transform .08s ease, opacity .15s ease;
}
.float-mute:active { transform: scale(.92); }
.float-mute[aria-pressed="true"] { opacity: .55; }
body.is-mobile .float-right { top: calc(env(safe-area-inset-top) + 62px); }

/* ── Narrow screens: bet without scrolling ─────────────────────────
   Stacked, the panels ran play area → payouts → totals → controls, which put
   the bet and the spin button at the very bottom of the page: on the roulette
   table they were off the bottom of a phone entirely. You had to scroll down
   to bet, back up to watch, and down again to bet again.

   The two column wrappers collapse with display:contents so their children
   become direct children of the screen and can be ordered freely — the play
   area, then the controls, then everything that is only there to be read.
   Nothing here applies at 760px and up, where the two-column layout puts the
   controls beside the table anyway. */
@media (max-width: 759px) {
  .table-screen { gap: 14px; }
  .table-main, .table-side { display: contents; }

  .table-screen .machine     { order: 1; }
  .table-screen .controls    { order: 2; }
  .table-screen .session-bar { order: 3; }
  .table-screen .history     { order: 4; }
  .table-screen .paytable    { order: 5; }

  /* .table-side was the panel these sat in; with it collapsed each of them
     needs to be a card in its own right — otherwise they are the only blocks
     on the page not wearing one, and read as unstyled strips. */
  .table-screen .controls,
  .table-screen .session-bar,
  .table-screen .history {
    padding: 14px;
    background:
      radial-gradient(ellipse 90% 70% at 50% 0%, rgba(240,197,72,.07), transparent 70%),
      linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 55%, var(--blue-deep) 100%);
    border: 3px solid var(--gold-dim);
    border-radius: var(--radius);
  }
  .table-screen .controls { margin-top: 0; }
  /* Totals read as a footnote down here rather than a header, so they lose the
     bottom margin that separated them from controls that no longer follow. */
  .table-screen .session-bar { margin-bottom: 0; padding: 12px 14px; }

  /* Recent games belongs on a phone as much as anywhere. It used to be hidden
     outright down here, which made it the one card that simply vanished at
     this width — while crash's own list of past rounds, which lives in the
     main column, stayed. The rows take their natural height rather than
     stretching to fill a column that no longer exists. */
  .table-screen .history { display: flex; flex-direction: column; margin: 0; }
  .table-screen .history-rows { flex: none; overflow: visible; }
}

@media (min-width: 760px) {
  /* Both halves are one stretched row each, which is what keeps them level. */
  /* Every table screen, by class rather than by name. The old list of ids
     had to be edited by hand for each new table, and roulette was added
     without it — so it never got the two-column layout and stacked into
     one very tall page. */
  .table-screen {
    grid-template-areas: "machine side";
    grid-template-columns: 1.3fr .9fr;
    grid-template-rows: auto;
    align-items: stretch;
  }
  .table-main { grid-area: machine; }
  .table-side { grid-area: side; justify-content: center; }
  .paytable-row { grid-template-columns: 2rem repeat(4, 1fr); gap: 10px; }
  .pay-symbols { font-size: 1.25rem; }
  .pay-mult { font-size: .8rem; }
  /* All nine routes on one row once there is width for them. */
  .payline-list { grid-template-columns: repeat(9, 1fr); gap: 6px; }
  .payline-mini { grid-template-columns: repeat(5, 6px); grid-template-rows: repeat(3, 6px); }

  /* The side column runs totals at the top and controls at the bottom, which
     left a tall gap between them. The spin log fills it and shrinks with it. */
  .history { display: flex; flex-direction: column; flex: 1; min-height: 0; margin: 12px 0; }
  .history-row:nth-child(n+9) { display: none; }
}

/* How many of those rows there is room for depends on how TALL the window is,
   not just how wide. A capped-at-eight card is 284px, and in a short window
   that is enough to push the play button off the bottom — which is what a
   Discord panel is: wide and short. Fewer rows rather than a cut-off one, so
   the list always ends on a whole row. */
@media (min-width: 760px) and (max-height: 860px) {
  .history-row:nth-child(n+7) { display: none; }
}
@media (min-width: 760px) and (max-height: 780px) {
  .history-row:nth-child(n+5) { display: none; }
}
@media (min-width: 760px) and (max-height: 680px) {
  .history-row:nth-child(n+4) { display: none; }
}

@media (min-width: 760px) {
}

/* ── Sound settings ────────────────────────────────────────────── */
.sound-panel {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 60px);
  right: calc(env(safe-area-inset-right) + 14px);
  z-index: 40;
  width: 216px;
  padding: 14px;
  display: flex; flex-direction: column; gap: 12px;
  background: linear-gradient(180deg, var(--blue-lit), var(--blue-deep));
  border: 2px solid var(--gold-dim);
  border-radius: 12px;
  box-shadow: inset 0 0 0 1px rgba(240,197,72,.28), 0 12px 30px rgba(0,0,0,.55);
  animation: pillIn var(--fade) ease;
}
body.is-mobile .sound-panel { top: calc(env(safe-area-inset-top) + 110px); }

.slider-row {
  display: grid;
  grid-template-columns: 52px 1fr 28px;
  align-items: center; gap: 8px;
  cursor: pointer;
}
.slider-label {
  font-size: .70rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.slider-val {
  font-size: .78rem; font-weight: 700; color: var(--gold-lit);
  font-variant-numeric: tabular-nums; text-align: right;
}
/* Styled from scratch: the native range control looks like a form field, and
   this is a casino. Both engines need their own thumb selector. */
.slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 4px; border-radius: 3px; outline: none;
  background: linear-gradient(90deg, var(--gold) 0%, var(--gold-dim) 100%);
  cursor: pointer;
}
.slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--gold-lit);
  border: 2px solid var(--blue-deep);
  box-shadow: 0 0 8px rgba(240,197,72,.6);
  cursor: pointer;
}
.slider::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--gold-lit);
  border: 2px solid var(--blue-deep);
  box-shadow: 0 0 8px rgba(240,197,72,.6);
  cursor: pointer;
}

/* ── Session totals ────────────────────────────────────────────── */
/* Rebuilt as label/value rows rather than one packed line — two numbers side
   by side were competing for the same glance. */
.session-bar { flex-direction: column; gap: 6px; align-items: stretch; }
.session-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
.session-net, .session-spins {
  font-size: 1rem; font-weight: 800; color: var(--gold-lit);
  font-variant-numeric: tabular-nums;
}

/* The result glow runs longer so auto-spin does not clip it. */
.machine.win { animation-duration: 1.15s; }
.machine.lose { animation-duration: 1.15s; }

/* ── Auto run lengths ──────────────────────────────────────────── */
.auto-menu {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  margin-top: 10px;
  animation: pillIn 160ms ease;
}
.btn-auto-opt {
  min-height: 40px; min-width: 0;
  font-size: .78rem; font-weight: 800; letter-spacing: .04em;
  color: var(--gold-lit);
  padding: 0 4px;
}

/* Result glows, pushed harder toward their colours so a win and a loss are
   unmistakable at a glance rather than a tint on the cabinet. */
@keyframes winGlow {
  0%, 100% { box-shadow: inset 0 0 0 1px rgba(240,197,72,.35), inset 0 2px 26px rgba(0,0,0,.6), 0 10px 30px rgba(0,0,0,.45); }
  45% {
    box-shadow:
      inset 0 0 0 2px rgba(110,231,135,.9),
      inset 0 0 40px rgba(110,231,135,.35),
      0 0 60px rgba(110,231,135,.75),
      0 10px 30px rgba(0,0,0,.45);
  }
}
@keyframes loseGlow {
  0%, 100% { box-shadow: inset 0 0 0 1px rgba(240,197,72,.35), inset 0 2px 26px rgba(0,0,0,.6), 0 10px 30px rgba(0,0,0,.45); }
  45% {
    box-shadow:
      inset 0 0 0 2px rgba(255,107,107,.9),
      inset 0 0 40px rgba(255,107,107,.32),
      0 0 56px rgba(255,107,107,.68),
      0 10px 30px rgba(0,0,0,.45);
  }
}
/* The border colour shifts too — a shadow alone reads as a shadow. */
.machine.win { border-color: var(--win); }
.machine.lose { border-color: var(--lose); }
.machine { transition: border-color .3s ease; }

/* The number spinner's arrows sat over the text at this field's width, so the
   input is a plain text box with a decimal keypad instead. These rules keep
   any stray spinner out on browsers that add one anyway. */
.auto-cash-input::-webkit-outer-spin-button,
.auto-cash-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.auto-cash-input {
  -moz-appearance: textfield;
  appearance: textfield;
}

/* Auto bet sits beside the bet button and has to read as a toggle, not as a
   second way to bet right now. */
#crash-autobet {
  font-size: .82rem; font-weight: 800;
  min-height: 60px;
  padding: 0 16px;
  white-space: nowrap;
  flex: 0 0 auto;
}
#crash-autobet.on {
  background: linear-gradient(180deg, #f0c548, #b8860b);
  color: #1a1204;
  border-color: var(--gold-lit);
}

/* ── Blackjack ─────────────────────────────────────────────────── */
/* The play area. Aegis blue like every other table top, with the deck sitting
   off to one side so dealt cards have somewhere to come from. */
/* A player's picture, wherever they are named at a table. Shared by poker and
   blackjack.

   The initial underneath is not a placeholder to be tidied away later: bots
   never have a picture, a member may not have set one, and a picture that
   fails to load has to leave a face rather than a hole. All three land here,
   on a colour derived from the name so two people are never the same disc. */
.face {
  --face: 20px;
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--face); height: var(--face);
  border-radius: 50%;
  overflow: hidden;
  font-size: calc(var(--face) * 0.58);
  font-weight: 900; line-height: 1;
  color: #f7f3e8;
  background-color: hsl(var(--face-hue, 40) 38% 32%);
  background-size: cover; background-position: center;
  border: 1px solid rgba(240,197,72,.35);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.12);
  user-select: none;
}
/* Once a real picture is on it the initial would show through the transparent
   parts of a PNG, so it is taken away rather than covered up. */
.face.has-pic { color: transparent; }

/* A table rather than three rows stacked up. The dealer stands at the top, the
   seat the table is drawn around sits at the bottom, and everybody else fans
   between them on percentages of the felt — so the whole thing scales as one
   piece and reads as somewhere people are sat rather than as a list.

   Sized by aspect ratio for the same reason poker's is: the positions inside
   are percentages, and a percentage of a height that is itself decided by its
   contents is a circular measurement. */
.bj-felt {
  position: relative;
  /* WIDE AND SHALLOW, which is the shape a blackjack table actually is: the
     dealer stands along one long side and the players arc round the other.
     Two earlier goes at this were portrait — tall enough for the seats to
     curve in, but that left the felt taking half the window's height while the
     cards on it stayed small, so it read as a big empty box.

     A 2:1 table fills the width it is given, which is where the room is, and
     the arc has the whole of it to spread across.

     Sized by whichever runs out first: the column's width, or the height left
     once the controls, the result line and the page's own padding are taken
     out. A Discord panel is wide and short, so the height cap is what usually
     wins and the shape never changes either way. */
  container-type: size;
  --felt-h: clamp(260px, calc(100vh - 420px), 460px);
  aspect-ratio: 1.85 / 1;
  max-height: var(--felt-h);
  max-width: calc(var(--felt-h) * 1.85);
  width: 100%;
  margin: 0 auto;
  /* Everything on the felt is a share of the FELT, not of the window — cqw is
     a percent of the container's width. Whichever way the table was sized, the
     cards on it come out in proportion to it, which is what stopped happening
     when these were tied to the window's height instead. */
  --cell: clamp(30px, 8.2cqw, 60px);
  border-radius: 16px;
  padding: 14px 12px;
  background:
    radial-gradient(ellipse 70% 50% at 50% 0%, rgba(240,197,72,.06), transparent 70%),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 55%, var(--blue-deep) 100%);
  border: 1px solid rgba(212,160,23,.28);
  box-shadow: inset 0 0 30px rgba(0,0,0,.5);
  overflow: hidden;
}
.bj-side { padding: 4px 0; }
.bj-felt > .bj-dealer-box {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: min(96%, 460px);
}
/* Placed on the arc with everybody else rather than pinned to the bottom rail,
   and sized to its own contents so it takes no more of the felt than the cards
   in it need. See bjLayout.

   Built to the same pattern as every other chair — hand on top, plate under
   it, stake under that — because it IS one of them now. Only bigger: it is the
   hand being played, and it is the one that has to be readable without
   looking twice. */
.bj-felt > .bj-you-box {
  position: absolute;
  left: 50%; top: 71%;
  transform: translate(-50%, -50%);
  width: auto;
  /* Never wider than one chair's share of the arc — see bjLayout. Its hand is
     the biggest on the felt and at a full table it would otherwise lie across
     the two seats either side of it. */
  max-width: var(--you-slot, 34%);
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 0;
}
/* Half again the size of everybody else's, not three times. It is the hand
   being played and has to be the easiest to read, but a full table is six
   chairs on one arc and there is only so much table each. */
.bj-you-box #bj-player { --cell: calc(var(--seat-cell) * 1.55); }
.bj-you-box #bj-player .bj-card { --cell: calc(var(--seat-cell) * 1.55); }
/* Lapped on the same rule the seats use, so a long hand tightens rather than
   spilling into the chair beside it. */
.bj-you-box #bj-player > :nth-child(n+3) { margin-left: calc(var(--seat-cell) * -0.9); }
.bj-you-box #bj-player > :nth-child(n+5) { margin-left: calc(var(--seat-cell) * -1.15); }
.bj-you-box #bj-player { order: 1; }
.bj-you-box .bj-side-head {
  order: 2;
  margin: 0;
  max-width: 100%;
  padding: 5px 10px;
  border-radius: 10px;
  background: rgba(6,10,20,.86);
  border: 1px solid rgba(240,197,72,.35);
}
.bj-you-box #bj-you-bet { order: 3; }
.bj-you-box .bj-who { font-size: .74rem; }
/* Your turn, marked the same way a seat's is. */
.bj-side.turn .bj-side-head {
  border-color: var(--gold-lit);
  background: rgba(24,20,8,.92);
  animation: pkrWaiting 1.6s ease-in-out infinite;
}
/* Narrower than the felt so the box itself clears the shoe in the corner. Its
   contents are centred either way, so it costs nothing to look at. */
.bj-felt > .bj-dealer-box { width: min(82%, 440px); }
.bj-felt > .bj-dealer-box { top: 10px; }
.bj-you-face { --face: 22px; }
/* Taking the chair. Marked on the plate that has just become yours rather than
   by rescaling the whole felt — that read as the table trying to resize itself
   for a moment, which is not a flourish, it is a glitch. */
.bj-felt.seated .bj-you-box .bj-side-head { animation: bjSeated 700ms ease-out; }
@keyframes bjSeated {
  0%   { box-shadow: 0 0 0 0 rgba(240,197,72,.55); border-color: var(--gold-lit); }
  60%  { box-shadow: 0 0 0 10px rgba(240,197,72,0); border-color: var(--gold-lit); }
  100% { box-shadow: 0 0 0 0 rgba(240,197,72,0); }
}
.bj-side-head {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-bottom: 10px;
}
.bj-who {
  font-size: .70rem; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--gold-lit);
}
/* The running total gets its own pill — it is the number being read on every
   card, so it should not be a footnote beside the label. */
.bj-total {
  font-size: 1.15rem; font-weight: 900; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  background: rgba(4,8,18,.75);
  border: 1px solid rgba(212,160,23,.4);
  border-radius: 999px;
  padding: 6px 14px;
  min-width: 3.2rem;
  text-align: center;
  box-shadow: inset 0 1px 0 rgba(240,197,72,.15);
}
.bj-total.bust { color: var(--lose); border-color: rgba(255,107,107,.55); }
.bj-total.good { color: var(--win); border-color: rgba(110,231,135,.5); }
/* Hands sit centred on the table, not shoved against the left edge. */
/* Exactly one card tall, always, whether it holds six cards or none.
   It used to reserve 1.4 cells while a card is 1.46, so the table grew a few
   pixels the moment cards appeared and collapsed again when the sweep emptied
   it — the whole game visibly breathing on every hand. Wrapping is off for the
   same reason: a hand long enough to spill onto a second row would double the
   height mid-hand. */
.bj-hand {
  display: flex; gap: 7px; flex-wrap: nowrap;
  justify-content: center;
  height: calc(var(--cell) * 1.46);
  min-height: calc(var(--cell) * 1.46);
  align-items: center;
}
/* A long hand tightens up rather than growing the table. */
.bj-hand > :nth-child(n+6) { margin-left: calc(var(--cell) * -0.42); }
/* The gold rule that used to sit between the dealer's row and the player's is
   gone. The two rows are already separated by their own labels and the gap
   between them, and a hairline that only ever registered as a flicker on the
   way in was worse than no line at all. */

/* A card. Built like a real one — an index in opposite corners and a mark in
   the middle — rather than a rank floating in the centre of a rectangle.
   Sized off --cell so the table scales with the same knob the reels use. */
.bj-card {
  position: relative;
  flex: 0 0 auto;
  width: calc(var(--cell) * 1.02);
  min-width: calc(var(--cell) * 1.02);
  max-width: calc(var(--cell) * 1.02);
  height: calc(var(--cell) * 1.46);
  min-height: calc(var(--cell) * 1.46);
  max-height: calc(var(--cell) * 1.46);
  transform-origin: 50% 50%;
  border-radius: 8px;
  background: linear-gradient(170deg, #fdfbf4, #ece5d4);
  color: #16181d;
  box-shadow:
    0 4px 10px rgba(0,0,0,.6),
    inset 0 0 0 1px rgba(0,0,0,.14),
    inset 0 0 0 3px rgba(255,255,255,.6);
}
.bj-card.red { color: #c0223a; }

/* The corner index, and its mirror. A real card is readable when fanned, which
   is what the second, rotated copy is for. */
.bj-corner {
  position: absolute;
  display: flex; flex-direction: column; align-items: center;
  line-height: .95;
  font-weight: 900;
}
.bj-corner.tl { top: 5px; left: 6px; }
.bj-corner.br { bottom: 5px; right: 6px; transform: rotate(180deg); }
.bj-corner .r { font-size: calc(var(--cell) * 0.26); }
.bj-corner .s { font-size: calc(var(--cell) * 0.22); }
/* The blackjack felt sizes its cards off the window now, so at the small end a
   quarter of a card lands under the 11px this page holds everything to. A floor
   rather than a bigger card: the card is the size the table has room for, and
   the index has to stay readable inside it either way. */
.bj-hand .bj-corner .r { font-size: max(11px, calc(var(--cell) * 0.26)); }
.bj-hand .bj-corner .s { font-size: max(11px, calc(var(--cell) * 0.22)); }

/* The Aegis mark, watermarked into the middle of every face. */
.bj-mark {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 62%; height: auto;
  opacity: .42;
  pointer-events: none;
}

/* The hole card. There is nothing under this in the page — the value is not
   sent until the hand is over. */
.bj-card.down {
  background:
    repeating-linear-gradient(45deg, rgba(212,160,23,.10) 0 4px, transparent 4px 8px),
    linear-gradient(150deg, #1b2a4a, #0a1020);
  box-shadow:
    0 4px 10px rgba(0,0,0,.6),
    inset 0 0 0 1px rgba(212,160,23,.45),
    inset 0 0 0 3px rgba(212,160,23,.12);
}
.bj-card.down .bj-mark { opacity: .85; width: 52%; }

/* The deck. Sits on the table so dealt cards have a visible origin, drawn as a
   few stacked backs rather than one. */
/* The shoe, beside the dealer where one actually sits — it used to be level
   with the middle of the felt, which is now where people are sitting. Scaled
   off the felt like everything else on it. */
.bj-deck {
  position: absolute;
  top: 16px; right: 14px;
  transform: none;
  /* The same size as the cards that come off it. A pile smaller than its own
     cards makes every deal look like the card grew on the way out. */
  --cell: inherit;
  width: calc(var(--cell) * 1.02);
  height: calc(var(--cell) * 1.46);
  pointer-events: none;
}
/* Layer offsets and opacity are set inline by renderCardStack, the same as
   Higher or Lower's piles — the fan of hardcoded rotations that used to live
   here is what stopped the two decks sharing one shuffle. */
.bj-deck .bj-card {
  position: absolute; inset: 0;
  animation: none;
  width: 100%; height: 100%;
}

.bj-rules {
  display: flex; flex-direction: column; gap: 4px;
  font-size: .82rem; color: var(--muted);
}
.bj-rules strong { color: var(--gold-lit); }

/* ── Higher or Lower ───────────────────────────────────────────── */
/* Two card slots side by side: what is showing, and what you are calling.
   Same felt and same card as blackjack, so the two tables read as belonging
   to one casino rather than two separate builds.

   The cards run bigger here than at blackjack for a simple reason: there are
   two of them rather than up to twelve, so there is room, and the whole game
   is reading one card against another. --cell is the single knob every card
   measurement derives from, so raising it scales the card, its corners and
   the deck together. */
#screen-highlow { --cell: 74px; }

.hl-felt {
  position: relative;
  border-radius: 12px;
  padding: 18px 12px 14px;
  background:
    radial-gradient(ellipse 70% 50% at 50% 0%, rgba(240,197,72,.06), transparent 70%),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 55%, var(--blue-deep) 100%);
  border: 1px solid rgba(212,160,23,.28);
  box-shadow: inset 0 0 30px rgba(0,0,0,.5);
  overflow: hidden;
}
/* Two piles, side by side and level. Each is a fixed-size box that stacked
   cards are absolutely positioned inside, so a pile getting thicker or thinner
   never moves anything around it. */
.hl-piles {
  display: flex; align-items: flex-start; justify-content: center;
  gap: calc(var(--cell) * 0.7);
}
.hl-pile { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.hl-pile-label {
  font-size: .74rem; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--gold-lit);
}
.hl-pile-count {
  font-size: .78rem; font-weight: 700; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* The box a pile lives in. Depth is drawn by offsetting each layer up and
   left, so a thick deck stands proud of the felt and a thin one sits flat. */
.hl-stack {
  position: relative;
  width: calc(var(--cell) * 1.02);
  height: calc(var(--cell) * 1.46);
}
.hl-stack .bj-card {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  animation: none;
}
/* An exhausted pile: the outline of where cards were. Shown when the deck runs
   out, and before the first card is turned. */
.hl-stack.empty::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: 8px;
  border: 1px dashed rgba(240,197,72,.22);
  background: rgba(4,8,18,.22);
}
/* The card in flight from one pile to the other. Positioned against the felt
   so it can travel between them, and above both while it does. */
.hl-flier { position: absolute; z-index: 20; pointer-events: none; }

/* Two lines held open. This table's messages are the longest in the casino —
   "Drew K♥ — a tie, so it pushes. Call again" wraps on a phone — and a line
   appearing or disappearing moved everything under it between calls. */
#screen-highlow .result { min-height: 2.8em; }

/* The other seats at the blackjack table ────────────────────
   A strip rather than a felt: your own hand keeps the big cards and the deal
   animation, and everybody else is a summary. That is the right weighting —
   what the seat beside you is holding matters, but not as much as what you
   are. The whole block is hidden when you are on your own. */
.bj-seats { position: absolute; inset: 0; pointer-events: none; }
/* A seat at the table: the hand on top, a name plate under it. No box round
   the lot — a heavy little card per player is what this replaced, and it read
   as a list of players rather than as people sat at a table.

   Only chairs somebody is IN are drawn, so the fan is always as wide as it
   needs to be and no wider. */
.bj-seat {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  /* Never wider than the gap between two chairs, or the fan overlaps itself.
     Five chairs sit 18% of the felt's width apart, so this has to stay under
     that — 15.5 leaves a little air between them. */
  width: clamp(66px, 16.4cqw, 138px);
  pointer-events: auto;
}
/* Whose turn it is, breathing while it waits so the strip never looks frozen,
   and struck once as the turn arrives so it is something you see happen. */
.bj-seat.turn .bj-seat-head {
  border-color: var(--gold-lit);
  background: rgba(24,20,8,.9);
  animation: pkrWaiting 1.6s ease-in-out infinite;
}
/* Whose turn it is, said in the one way that cannot be missed: a marker over
   the seat, pointing at it. The gold border alone was too easy to read as
   decoration on a table where everything has a gold border. */
.bj-seat.turn::after,
.bj-side.turn::after {
  content: '';
  position: absolute; left: 50%; top: -10px;
  width: 0; height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 9px solid var(--gold-lit);
  filter: drop-shadow(0 2px 4px rgba(0,0,0,.5));
  animation: bjPoint 1.15s ease-in-out infinite;
}
@keyframes bjPoint {
  0%, 100% { transform: translate(-50%, 0); }
  50%      { transform: translate(-50%, 4px); }
}
.bj-side-head { position: relative; }
/* Your own row says it in words as well. There are no turns to wait for on a
   table of one, so it only ever appears with company. */
.bj-turn-pill {
  font-size: .72rem; font-weight: 900; letter-spacing: .1em;
  text-transform: uppercase; color: #221800;
  background: linear-gradient(180deg, var(--gold-lit), var(--gold));
  border-radius: 999px; padding: 3px 9px;
  animation: pkrWaiting 1.6s ease-in-out infinite;
}
.bj-seat.struck { animation: pkrStruck 420ms cubic-bezier(.2,.9,.3,1.2); }
/* The card they just drew, rather than the whole hand again. */
.bj-seat-cards .bj-card.drew { animation: bjDrew 460ms cubic-bezier(.2,.9,.3,1.15); }
@keyframes bjDrew {
  0%   { transform: translateY(-8px) scale(.7); opacity: 0; }
  60%  { transform: translateY(1px) scale(1.1); opacity: 1; }
  100% { transform: none; opacity: 1; }
}
/* Its own keyframes rather than poker's: that one composes a translateX(-50%)
   for an absolutely centred label, and this one sits in normal flow. */
.bj-seat-foot.settled { animation: bjSettled 620ms ease-out; }
@keyframes bjSettled {
  0%   { transform: scale(.75); opacity: 0; }
  30%  { transform: scale(1.12); opacity: 1; }
  100% { transform: none; opacity: 1; }
}
.bj-seat.busted, .bj-seat.lost { opacity: .58; }
.bj-seat.won .bj-seat-head { border-color: rgba(110,231,135,.5); }
.bj-seat.push .bj-seat-head { border-color: rgba(255,255,255,.24); }
/* The plate. Never wider than its seat, for the same reason poker's is not:
   a long name would otherwise push the seat sideways into its neighbour. */
.bj-seat-head {
  order: 2;
  display: flex; align-items: center; gap: 6px;
  max-width: 100%;
  padding: 4px 8px;
  border-radius: 9px;
  background: rgba(6,10,20,.86);
  border: 1px solid rgba(255,255,255,.10);
}
.bj-seat-slot { order: 1; }
.bj-seat-foot { order: 3; }
.bj-seat .face { --face: 20px; }
.bj-seat-name {
  flex: 0 1 auto; min-width: 0;
  font-size: .82rem; font-weight: 800; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bj-seat-total {
  flex: 0 0 auto;
  font-size: .92rem; font-weight: 900; color: var(--gold-lit);
  font-variant-numeric: tabular-nums;
}
/* The cards get a slot of a fixed height whether there are any in it or not,
   so a seat does not jump when a hand arrives and shrink when the sweep clears
   it — the whole table used to breathe on every deal. */
.bj-seat-slot {
  position: relative;
  height: calc(var(--seat-cell) * 1.46);
  display: flex; align-items: center; justify-content: center;
}
/* Real cards, now that the strip runs the whole width of the felt. They used to
   be the rank in a little box, on the grounds that six seats of drawn cards
   would crowd out the hand you are actually playing — which was true of a strip
   squeezed into the middle and is not true of one that uses the table. */
.bj-seat-cards {
  display: flex; gap: 3px; flex-wrap: nowrap; align-items: center;
  max-width: 100%;
}
/* Scaled off the felt like everything else on it, and named separately so the
   slot above and the laps below are all measured against the same number. */
.bj-seats,
.bj-you-box { --seat-cell: clamp(19px, 4.5cqw, 36px); }
.bj-seat-cards .bj-card { --cell: var(--seat-cell); }
/* A seat's cards are small at every size, so their index is set outright at the
   11px this page holds itself to rather than scaled — four tenths of a card
   this size lands under it. The mirrored corner and the watermark go with it:
   on a card this small they are texture, and the index is the only part of it
   anybody reads. */
.bj-seat-cards .bj-corner.br { display: none; }
.bj-seat-cards .bj-mark { display: none; }
.bj-seat-cards .bj-corner.tl { top: 2px; left: 3px; }
.bj-seat-cards .bj-corner .r,
.bj-seat-cards .bj-corner .s { font-size: 11px; }
/* A hand that keeps hitting tightens up rather than pushing out of its chair,
   and tightens further the longer it gets. Each extra card laps the one before
   it from the RIGHT, and the index it has to be read by is in the top left, so
   an overlapped hand stays legible however far it is squeezed.

   The tiers are what stop a six-card hand leaving its chair and landing in the
   one next door: two cards at full width, then well under a third each. */
.bj-seat-cards > :nth-child(n+3) { margin-left: calc(var(--seat-cell) * -0.70); }
.bj-seat-cards > :nth-child(n+5) { margin-left: calc(var(--seat-cell) * -0.85); }
.bj-seat-waiting {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  font-size: .78rem; color: var(--muted); font-style: italic;
}
.bj-seat-foot {
  min-height: 1em;
  font-size: .76rem; font-weight: 700; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* Leave sits under the Hit/Stand/Double row and needs the same breathing room
   Mines gives its cash-out and Russian Roulette its bank. */
#bj-stand-up-row { margin-top: 10px; }

/* What the table is doing when it is not waiting on you. Sits where the play
   buttons sit, so the panel never goes blank between turns. */
.bj-wait {
  display: flex; align-items: center; justify-content: center;
  min-height: 46px; margin-bottom: 10px;
  border-radius: 12px;
  background: rgba(8,12,24,.55);
  border: 1px solid rgba(240,197,72,.22);
}
.bj-wait-text {
  font-size: .84rem; font-weight: 800; letter-spacing: .04em;
  color: var(--gold-lit);
  animation: crashRiding 1.9s ease-in-out infinite;
}

.bj-table-line {
  margin: 6px 0 0; text-align: center;
  font-size: .74rem; font-weight: 700; letter-spacing: .04em; color: var(--muted);
}

/* Not 759px, where the phone rules usually start. At 760 the page splits into
   two columns and the felt gets the narrower of them — about 420px, which is
   less than a phone gets. Six chairs of drawn cards do not fit in that, so the
   strip goes back to phone behaviour for the whole band. */
/* Not 759px, where the phone rules usually start. At 760 the page splits into
   two columns and the felt gets the narrower of them — about 420px, which is
   less than a phone gets. Everything on the table has to come down a size for
   that whole band, not just for phones. */
@media (max-width: 1000px) {
  .bj-felt { aspect-ratio: 10 / 13; }
  /* Narrower than the felt so the box itself clears the shoe in the corner —
     its contents are centred either way, so it costs nothing to see. */
  .bj-felt > .bj-dealer-box { width: 72%; }
  .bj-felt > .bj-you-box { width: 98%; }
  /* Squarer, not portrait: the narrow half of a two-column layout is about
     420px across, and a 2:1 table in that is 210px tall — too shallow for an
     arc to sit in. Two rows of chairs rather than one, so each row has half as
     many to fit across it. See bjSeatPos. */
  .bj-felt {
    /* Taller than it is wide here. Two rows of chairs, a dealer above them and
       the hand being played below is more than a square has room for. */
    aspect-ratio: 1 / 1.2;
    --felt-h: clamp(320px, calc(100vh - 400px), 520px);
    max-width: calc(var(--felt-h) / 1.2);
    --cell: clamp(24px, 9cqw, 40px);
  }
  .bj-seats { --seat-cell: clamp(17px, 5.4cqw, 26px); }
  .bj-seat { width: clamp(58px, 21cqw, 92px); }
  .bj-seat .face { --face: 19px; }
  .bj-seat-head { gap: 4px; padding: 3px 5px; }
  /* Six chairs across a phone cannot carry names as well as hands — five of
     them at a readable size is wider than the felt. The face is the identity
     at this width, and the line under the table names whoever is being waited
     on, which is the name that actually matters. */
  .bj-seat-name { display: none; }
  .bj-seat-total { font-size: .78rem; }
  .bj-seat-slot { height: calc(20px * 1.46); }
  /* Chairs are narrower here and a hand can still run to six cards, so past
     the second they lap each other almost completely — a sliver apiece, enough
     to count and no more. The total on the plate is what is read at this size,
     and the alternative is a long hand reaching into the seat next to it. */
  .bj-seat-cards { gap: 1px; }
  .bj-seat-cards > :nth-child(n+3) { margin-left: calc(var(--seat-cell) * -0.86); }
  .bj-seat-cards > :nth-child(n+5) { margin-left: calc(var(--seat-cell) * -0.92); }
  .bj-seat-waiting, .bj-seat-foot { font-size: .7rem; }
  .bj-deck { --cell: 28px; top: 10px; right: 8px; }
}

/* Poker puts the money UNDER the table and the reference beside it. A player
   looks at the board and then at what it costs, and those two were at opposite
   ends of the screen; what beats what is a reference and belongs where a
   reference goes. Only at the width where there are two columns to arrange —
   on a phone the running order is set by the shared mobile rules and is
   already table, controls, everything else. */
@media (min-width: 760px) {
  /* Both card tables are what people are looking at, so they take more of the
     width than the machines do. The side column only carries the stack, the
     blinds and a reference now that the hand history has gone. */
  #screen-blackjack.table-screen { grid-template-columns: 1.55fr .85fr; }
  #screen-poker.table-screen {
    grid-template-columns: 1.6fr .8fr;
    /* The side column stops where its content does. Stretched to match the
       table it was a tall panel with a reference at the top and nothing under
       it, which reads as something failing to load. */
    align-items: start;
  }
  #screen-poker .table-main .controls { margin-top: 0; padding: 14px; }
  #screen-poker .table-side { align-self: start; }
}

/* Poker ──────────────────────────────────────────────
   The felt is a fixed shape and everything on it is placed by percentage, so
   the whole table scales as one thing. Note the aspect-ratio rather than a
   height: a percentage height resolves against a parent that has none, which
   has flattened three stages in this file already. */
/* Sitting down and playing share the controls panel, one or the other. */
#pkr-sit-box[hidden], #pkr-play-box[hidden] { display: none; }

.pkr-field-label {
  display: block; margin: 14px 0 6px;
  font-size: .72rem; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.pkr-buyin { margin: 6px 0 0; font-size: .8rem; color: var(--muted); text-align: center; }
.pkr-buyin strong { color: var(--gold-lit); }
.pkr-bot-row { display: flex; gap: 8px; margin-bottom: 12px; }

/* The felt. One oval, six pods around it, everything else in the middle. */
.pkr-machine { padding: 12px; }
.pkr-felt {
  --cell: 46px;
  position: relative;
  width: 100%;
  /* A little taller than it looks like it needs to be. Six pods have to clear
     the board AND the deck vertically, and at 15/10 a showdown pod — which
     grows a hand-name line — reached the deck. */
  aspect-ratio: 15 / 11;
  margin: 0 auto;
  border-radius: 46% / 42%;
  background:
    radial-gradient(ellipse 60% 55% at 50% 45%, rgba(255,255,255,.06), transparent 70%),
    radial-gradient(ellipse 80% 70% at 50% 50%, #1f6b46 0%, #14512f 55%, #0e3a22 100%);
  border: 10px solid #2a1b10;
  box-shadow:
    inset 0 0 60px rgba(0,0,0,.55),
    0 0 0 2px rgba(212,160,23,.35),
    0 14px 34px rgba(0,0,0,.5);
}

.pkr-middle {
  position: absolute;
  left: 50%; top: 46%;
  transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  width: 78%;
}
.pkr-pot {
  display: flex; align-items: baseline; gap: 7px;
  padding: 4px 12px;
  border-radius: 999px;
  background: rgba(8,12,24,.55);
  border: 1px solid rgba(212,160,23,.35);
}
.pkr-pot-label {
  font-size: .7rem; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.pkr-pot-value {
  font-size: 1.15rem; font-weight: 900; color: var(--gold-lit);
  font-variant-numeric: tabular-nums;
  display: inline-block;
}
/* The pot growing. Bets land in front of their seats and then arrive here at
   the end of the street, so this is the other half of that movement. */
.pkr-pot-value.grew { animation: pkrPot 460ms ease-out; }
@keyframes pkrPot {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.25); color: #fff2c9; }
  100% { transform: scale(1); }
}
.pkr-board { display: flex; gap: 6px; justify-content: center; }
.pkr-slot {
  width: calc(var(--cell) * 1.02);
  height: calc(var(--cell) * 1.46);
  border-radius: 8px;
  border: 1px dashed rgba(255,255,255,.16);
  background: rgba(0,0,0,.16);
}
.pkr-street {
  margin: 0;
  font-size: .7rem; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: rgba(255,255,255,.5);
}
/* A board card arriving, rather than the whole board re-dealing itself every
   time the state is polled. */
.pkr-dealt { animation: pkrDeal 260ms ease-out both; }
@keyframes pkrDeal {
  from { opacity: 0; transform: translateY(-10px) scale(.9); }
  to   { opacity: 1; transform: none; }
}

/* The deck every card is dealt off, sitting where a dealer's shoe sits: at
   the left edge, level with the middle of the table.
   
   It was on the centre line above the pot, which worked only while the cards
   were small — once they grew there was no longer a band tall enough between
   the top player's plate and the pot to hold it. The left edge at half height
   is the one spot that is clear at both widths, because the seat ring leaves
   its widest gap exactly there.
   
   Riffles between hands using the same keyframes blackjack's deck does, which
   is why both piles are built by renderCardStack rather than written out
   twice. */
.pkr-deck {
  position: absolute;
  left: 13%; top: 50%;
  transform: translate(-50%, -50%);
  /* Its own size, smaller than the board's. A deck of backs carries no text
     to read, and every pixel of it is a pixel closer to the two pods either
     side of it. */
  --cell: 32px;
  width: calc(var(--cell) * 1.02);
  height: calc(var(--cell) * 1.46);
  pointer-events: none;
  z-index: 1;
}
.pkr-deck .bj-card {
  position: absolute; inset: 0;
  animation: none;
  width: 100%; height: 100%;
}

/* A seat. Placed by JS as a percentage of the felt and pulled back onto its
   own centre, the same way the roulette pockets and the revolver chambers
   are — one rule that holds at any size. */
.pkr-seat {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  /* The plate can be no wider than its seat, so this is what decides how much
     of a name fits. The felt got wider when poker took more of the page. */
  width: 126px;
  padding: 0;
}
.pkr-seat.empty .pkr-seat-empty {
  font-size: .72rem; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: rgba(255,255,255,.28);
  padding: 8px 10px;
  border: 1px dashed rgba(255,255,255,.16);
  border-radius: 9px;
}
.pkr-hole { display: flex; gap: 3px; min-height: 1px; }
.pkr-hole .bj-card { --cell: 30px; }
/* A card that is face UP has to be readable, and a card that is face down is
   just a back — so the two are sized differently rather than the whole felt
   being scaled to the worst case. An opponent's cards are only ever face up
   at a showdown, which is exactly when you need to read them.  */
.pkr-hole .bj-card:not(.down) { --cell: 36px; }
/* Your own two are the pair you read every hand, so they are the largest. */
.pkr-seat.you .pkr-hole { gap: 4px; }
.pkr-seat.you .pkr-hole .bj-card { --cell: 56px; }
/* Poker deals two cards where blackjack deals five, so the corner index can
   afford to be a bigger share of the card than the shared rule allows. */
.pkr-card .bj-corner .r { font-size: calc(var(--cell) * 0.40); }
.pkr-card .bj-corner .s { font-size: calc(var(--cell) * 0.40); }
/* Never wider than the pod it sits in. Without the cap a long hand name at a
   showdown — "Three of a Kind" — made the plate wider than the seat, and the
   pod's box grew sideways into the pot. */
.pkr-plate {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 5px 10px;
  min-width: 88px;
  max-width: 100%;
  border-radius: 8px;
  background: rgba(6,10,20,.82);
  border: 1px solid rgba(255,255,255,.10);
}
.pkr-seat.you .pkr-plate { border-color: rgba(240,197,72,.55); }
/* Hung off the left edge of the plate rather than sitting inside it. In the
   name row it competed with the name and the BOT tag for a plate that is
   capped at the width of its seat, and every bot ended up called "V..". Out
   here it costs the plate nothing at all. */
.pkr-face {
  --face: 24px;
  position: absolute;
  left: 0; top: 50%;
  transform: translate(-55%, -50%);
  z-index: 2;
}
/* Whose turn it is, which is the one thing on the felt that has to be
   readable at a glance from across the table. It breathes while it waits, so
   the table never looks frozen between somebody's decisions — and it is
   struck once as the turn arrives, so a turn moving round the table is
   something you SEE happen rather than something you notice has happened. */
.pkr-seat.acting .pkr-plate {
  border-color: var(--gold-lit);
  animation: pkrWaiting 1.6s ease-in-out infinite;
}
@keyframes pkrWaiting {
  0%, 100% { box-shadow: 0 0 0 2px rgba(240,197,72,.22), 0 0 12px rgba(240,197,72,.28); }
  50%      { box-shadow: 0 0 0 3px rgba(240,197,72,.42), 0 0 22px rgba(240,197,72,.55); }
}
.pkr-plate.struck { animation: pkrStruck 420ms cubic-bezier(.2,.9,.3,1.2); }
@keyframes pkrStruck {
  0%   { transform: scale(1); box-shadow: 0 0 0 0 rgba(240,197,72,.7); }
  40%  { transform: scale(1.09); box-shadow: 0 0 0 6px rgba(240,197,72,0); }
  100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(240,197,72,0); }
}

/* Chips arriving in a stack. Chips leaving are already shown by the bet
   going out in front of the seat, so only the way in is marked. */
.pkr-seat-stack.up { animation: pkrStackUp 520ms ease-out; }
@keyframes pkrStackUp {
  0%   { transform: scale(1); color: var(--gold-lit); }
  35%  { transform: scale(1.22); color: #6ee787; }
  100% { transform: scale(1); color: var(--gold-lit); }
}

/* The seat that took it. */
.pkr-seat.won .pkr-plate {
  border-color: #6ee787;
  animation: pkrWon 1.1s ease-out;
}
@keyframes pkrWon {
  0%   { box-shadow: 0 0 0 0 rgba(110,231,135,.75); }
  70%  { box-shadow: 0 0 0 14px rgba(110,231,135,0); }
  100% { box-shadow: 0 0 0 0 rgba(110,231,135,0); }
}
.pkr-seat.out { opacity: .45; }
.pkr-name { display: flex; align-items: center; gap: 4px; max-width: 100%; min-width: 0; }
.pkr-name-text {
  font-size: .84rem; font-weight: 800; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 84px;
}
/* Bots are marked, always. Somebody should never be unsure whether the seat
   taking their money is a person. */
.pkr-bot-tag {
  flex: 0 0 auto;
  font-size: .7rem; font-weight: 900; letter-spacing: .04em;
  padding: 1px 4px; border-radius: 4px;
  background: rgba(240,197,72,.16); color: var(--gold-lit);
  border: 1px solid rgba(240,197,72,.3);
}
.pkr-seat-stack {
  font-size: .88rem; font-weight: 900; color: var(--gold-lit);
  font-variant-numeric: tabular-nums; line-height: 1;
}
.pkr-seat-hand {
  font-size: .7rem; font-weight: 700; color: #6ee787;
  text-transform: uppercase; letter-spacing: .02em;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Below the plate rather than beside it: at the right edge it sat on top of
   the BOT tag and clipped the name. */
.pkr-dealer {
  position: absolute; right: -4px; bottom: -7px;
  width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: .7rem; font-weight: 900; color: #221800;
  background: linear-gradient(180deg, #fff, #d9d2c0);
  box-shadow: 0 2px 6px rgba(0,0,0,.5);
}
/* A bet going out in front of the seat. It lands rather than appears, which
   is what makes a bot betting something you can see happen.

   The keyframes compose onto --rest rather than naming a transform of their
   own, because your own seat parks its chip somewhere different from
   everybody else's and a bare transform in here would throw it back to the
   middle for the length of the animation. Same trick the card stack uses. */
.pkr-committed.bumped { animation: pkrChip 420ms cubic-bezier(.2,.9,.3,1.15); }
@keyframes pkrChip {
  0%   { transform: var(--rest) translateY(-14px) scale(.6); opacity: 0; }
  60%  { transform: var(--rest) translateY(2px) scale(1.12); opacity: 1; }
  100% { transform: var(--rest) translateY(0) scale(1); opacity: 1; }
}
.pkr-committed {
  --rest: translateX(-50%);
  position: absolute; left: 50%; bottom: -20px;
  transform: var(--rest);
  font-size: .74rem; font-weight: 900; color: var(--gold-lit);
  font-variant-numeric: tabular-nums;
  padding: 1px 7px; border-radius: 999px;
  background: rgba(8,12,24,.85); border: 1px solid rgba(212,160,23,.4);
  white-space: nowrap;
}
/* What they just did, said once and briefly enlarged so it reads as an
   announcement rather than a label that was always there. */
.pkr-last.said { animation: pkrSaid 620ms ease-out; }
@keyframes pkrSaid {
  0%   { transform: translateX(-50%) scale(.7); opacity: 0; }
  25%  { transform: translateX(-50%) scale(1.15); opacity: 1; }
  100% { transform: translateX(-50%) scale(1); opacity: 1; }
}
.pkr-last {
  position: absolute; left: 50%; top: -16px;
  transform: translateX(-50%);
  font-size: .7rem; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: rgba(255,255,255,.65);
  white-space: nowrap;
}
/* Your own seat sits on the bottom rail, so anything hung UNDER it lands off
   the felt entirely and on top of the line that says whose move it is. Both
   badges go out beside the plate instead: the bet to the right, the button to
   the left of your face. */
.pkr-seat.you .pkr-committed {
  --rest: translate(calc(100% + 8px), -50%);
  left: auto; right: 0; top: 50%; bottom: auto;
}
.pkr-seat.you .pkr-dealer {
  right: auto; left: -46px; bottom: 50%;
  transform: translateY(50%);
}
.pkr-last.fold { color: #ff8b8b; }

/* The clock on your own decision. */
.pkr-clock { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.pkr-clock-bar {
  flex: 1; height: 5px; border-radius: 999px;
  background: rgba(8,12,24,.7); overflow: hidden;
}
.pkr-clock-bar span {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--gold), var(--gold-lit));
  transition: width .2s linear;
}
.pkr-clock-bar span.urgent { background: linear-gradient(90deg, #a32e26, #e8635a); }
.pkr-clock-text {
  font-size: .72rem; font-weight: 800; color: var(--muted);
  font-variant-numeric: tabular-nums; min-width: 26px; text-align: right;
}

/* Raising. The slider is the fine control and the pot fractions are how the
   size is actually chosen. */
.pkr-raise { margin-bottom: 10px; }
.pkr-raise-top { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.pkr-raise-label {
  font-size: .72rem; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.pkr-raise-amt {
  flex: 1; min-width: 0;
  font-size: 1.05rem; font-weight: 900; color: var(--gold-lit);
  font-variant-numeric: tabular-nums; text-align: right;
  background: rgba(8,12,24,.75);
  border: 1px solid rgba(212,160,23,.3);
  border-radius: 9px; padding: 5px 9px;
}
.pkr-slider {
  width: 100%; height: 22px; margin: 0 0 8px;
  -webkit-appearance: none; appearance: none; background: none;
}
.pkr-slider::-webkit-slider-runnable-track {
  height: 6px; border-radius: 999px;
  background: linear-gradient(90deg, rgba(212,160,23,.5), rgba(212,160,23,.15));
}
.pkr-slider::-moz-range-track {
  height: 6px; border-radius: 999px;
  background: linear-gradient(90deg, rgba(212,160,23,.5), rgba(212,160,23,.15));
}
.pkr-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; margin-top: -6px;
  border-radius: 50%;
  background: linear-gradient(180deg, var(--gold-lit), var(--gold));
  border: 1px solid rgba(0,0,0,.4);
  box-shadow: 0 2px 6px rgba(0,0,0,.5);
}
.pkr-slider::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%;
  background: linear-gradient(180deg, var(--gold-lit), var(--gold));
  border: 1px solid rgba(0,0,0,.4);
}

.pkr-actions { display: flex; gap: 8px; margin-bottom: 10px; }
.pkr-actions .btn-act { flex: 1; min-width: 0; font-size: .84rem; }
.pkr-fold {
  background: linear-gradient(180deg, #4a2320 0%, #331715 100%);
  border-color: rgba(232,99,90,.4); color: #ffd9d5;
}
.pkr-check {
  background: linear-gradient(180deg, #1f5d3d 0%, #14402a 100%);
  border-color: rgba(110,231,135,.35); color: #d8ffe4;
}
.pkr-raise-btn {
  background: linear-gradient(180deg, var(--gold-lit), var(--gold));
  border-color: var(--gold-lit); color: #221800;
}
.pkr-actions .btn-act:disabled {
  background: linear-gradient(180deg, var(--blue-lit), var(--blue));
  color: var(--muted); border-color: rgba(212,160,23,.25); box-shadow: none;
}

.pkr-ranks { display: flex; flex-direction: column; gap: 3px; }
.pkr-rank-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 5px 9px; border-radius: 7px;
  background: rgba(8,12,24,.45);
  font-size: .78rem;
}
.pkr-rank-name { font-weight: 800; color: var(--gold-lit); }
.pkr-rank-note { color: var(--muted); font-size: .74rem; text-align: right; }
/* What you are actually holding, and what still beats it. The rows BELOW your
   hand fade rather than vanish: knowing what you have already got past is
   half of reading the board. */
.pkr-rank-row.you {
  background: linear-gradient(90deg, rgba(240,197,72,.20), rgba(240,197,72,.06));
  border: 1px solid rgba(240,197,72,.55);
  box-shadow: 0 0 12px rgba(240,197,72,.18);
}
.pkr-rank-row.you .pkr-rank-name { color: #fff2c9; }
.pkr-rank-row.you .pkr-rank-note { color: var(--gold-lit); font-weight: 800; }
.pkr-rank-row.below { opacity: .42; }

/* Up to 1000px rather than up to a phone: at 760 the page becomes two columns
   and the felt gets about 420px of the width, which is less room than a phone
   gives it. The landscape oval put the board through the seats and the deck
   through the board for that whole band. pkrSeatPos carries the same number —
   the geometry and the shape it is drawn on have to agree. */
@media (max-width: 1000px) {
  /* The felt goes taller than it is wide so six pods still have room to sit
     round it on a phone. */
  /* Taller than it is wide, and taller than it used to be: six pods and a
     five-card board do not fit a 10/12 felt once the cards are big enough to
     read. See pkrSeatPos for how little room there is to play with. */
  .pkr-felt {
    --cell: 28px;
    aspect-ratio: 10 / 14;
    border-width: 7px;
    border-radius: 40% / 26%;
  }
  .pkr-seat { width: 74px; }
  .pkr-hole .bj-card { --cell: 20px; }
  .pkr-hole .bj-card:not(.down) { --cell: 28px; }
  .pkr-seat.you .pkr-hole .bj-card { --cell: 42px; }
  .pkr-plate { min-width: 66px; padding: 3px 5px; }
  .pkr-name-text { max-width: 52px; font-size: .74rem; }
  /* The plate cannot be wider than its seat, and on a portrait felt a seat is
     74px, so its padding is trimmed to give the name back what it can. The BOT
     tag is NOT shrunk to buy more: it is already close to the 11px floor, and
     a bot marking nobody can read is worse than a name that ends in a dot. */
  .pkr-bot-tag { letter-spacing: 0; }
  .pkr-seat-stack { font-size: .78rem; }
  /* Pulled in close, because every pixel a badge hangs outside the plate is a
     pixel of the pod's box reaching toward the board. */
  .pkr-last { top: -13px; }
  .pkr-committed { bottom: -14px; }
  .pkr-face { --face: 19px; transform: translate(-42%, -50%); }
  /* Your bet still goes out to the right of the plate — there is nothing
     beside the bottom seat at that height on a portrait felt, and below it is
     off the table. The button does NOT: out to the left it reached into the
     seat above you, which on this felt is much closer than on the wide one, so
     it goes back to the corner of the plate where it fits. */
  .pkr-seat.you .pkr-dealer {
    left: auto; right: -2px; bottom: -5px;
    transform: none;
  }
  .pkr-deck { --cell: 24px; }
  .pkr-middle { width: 92%; gap: 6px; }
  .pkr-board { gap: 3px; }
  .pkr-dealer { width: 17px; height: 17px; font-size: .7rem; right: -2px; bottom: -5px; }
  .pkr-committed { bottom: -17px; font-size: .7rem; padding: 1px 5px; }
  .pkr-actions .btn-act { font-size: .78rem; padding: 10px 4px; }
  .pkr-rank-row { font-size: .74rem; }
}

/* Russian Roulette ──────────────────────────────────────────────────
   A cylinder that turns one chamber per pull under a fixed hammer. Chambers
   are placed around the rim by angle from JS, so any cylinder from two to
   twelve is laid out by the same rule. */
.rr-field {
  --rr-cyl: min(100%, 260px);
  padding: 14px;
  border-radius: 12px;
  background:
    radial-gradient(ellipse 70% 60% at 50% 0%, rgba(240,197,72,.07), transparent 70%),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 60%, var(--blue-deep) 100%);
  border: 1px solid rgba(212,160,23,.28);
}
.rr-cyl-stage {
  position: relative;
  width: var(--rr-cyl);
  /* NOT height: var(--rr-cyl). That variable carries a percentage, and a
     percentage height resolves against a parent that has none — the stage came
     out zero tall and the cylinder drew itself across the whole screen. Same
     trap the plinko board fell into; an aspect ratio takes the height from the
     width instead. */
  aspect-ratio: 1;
  margin: 0 auto;
}
.rr-cyl {
  position: absolute; inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(circle at 38% 32%, rgba(255,255,255,.1), transparent 55%),
    conic-gradient(from 210deg, #2b3242, #1a202c 30%, #2b3242 60%, #161b25 100%);
  border: 3px solid rgba(212,160,23,.45);
  box-shadow: inset 0 0 30px rgba(0,0,0,.7), 0 10px 26px rgba(0,0,0,.5);
  /* Turned by JS one chamber at a time. The long ease is what makes it read
     as a cylinder indexing round rather than a list jumping to a value. */
  transition: transform .6s cubic-bezier(.2,.9,.2,1);
}
/* The extra half-turn on a pull, so the action is visible even when the
   cylinder only advances one chamber of twelve. */
.rr-cyl.turning { transition: transform .6s cubic-bezier(.3,.7,.2,1); }

.rr-chamber {
  position: absolute;
  width: 17%; height: 17%;
  margin: -8.5% 0 0 -8.5%;
  border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, #0d1119, #05070b 70%);
  border: 2px solid rgba(255,255,255,.14);
  box-shadow: inset 0 3px 8px rgba(0,0,0,.9);
  transition: background .2s ease, border-color .2s ease, box-shadow .2s ease;
}
/* Already been under the hammer and turned out empty. */
.rr-chamber.spent {
  background: radial-gradient(circle at 40% 35%, #1b3a2a, #0d2318 70%);
  border-color: rgba(110,231,135,.45);
}
/* The one about to come round. */
.rr-chamber.next {
  border-color: var(--gold-lit);
  box-shadow: inset 0 3px 8px rgba(0,0,0,.9), 0 0 14px rgba(240,197,72,.5);
}
/* Where it was, shown only once the game is over. */
.rr-chamber.bullet {
  background: radial-gradient(circle at 40% 35%, #d9a441, #7d4a10 70%);
  border-color: #ff6b6b;
  box-shadow: 0 0 18px rgba(255,107,107,.6);
}

/* Fixed at the top. The cylinder turns under it, which is what says the
   chambers are moving rather than the marker. */
.rr-hammer {
  position: absolute;
  top: -6px; left: 50%;
  width: 0; height: 0;
  margin-left: -9px;
  border-left: 9px solid transparent;
  border-right: 9px solid transparent;
  border-top: 15px solid var(--gold-lit);
  filter: drop-shadow(0 2px 5px rgba(0,0,0,.7));
}

.rr-meta {
  display: flex; gap: 8px; justify-content: center; flex-wrap: wrap;
  margin-top: 14px;
}
.rr-stat {
  flex: 1 1 0; min-width: 76px;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 7px 8px;
  border-radius: 10px;
  background: rgba(8,12,24,.55);
  border: 1px solid rgba(212,160,23,.22);
}
.rr-stat-label {
  font-size: .72rem; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.rr-stat-value {
  font-size: .95rem; font-weight: 900; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.rr-cash { color: var(--gold-lit); }

/* How many rounds go in. Five of them, shaped like the Mines board picker:
   the number large, what it means underneath. */
.rr-bul-row { display: flex; gap: 8px; margin-bottom: 12px; }
.rr-bul {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1px;
  padding: 7px 4px;
  color: var(--muted);
}
.rr-bul-n {
  font-size: 1.05rem; font-weight: 900; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.rr-bul-lbl { font-size: .72rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.rr-bul.on {
  color: #221800;
  background: linear-gradient(180deg, var(--gold-lit), var(--gold));
  border-color: var(--gold-lit);
}

/* Bank sits under whichever action row is showing, and needs the same
   breathing room Mines gives its cash-out. */
#rr-bank-row { margin-top: 10px; }

/* Pull is the one button on any table that can end the game outright, so it
   is the one button that is not gold. */
.btn-danger-spin {
  background: linear-gradient(180deg, #d8483f 0%, #a32e26 55%, #6f1c16 100%);
  border-color: #e8635a;
  color: #fff3f1;
  box-shadow: 0 6px 20px rgba(216,72,63,.3), inset 0 1px 0 rgba(255,255,255,.25);
}
.btn-danger-spin:disabled {
  background: linear-gradient(180deg, var(--blue-lit), var(--blue));
  color: var(--muted); box-shadow: none; border-color: rgba(212,160,23,.3);
}

.rr-ladder { display: flex; flex-direction: column; gap: 4px; }
.rr-ladder-row {
  display: grid;
  grid-template-columns: 3rem 1fr 1fr 1fr;
  align-items: center; gap: 8px;
  padding: 7px 10px;
  border-radius: 8px;
  background: rgba(8,12,24,.5);
  border: 1px solid rgba(255,255,255,.05);
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
}
.rr-ladder-row.on { border-color: rgba(240,197,72,.45); background: rgba(240,197,72,.07); }
.rr-ladder-head {
  background: none; border-color: transparent; padding-bottom: 0;
  font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  color: var(--muted); line-height: 1.15;
}
.rr-ladder-row span:not(:first-child) { text-align: right; }
.rr-ladder-k { font-weight: 900; color: var(--text); }
.rr-ladder-mult { color: var(--gold-lit); font-weight: 800; }
.rr-ladder-odds { color: var(--muted); }

@media (max-width: 759px) {
  /* The cylinder is square, so its width is its height and that height comes
     off the bottom of the panel underneath. */
  .rr-field { --rr-cyl: min(100%, 208px); padding: 10px; }
  .rr-stat { min-width: 68px; padding: 6px; }
  /* Eleven chamber sizes on one row rather than two: the second row is 44px
     that comes straight off the bottom of the bank button. */
  .rr-bul-row { gap: 6px; }
  .rr-bul { padding: 6px 2px; }
  .rr-bul-n { font-size: .95rem; }
  /* Tighter tracking rather than smaller type: five of these have to fit a
     phone and the label still has to clear 11px. */
  .rr-bul-lbl { letter-spacing: .02em; }
}

/* Plinko ─/* Plinko ──────────────────────────────────────────────────
   A triangle of pegs over a row of buckets. The stage is square-ish and the
   ball is positioned in PERCENTAGES of it, so the board can be any size and
   the ball still lands on a bucket rather than near one. */
.pk-field {
  --pk-w: min(100%, 420px);
  padding: 12px;
  border-radius: 12px;
  background:
    radial-gradient(ellipse 70% 60% at 50% 0%, rgba(240,197,72,.07), transparent 70%),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 60%, var(--blue-deep) 100%);
  border: 1px solid rgba(212,160,23,.28);
}
.pk-stage, .pk-buckets { max-width: var(--pk-w); margin-left: auto; margin-right: auto; }
.pk-stage {
  position: relative;
  width: 100%;
  /* NOT a height derived from --pk-w: that variable carries a percentage, and
     a percentage height resolves against a parent that has none — the stage
     came out zero tall and took the whole peg triangle with it. An aspect
     ratio gives it a real height from its own width. */
  aspect-ratio: 5 / 3;
  border-radius: 10px;
  background: rgba(8,12,24,.5);
  border: 1px solid rgba(255,255,255,.05);
  overflow: hidden;
}
/* Full bleed: every peg is placed by percentage from JS, using the same step
   the ball moves by, so the triangle and the ball's route are one geometry. */
.pk-pegs { position: absolute; inset: 0; }
.pk-peg {
  position: absolute;
  width: 5px; height: 5px; border-radius: 50%;
  margin: -2.5px 0 0 -2.5px;
  background: rgba(246,241,228,.5);
  box-shadow: 0 0 5px rgba(246,241,228,.25);
}
/* A ball. One element per ball in the throw, created and destroyed by the JS,
   so a whole handful can be in the air at once. `left`/`top` are percentages
   transitioned one row at a time, so what falls is the route the bot rolled. */
.pk-ball {
  position: absolute;
  width: 12px; height: 12px; border-radius: 50%;
  margin: -6px 0 0 -6px;
  background: radial-gradient(circle at 35% 30%, #fff6d5, var(--gold) 65%, var(--gold-dim));
  box-shadow: 0 0 10px rgba(240,197,72,.7);
  pointer-events: none;
  will-change: top, left;
}
/* Squash on the peg and stretch coming off it. Timed from the JS to land with
   the ball rather than with the instruction to move it. */
.pk-ball.bump { animation: pkBump 90ms ease-out; }
@keyframes pkBump {
  0%   { transform: scale(1, 1); }
  40%  { transform: scale(1.35, .68); }
  100% { transform: scale(1, 1); }
}
.pk-ball.landed { transform: scale(1.3); opacity: 0; transition: transform .18s ease, opacity .18s ease; }

.pk-buckets { display: flex; gap: 3px; margin-top: 8px; }
.pk-bucket {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 5px 2px;
  border-radius: 6px;
  border: 1px solid rgba(255,255,255,.08);
  font-variant-numeric: tabular-nums;
  transition: transform .12s ease, box-shadow .2s ease, filter .2s ease;
}
.pk-bucket-mult { font-size: .72rem; font-weight: 900; line-height: 1; }
.pk-bucket-pay { font-size: .72rem; color: var(--muted); line-height: 1; }
/* Hot at the edges, cold in the middle, read off the multiplier rather than
   the position — a re-tuned table re-colours itself instead of lying. */
.pk-bucket.hot   { background: linear-gradient(180deg, #6d2a12, #3d1608); border-color: #c8792f; }
.pk-bucket.hot .pk-bucket-mult { color: #ffcf87; }
.pk-bucket.warm  { background: linear-gradient(180deg, #4a3a10, #2a2008); border-color: var(--gold-dim); }
.pk-bucket.warm .pk-bucket-mult { color: var(--gold-lit); }
.pk-bucket.even  { background: linear-gradient(180deg, #17332a, #0e2019); border-color: rgba(110,231,135,.3); }
.pk-bucket.even .pk-bucket-mult { color: var(--win); }
.pk-bucket.cold  { background: linear-gradient(180deg, #1a2133, #10151f); border-color: rgba(255,255,255,.08); }
.pk-bucket.cold .pk-bucket-mult { color: var(--muted); }
.pk-bucket.hit {
  transform: translateY(-3px);
  filter: brightness(1.5);
  box-shadow: 0 0 16px rgba(240,197,72,.6), inset 0 0 0 1px rgba(255,255,255,.4);
}

/* Risk. Picked before the stake, because it is what every bucket is worth. */
.pk-risk-row { display: flex; gap: 8px; margin-bottom: 12px; }
.pk-risk {
  flex: 1;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1px;
  padding: 7px 4px;
  color: var(--muted);
}
.pk-risk-n { font-size: .95rem; font-weight: 900; line-height: 1; }
.pk-risk-lbl { font-size: .72rem; font-weight: 700; letter-spacing: .04em; }
.pk-risk.on {
  color: #221800;
  background: linear-gradient(180deg, var(--gold-lit), var(--gold));
  border-color: var(--gold-lit);
}

/* How many balls. A stepper for one at a time and a slider for the jump to a
   handful, because going from 1 to 25 one press at a time is not a control. */
.pk-balls-row { display: flex; gap: 8px; margin-bottom: 8px; }
.pk-balls-mid {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: rgba(8,12,24,.75);
  border: 1px solid rgba(212,160,23,.3);
  border-radius: 12px;
  padding: 6px;
}
.pk-balls-label {
  font-size: .7rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.pk-balls-input {
  width: 100%;
  background: none; border: none; text-align: center;
  font: inherit; font-size: 1.15rem; font-weight: 800;
  color: var(--gold-lit); font-variant-numeric: tabular-nums;
  outline: none;
}
.pk-balls-range { width: 100%; margin: 0 0 10px; accent-color: var(--gold); }
.pk-total {
  margin: -6px 0 12px;
  text-align: center;
  font-size: .8rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.pk-hist-balls { margin-left: 7px; font-size: .74rem; color: var(--muted); }

.pk-odds { display: flex; flex-direction: column; gap: 4px; }
.pk-odds-row {
  display: grid;
  grid-template-columns: 1fr 3.2rem 1fr 3.4rem;
  align-items: center; gap: 8px;
  padding: 7px 10px;
  border-radius: 8px;
  background: rgba(8,12,24,.5);
  border: 1px solid rgba(255,255,255,.05);
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
}
.pk-odds-head {
  background: none; border-color: transparent; padding-bottom: 0;
  font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  color: var(--muted); line-height: 1.15;
}
.pk-odds-row span:not(:first-child) { text-align: right; }
.pk-odds-where { color: var(--text); font-weight: 700; }
.pk-odds-mult { font-weight: 900; }
.pk-odds-row.hot .pk-odds-mult  { color: #ffcf87; }
.pk-odds-row.warm .pk-odds-mult { color: var(--gold-lit); }
.pk-odds-row.even .pk-odds-mult { color: var(--win); }
.pk-odds-row.cold .pk-odds-mult { color: var(--muted); }
.pk-odds-odds { color: var(--muted); }
.pk-hist-mult {
  display: inline-grid; place-items: center;
  min-width: 42px; padding: 1px 6px;
  border-radius: 5px;
  font-size: .74rem; font-weight: 900;
  font-variant-numeric: tabular-nums;
  border: 1px solid rgba(255,255,255,.1);
}
.pk-hist-mult.hot  { background: #3d1608; color: #ffcf87; }
.pk-hist-mult.warm { background: #2a2008; color: var(--gold-lit); }
.pk-hist-mult.even { background: #0e2019; color: var(--win); }
.pk-hist-mult.cold { background: #10151f; color: var(--muted); }

/* Coinflip ──────────────────────────────────────────────────
   One coin, two faces, and nothing to read but which way up it lands. */
.cf-field {
  padding: 18px 14px 14px;
  border-radius: 12px;
  background:
    radial-gradient(ellipse 70% 60% at 50% 0%, rgba(240,197,72,.07), transparent 70%),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 60%, var(--blue-deep) 100%);
  border: 1px solid rgba(212,160,23,.28);
}
.cf-coin-stage {
  display: grid; place-items: center;
  min-height: clamp(140px, 34vw, 200px);
  perspective: 700px;
}
/* The two faces are the same box back to back, so the turn is one rotation
   rather than a swap — a swap reads as the coin being replaced. */
.cf-coin {
  position: relative;
  width: clamp(96px, 26vw, 148px);
  height: clamp(96px, 26vw, 148px);
  transform-style: preserve-3d;
  will-change: transform;
}
.cf-face {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  border-radius: 50%;
  backface-visibility: hidden;
  font-size: clamp(2.2rem, 7vw, 3.4rem);
  font-weight: 900;
  letter-spacing: -.02em;
  overflow: hidden;
}
/* The mark sits inside the coin's rim rather than filling it edge to edge,
   the way a struck coin does. */
.cf-face img {
  width: 66%; height: 66%;
  object-fit: contain;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,.4));
  pointer-events: none;
}
.cf-heads {
  background: radial-gradient(circle at 34% 28%, #ffe9a8, var(--gold) 58%, var(--gold-dim));
  color: #3a2a04;
  box-shadow: inset 0 0 0 5px rgba(255,255,255,.22), 0 10px 26px rgba(0,0,0,.5);
}
.cf-tails {
  transform: rotateY(180deg);
  background: radial-gradient(circle at 34% 28%, #dfe6f2, #93a3bd 58%, #5b6a86);
  color: #1b2331;
  box-shadow: inset 0 0 0 5px rgba(255,255,255,.22), 0 10px 26px rgba(0,0,0,.5);
}

/* The call buttons wear both faces beside their words, so they say which is
   which as well as taking the call — a separate legend above them was the
   same information twice. */
.cf-call-row { display: flex; gap: 10px; margin-top: 14px; }
.cf-call-btn {
  flex: 1;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 8px;
  color: var(--muted);
}
.cf-call-face {
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: 50%;
  font-size: .82rem; font-weight: 900;
  background: rgba(8,12,24,.6);
  border: 1px solid rgba(255,255,255,.14);
  overflow: hidden;
}
.cf-call-face img { width: 76%; height: 76%; object-fit: contain; }
.cf-call-face.tails { color: #cfd8e8; }
.cf-call-word { font-size: .92rem; font-weight: 800; }
.cf-call-btn.on {
  color: #221800;
  background: linear-gradient(180deg, var(--gold-lit), var(--gold));
  border-color: var(--gold-lit);
}
.cf-call-btn.on .cf-call-face { background: rgba(0,0,0,.22); border-color: rgba(0,0,0,.3); color: #241a02; }

.cf-hist-face {
  display: inline-grid; place-items: center;
  width: 22px; height: 22px; border-radius: 50%;
  margin-right: 7px;
  font-size: .72rem; font-weight: 900;
  border: 1px solid rgba(255,255,255,.12);
}
.cf-hist-face { overflow: hidden; }
.cf-hist-face img { width: 78%; height: 78%; object-fit: contain; }
.cf-hist-face.heads { background: #4a3a10; color: var(--gold-lit); }
.cf-hist-face.tails { background: #2b3446; color: #cfd8e8; }
.cf-hist-word { font-weight: 700; }

/* ── Mines ───────────────────────────────────────────────────
   Five columns of four. The grid holds its shape at any width by sizing the
   tiles from the container rather than from a fixed pixel — twenty tiles is
   the game, so the board may never wrap to a different shape. */
.mn-field {
  padding: 14px 14px 12px;
  border-radius: 12px;
  background:
    radial-gradient(ellipse 70% 60% at 50% 0%, rgba(240,197,72,.07), transparent 70%),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 60%, var(--blue-deep) 100%);
  border: 1px solid rgba(212,160,23,.28);
}
/* One knob for how wide the field may get, and the board is square, so it is
   also how TALL the field gets. That is the whole budget: at 400px the play
   area has to leave room for the betting panel under it. */
.mn-field { --mn-w: min(100%, 360px); }
.mn-head, .mn-grid, .mn-meta { max-width: var(--mn-w); margin-left: auto; margin-right: auto; }
.mn-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  margin-bottom: 8px;
}
.mn-head-count {
  font-size: .82rem; font-weight: 900; color: var(--gold-lit);
  font-variant-numeric: tabular-nums;
}
.mn-head-hint { font-size: .72rem; color: var(--muted); }
.mn-grid {
  display: grid;
  /* Column count is set by JS from the board being played. */
  grid-template-columns: repeat(6, 1fr);
  gap: 5px;
}
.mn-tile {
  aspect-ratio: 1 / 1;
  display: grid; place-items: center;
  border-radius: 7px;
  border: 1px solid rgba(212,160,23,.3);
  background: linear-gradient(180deg, #223055 0%, #16203a 55%, #0e1526 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.07), 0 2px 6px rgba(0,0,0,.4);
  cursor: pointer;
  /* Sized against the tile rather than the window, so a 6x6 and an 8x8 both
     get a mark that fills the same share of the square. */
  font-size: clamp(.8rem, 3.4vw, 1.25rem);
  line-height: 1;
  transition: transform .1s ease, filter .12s ease, box-shadow .15s ease, background .2s ease;
  -webkit-tap-highlight-color: transparent;
}
.mn-tile:not(:disabled):hover { filter: brightness(1.3); transform: translateY(-1px); }
.mn-tile:not(:disabled):active { transform: scale(.95); }
/* A tile that cannot be turned should not look pressable, but a FINISHED
   board is all disabled tiles and dimming the lot would hide the result. Only
   the untouched ones on a live board go quiet. */
.mn-tile:disabled { cursor: default; }
.mn-tile .mn-face { pointer-events: none; }

.mn-tile.gem {
  background: linear-gradient(180deg, #1b6b45 0%, #0f4a2f 100%);
  border-color: #35c06d;
  box-shadow: inset 0 0 0 1px rgba(110,231,135,.35), 0 0 12px rgba(110,231,135,.2);
}
.mn-tile.mine {
  background: linear-gradient(180deg, #6b2320 0%, #3d1210 100%);
  border-color: rgba(255,107,107,.5);
  opacity: .75;
}
/* The one that ended it, told apart from the mines merely uncovered with it. */
.mn-tile.boom {
  opacity: 1;
  border-color: #ff6b6b;
  box-shadow: inset 0 0 0 2px rgba(255,107,107,.7), 0 0 18px rgba(255,107,107,.55);
  animation: mnBoom .5s ease;
}
@keyframes mnBoom {
  0% { transform: scale(1); }
  35% { transform: scale(1.14); }
  100% { transform: scale(1); }
}
/* Safe tiles never turned. Shown as what they were, quietly. */
.mn-tile.spent { opacity: .42; }

/* A marker the player put down. Deliberately not the same shape as a result:
   it is something they said, not something the board did. */
.mn-tile.flag {
  background: linear-gradient(180deg, #3a2f10 0%, #241d08 100%);
  border-color: var(--gold-dim);
  cursor: default;
}
/* Called it. Kept lit once the board is over, because getting one right is
   the only thing a flag can ever be worth on this table. */
.mn-tile.called {
  background: linear-gradient(180deg, #6b2320 0%, #3d1210 100%);
  border-color: var(--gold-lit);
  box-shadow: inset 0 0 0 2px rgba(240,197,72,.6), 0 0 14px rgba(240,197,72,.35);
  opacity: 1;
}



.mn-meta {
  display: flex; gap: 8px; justify-content: center; flex-wrap: wrap;
  margin-top: 12px;
}
.mn-stat {
  flex: 1 1 0; min-width: 88px;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 7px 10px;
  border-radius: 10px;
  background: rgba(8,12,24,.55);
  border: 1px solid rgba(212,160,23,.22);
}
.mn-stat-label {
  font-size: .72rem; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
}
.mn-stat-value {
  font-size: 1rem; font-weight: 900; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.mn-cash { color: var(--gold-lit); }

/* How many mines. Picked before the stake, because it is what every tile on
   the board is worth. */
.mn-risk-row { display: flex; gap: 8px; margin-bottom: 12px; }
.mn-risk {
  flex: 1;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1px;
  padding: 7px 4px;
  color: var(--muted);
}
.mn-risk-n { font-size: 1.05rem; font-weight: 900; line-height: 1; }
.mn-risk-lbl { font-size: .72rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; }
.mn-risk.on {
  color: #221800;
  background: linear-gradient(180deg, var(--gold-lit), var(--gold));
  border-color: var(--gold-lit);
}

/* The ladder. Its own list rather than the slots paytable, which is a
   five-column grid built for symbols. */
.mn-odds { display: flex; flex-direction: column; gap: 4px; }
.mn-odds-row {
  display: grid;
  grid-template-columns: 3.6rem 1fr 1fr 1fr;
  align-items: center; gap: 8px;
  padding: 7px 10px;
  border-radius: 8px;
  background: rgba(8,12,24,.5);
  border: 1px solid rgba(255,255,255,.05);
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
}
.mn-odds-row.on { border-color: rgba(240,197,72,.45); background: rgba(240,197,72,.07); }
.mn-odds-head {
  background: none; border-color: transparent; padding-bottom: 0;
  font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  color: var(--muted); line-height: 1.15;
}
.mn-odds-row span:not(:first-child) { text-align: right; }
.mn-odds-mines { font-weight: 900; color: var(--text); }
.mn-odds-chance { color: var(--muted); }
.mn-odds-all { color: var(--gold-lit); font-weight: 800; }

/* Two stacked action rows with nothing between them read as one control split
   in half. Higher/Lower gets the same gap from its action slot. */
#mn-cash-row { margin-top: 10px; }

/* ── Roulette ──────────────────────────────────────────────────────
   Two halves: the wheel the ball runs in, and the cloth the chips go on. */

.rl-wheel-wrap {
  /* One knob for the wheel's size; the stage, the ball's ring and the drop all
     derive from it, so the whole assembly scales together. */
  --rl-wheel: clamp(120px, 26vw, 190px);
  display: flex; align-items: center; gap: 14px;
  padding: 10px 12px;
  border-radius: 12px 12px 0 0;
  background:
    radial-gradient(ellipse 70% 60% at 50% 0%, rgba(240,197,72,.07), transparent 70%),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 60%, var(--blue-deep) 100%);
  border: 1px solid rgba(212,160,23,.28);
  border-bottom: none;
}

/* The wheel sits in a square stage so the rotor, the ball track and the marker
   all share one centre — every rotation in here turns about that point. */
/* Width AND height are stated outright rather than leaning on aspect-ratio to
   derive one from the other. Everything in here is absolutely positioned
   against this box, so if its height ever fails to resolve the wheel is drawn
   against nothing and spills out of the panel — which is exactly what it did. */
.rl-wheel-stage {
  position: relative;
  flex: 0 0 auto;
  width: var(--rl-wheel);
  height: var(--rl-wheel);
  /* A hard stop. Whatever happens to the wheel inside, it cannot paint over
     the rest of the table again. */
  overflow: hidden;
}
/* Left to clip (the SVG default) rather than overflow: if the stage is ever
   mis-sized again the wheel is contained instead of spilling across the
   page. preserveAspectRatio keeps it centred and round at any size. */
.rl-wheel { position: absolute; inset: 0; width: 100%; height: 100%; }
/* Turned by JS. The long ease-out is the whole illusion: a wheel that stops
   abruptly reads as a list snapping to an index rather than as a wheel. */
/* The WHOLE SVG is what turns, not a group inside it.
   Rotating an inner <g> meant relying on transform-box: view-box with a
   percentage transform-origin, and if that resolves against the SVG viewport
   in user units instead of against the viewBox rectangle the origin lands off
   the wheel's centre — so the wheel orbited instead of spinning, swung out of
   its panel, and left whatever was drawn at the top (the 0) under the marker
   whatever had actually been rolled.

   An <svg> element is an ordinary replaced box, so transform-origin: 50% 50%
   is simply the centre of that box. The viewBox is centred on the wheel, so
   that point IS the wheel's centre, with no SVG-specific rules in play. */
.rl-wheel { transform-origin: 50% 50%; }

/* The ball rides the rim on its own turntable so it can run against the rotor,
   the way a croupier sends it. The track rotates about the same centre; the
   ball is pushed out to the rim by its own translate. */
.rl-ball-track {
  position: absolute; inset: 0;
  transform: rotate(0deg);
  pointer-events: none;
}
/* The ball's distance from the centre is its `top`, not a percentage translate.
   A percentage in a transform resolves against the ELEMENT's own box — 38% of a
   9px ball is 3px — so the ball never left the middle of the wheel. A `top`
   percentage resolves against the track, which is the whole stage. */
.rl-ball {
  position: absolute;
  left: 50%;
  top: 13%;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 30%, #fff, #d8d2c4 60%, #8e8878);
  box-shadow: 0 1px 4px rgba(0,0,0,.7), 0 0 8px rgba(255,255,255,.35);
  transform: translate(-50%, -50%);
}

/* There is no marker any more. The wheel used to stop with the winning
   number at twelve o'clock every single time, which read as a dial being set
   rather than a wheel being spun. The ball now rests wherever it lands and
   that IS the answer — the same way you read a real wheel. */

/* One pocket, centred in whatever the wheel leaves. */
.rl-readout { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; justify-content: center; }

/* The pocket the ball is in, big enough to be the headline of the table. */
.rl-pocket {
  width: 52px; height: 52px;
  display: grid; place-items: center;
  border-radius: 50%;
  font-size: 1.35rem; font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: #f6f2e6;
  background: linear-gradient(160deg, #2a3350, #141a2c);
  border: 2px solid rgba(212,160,23,.5);
  box-shadow: inset 0 2px 6px rgba(0,0,0,.5), 0 4px 14px rgba(0,0,0,.45);
  transition: background .3s ease, border-color .3s ease;
}
.rl-pocket.red   { background: linear-gradient(160deg, #c0392b, #7d1f16); border-color: #e05a4a; }
.rl-pocket.black { background: linear-gradient(160deg, #23262e, #0d0f14); border-color: #4a4f5c; }
.rl-pocket.green { background: linear-gradient(160deg, #1f8b4c, #0e4a28); border-color: #35c06d; }
/* Dimmed to a dash while the ball is still running rather than cycling numbers.
   A flickering figure reads as the result being decided on screen, which is
   exactly what is not happening — it was settled before the ball moved. */
.rl-pocket.rolling { color: rgba(246,242,230,.3); }

/* ── The cloth ──
   One grid: a column for zero and twelve for the numbers, three rows of
   numbers, then a row of dozens and a row of even-money bets. Every cell is
   placed by grid line rather than by document order, so this is the real table
   layout and not an approximation of it.

   It has to survive a phone, where thirteen columns is about 26px each, so the
   cells scale with the container and the text scales with them. A board that
   scrolls sideways is a board you cannot read. */
.rl-board {
  display: grid;
  grid-template-columns: 1.15fr repeat(12, 1fr);
  grid-template-rows: repeat(3, auto) auto auto;
  gap: 3px;
  padding: 10px 8px 12px;
  border-radius: 0 0 12px 12px;
  background: linear-gradient(180deg, #0f5132 0%, #0c3f28 100%);
  border: 1px solid rgba(212,160,23,.28);
  border-top: none;
  box-shadow: inset 0 0 30px rgba(0,0,0,.45);
}
.rl-cell {
  position: relative;
  /* Without this a grid item refuses to go below its min-content width, so a
     longer label ("1ST 12") widens its track and the board — and with it the
     page — scrolls sideways. The board sets the widths; the text fits them. */
  min-width: 0;
  border: 1px solid rgba(255,255,255,.16);
  border-radius: 4px;
  color: #f4f0e6;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  font-size: clamp(.72rem, 2.1vw, .88rem);
  padding: 0;
  /* Slightly wider than tall: five rows of a 13-column board is the tallest
     thing on this screen, and squaring the cells up buys back real height. */
  aspect-ratio: 1 / 0.86;
  display: grid; place-items: center;
  cursor: pointer;
  transition: filter .12s ease, box-shadow .12s ease;
  -webkit-tap-highlight-color: transparent;
}
.rl-cell:disabled { cursor: default; }
.rl-cell.red   { background: linear-gradient(180deg, #a8342a, #7d1f16); }
.rl-cell.black { background: linear-gradient(180deg, #232730, #12151b); }
.rl-cell.green { background: linear-gradient(180deg, #1c7d45, #0d4525); }
.rl-cell:not(:disabled):hover { filter: brightness(1.25); }

/* Zero runs the full height of the number rows, as it does on a real cloth. */
.rl-zero { grid-column: 1; grid-row: 1 / 4; aspect-ratio: auto; }

/* The outside bets are wider than they are tall — they are labels, not cells. */
.rl-out {
  aspect-ratio: auto;
  min-height: 32px;
  background: linear-gradient(180deg, rgba(20,60,42,.9), rgba(10,40,27,.9));
  font-size: clamp(.68rem, 1.9vw, .82rem);
  letter-spacing: .02em;
  text-transform: uppercase;
}
.rl-out.red   { background: linear-gradient(180deg, #a8342a, #7d1f16); }
.rl-out.black { background: linear-gradient(180deg, #232730, #12151b); }

/* A chip sitting on a spot. Absolutely placed so it never changes the cell's
   size and never reflows the board as bets go on. */
.rl-chip-on {
  position: absolute;
  right: 1px; bottom: 1px;
  min-width: 19px; height: 18px;
  padding: 0 4px;
  display: grid; place-items: center;
  border-radius: 8px;
  background: linear-gradient(180deg, var(--gold-lit), var(--gold-dim));
  color: #241a02;
  font-size: .72rem; font-weight: 900;
  border: 1px solid rgba(0,0,0,.45);
  box-shadow: 0 1px 3px rgba(0,0,0,.6);
  pointer-events: none;
}
.rl-cell.has-bet { box-shadow: inset 0 0 0 2px var(--gold-lit); }
/* The pocket that just came up, held long enough to be found among 37 cells. */
.rl-cell.hit { box-shadow: inset 0 0 0 2px #fff, 0 0 14px rgba(255,255,255,.55); filter: brightness(1.4); }
/* A spot that actually paid, marked separately from the pocket — on an outside
   bet the winning chip is nowhere near the number that came up. */
.rl-cell.paid { box-shadow: inset 0 0 0 2px #6ee787, 0 0 12px rgba(110,231,135,.5); }

/* ── Payouts ──
   Its own list rather than the slots paytable. That grid is five columns wide
   for a symbol and four run lengths, so two cells dropped into it landed in the
   wrong columns with no styling at all, which is how this came out broken. */
.rl-pays { display: flex; flex-direction: column; gap: 4px; }
.rl-pay {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center; gap: 10px;
  padding: 5px 9px;
  background: rgba(8,12,24,.6);
  border: 1px solid rgba(212,160,23,.16);
  border-radius: 8px;
}
.rl-pay-what { font-size: .80rem; font-weight: 700; color: var(--text); }
.rl-pay-odds { font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.rl-pay-mult {
  font-size: .78rem; font-weight: 900; color: var(--gold-lit);
  font-variant-numeric: tabular-nums;
  min-width: 2.4rem; text-align: right;
}

/* Chip denominations. Same shape as the quick-bet rows on the other tables. */
.chip-row { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; margin-bottom: 10px; }
.chip {
  flex: 1 1 0; min-width: 40px;
  padding: 7px 4px;
  border-radius: 999px;
  border: 1px solid rgba(212,160,23,.4);
  background: rgba(8,12,24,.75);
  color: var(--text);
  font-size: .82rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.chip.active {
  background: linear-gradient(180deg, var(--gold-lit), var(--gold-dim));
  color: #241a02; border-color: var(--gold-lit);
}
.chip:disabled { opacity: .45; cursor: default; }

/* The exact figure riding on this spin, with what it is spread across. This is
   the answer to "how much am I betting", so it is the loudest thing in the
   controls after the Spin button itself. */
.rl-staked-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 7px 12px; margin-bottom: 10px;
  border-radius: 10px;
  background: rgba(8,12,24,.75);
  border: 1px solid rgba(212,160,23,.28);
}
.rl-staked-label { font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.rl-staked-value {
  font-size: 1.25rem; font-weight: 900;
  font-variant-numeric: tabular-nums; color: var(--gold-lit);
  line-height: 1.1;
}
.rl-staked-head { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rl-staked-spots { font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }
/* Undo and Clear are secondary to Spin, so they share a row above it. */
.rl-spin-row { margin-top: 10px; }

/* The pocket, in the recent-spins list on the side panel. */
.rl-hist-pocket {
  min-width: 25px; height: 23px; padding: 0 5px;
  display: inline-grid; place-items: center;
  border-radius: 5px;
  font-size: .78rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: #f3efe4;
  border: 1px solid rgba(255,255,255,.12);
}
.rl-hist-pocket.red   { background: #8f2b20; }
.rl-hist-pocket.black { background: #1b1e25; }
.rl-hist-pocket.green { background: #16663a; }

/* Narrow phones: the wheel goes above the readout rather than beside it, or
   both end up too cramped to read. */
@media (max-width: 420px) {
  /* Resize the wheel through its own variable, never by overriding width on
     its own: the stage takes width AND height from --rl-wheel, so setting one
     side here left the other on the old value and the wheel came out
     letterboxed rather than round. */
  /* Left at the size it was. The wheel is bigger everywhere there is room for
     it, but on a phone the wheel and the readout are stacked, so every pixel
     it gains pushes the spin button below the fold — and betting without
     scrolling is worth more here than a larger wheel. */
  .rl-wheel-wrap { --rl-wheel: min(42vw, 150px); gap: 10px; padding: 8px 12px; }
  .rl-readout { align-items: center; }

  /* The eight-column board is 8 rows tall as well as wide, and on a phone that
     height comes straight off the bottom of the start button. 300px gives a
     37px tile with a gap around it, which is still a thumb. */
  /* Square board, so this is its height too, and that height comes straight
     off the bottom of the panel underneath. 270px is a 33px tile on the
     eight-column board with a gap around each one. */
  .mn-field { --mn-w: min(100%, 270px); padding: 10px 10px 10px; }
  .mn-grid { gap: 4px; }
  .mn-meta { gap: 6px; margin-top: 10px; }
  /* Three of these have to sit on one line inside --mn-w, or they wrap to two
     rows and give back more height than the narrower board just saved. */
  .mn-stat { padding: 6px 6px; min-width: 74px; }
  .mn-head-hint { display: none; }
}

/* A narrow cloth. A spot is about 24px across on a phone, and a chip badge
   sized for a desktop board covered the number underneath it whole — so the one
   thing the board has to tell you, which spots your chips are on, was the thing
   it hid. The cells get taller, the number sits in the top of that space and the
   badge takes the bottom corner, shrunk to match.

   Keyed off the window, but the thing that matters is the width of one spot:
   above 760px the cloth only gets half the window back, so a 900px tablet has
   spots no bigger than a 400px phone. Anything short of a full desktop gets the
   compact board. */
@media (max-width: 1000px) {
  /* Every spot reads top label, bottom chip. The number or the word sits in
     the upper half; the badge owns the lower right corner and never lands on
     the thing it is annotating. */
  .rl-cell { aspect-ratio: 1 / 1.4; align-items: start; padding-top: 3px; line-height: 1; }
  .rl-board { padding: 8px 8px 10px; }
  /* The outside bets are labels, not squares — they take a floor instead of a
     ratio, and it has to clear a badge as well as the word. Reserving room
     beside the word instead was what broke "19-36" onto two lines.
     Both these and zero carry aspect-ratio in their base rules, and an
     equal-weight .rl-cell rule later in the file otherwise takes it back —
     which stretched "1ST 12", four columns wide, to 136px tall. */
  .rl-out {
    aspect-ratio: auto; min-height: 32px;
    align-items: start; padding-top: 4px;
  }
  /* Zero runs the full height of the three number rows, so its own badge has
     all the room it needs below a centred numeral. */
  .rl-zero { aspect-ratio: auto; align-items: center; padding-top: 0; }
  .rl-chip-on {
    min-width: 17px; height: 14px; padding: 0 3px;
    font-size: .68rem; border-radius: 7px;
  }
}

/* What the table will take. A max of 0 means the server set no cap, and saying
   "no max" outright is more use than leaving the line off and letting somebody
   discover the ceiling by being refused. */
.bet-limits {
  /* The bottom margin matters: this line sits between .quick-row and the
     action buttons, and .quick-row's own 12px used to be the whole gap above
     them. Without one here the buttons end up flush against this text on
     every table. */
  margin: 6px 0 12px;
  text-align: center;
  font-size: .78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.bet-limits strong { color: var(--gold-lit); font-weight: 700; }

.hl-meta {
  display: flex; justify-content: center; gap: 10px;
  margin-top: 16px; flex-wrap: wrap;
}
.hl-stat {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  background: rgba(4,8,18,.6);
  border: 1px solid rgba(212,160,23,.26);
  border-radius: 10px;
  padding: 7px 14px; min-width: 90px;
}
.hl-stat-label {
  font-size: .72rem; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
}
.hl-stat-value {
  font-size: 1rem; font-weight: 900; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.hl-cash { color: var(--gold-lit); }
/* Higher and Lower sit as one row of equal calls, like hit/stand. */
/* Higher and Lower sit above the Cash out button, so they need room under
   them as well as between them. */
/* Start and Higher/Lower are alternatives, so they share one grid cell and the
   slot is as tall as whichever is showing — both button rows are 60px, so the
   height never changes. Exactly one of the two is ever visible, so the cell
   always has a child to size it. */
.hl-act-slot { display: grid; margin-bottom: 10px; }
.hl-act-slot > * { grid-area: 1 / 1; margin-bottom: 0; }

.hl-actions { display: flex; gap: 8px; margin-bottom: 10px; }
.hl-actions .btn-act {
  flex-direction: column; gap: 2px; line-height: 1.1;
}
.hl-act-word { font-size: .9rem; }
.hl-act-odds {
  font-size: .76rem; font-weight: 700; opacity: .75;
  font-variant-numeric: tabular-nums; letter-spacing: 0; text-transform: none;
}
.hl-up    { background: linear-gradient(180deg, #2f8f5b 0%, #24704779 55%, #1b5637 100%); }
.hl-down  { background: linear-gradient(180deg, #a8434f 0%, #8a3540 55%, #6d2933 100%); }
.btn-cashout {
  background: linear-gradient(180deg, var(--gold-lit) 0%, var(--gold) 45%, var(--gold-dim) 100%);
  color: #201804;
}

/* Hit / Stand / Double sit as one row of equal actions. */
.bj-actions { display: flex; gap: 8px; }
.btn-act {
  flex: 1; min-height: 60px;
  font-size: .92rem; font-weight: 800;
  letter-spacing: .04em; text-transform: uppercase;
}
.btn-act:disabled { opacity: .4; }
.btn-double {
  background: linear-gradient(180deg, var(--gold-lit) 0%, var(--gold) 45%, var(--gold-dim) 100%);
  border-color: var(--gold-lit);
  color: #221800;
}

/* The bet is typed as well as stepped. Styled to sit exactly where the plain
   number did, so the field reads as the display it replaced rather than as a
   form control bolted on. */
.bet-input {
  width: 100%;
  background: none;
  border: none;
  text-align: center;
  font: inherit;
  font-size: 1.28rem;
  font-weight: 900;
  color: var(--gold-lit);
  font-variant-numeric: tabular-nums;
  padding: 0;
  outline: none;
}
.bet-input:focus {
  color: var(--text);
  text-shadow: 0 0 12px rgba(240,197,72,.5);
}
.bet-input:disabled { opacity: .5; }
.bet-display:focus-within {
  border-color: var(--gold-lit);
  box-shadow: 0 0 0 1px rgba(240,197,72,.35);
}

/* ── Casino floor ──────────────────────────────────────────────── */
/* Poker takes the big card; the other eight sit beside it. The whole floor is
   built to fit on screen — nothing here should ever produce a scrollbar. */
.floor-tables { display: flex; flex-direction: column; gap: 12px; }
.game-featured { display: grid; gap: 11px; }
/* The biggest card on the floor was also the dimmest: this block sets a border
   colour and lands AFTER .playable's, so it quietly overrode it and poker came
   out duller than the small cards beside it. The lead now carries more than a
   standard card rather than less. */
.game-card.game-lead {
  min-height: 170px;
  background:
    radial-gradient(ellipse 70% 80% at 50% 0%, rgba(240,197,72,.12), transparent 70%),
    linear-gradient(180deg, var(--blue-lit) 0%, var(--blue) 55%, var(--blue-deep) 100%);
}
.game-card.game-lead.playable {
  border-color: var(--gold-lit);
  box-shadow:
    inset 0 0 0 1px rgba(240,197,72,.5),
    0 8px 28px rgba(0,0,0,.5),
    0 0 26px rgba(212,160,23,.16);
}
.game-card.game-lead.playable:hover {
  box-shadow:
    inset 0 1px 0 rgba(240,197,72,.28),
    0 10px 32px rgba(0,0,0,.55),
    0 0 32px rgba(212,160,23,.26);
}
.game-card.game-lead:disabled {
  border-color: rgba(212,160,23,.5);
  box-shadow: 0 8px 26px rgba(0,0,0,.4), inset 0 1px 0 rgba(240,197,72,.12);
}
.game-card.game-lead .game-emoji { font-size: 3.2rem; }
.game-card.game-lead .game-name { font-size: 1.35rem; }

/* The house mark, sitting under the floor rather than in the chrome. */
.floor-mark {
  margin: 30px auto 8px;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  opacity: .42;
  pointer-events: none;
}
.floor-mark img {
  width: 96px; height: 96px; object-fit: contain;
  filter: drop-shadow(0 6px 20px rgba(212,160,23,.45));
}
.floor-mark span {
  font-size: .78rem; font-weight: 800; letter-spacing: .34em;
  text-transform: uppercase; color: var(--gold-lit);
}

@media (min-width: 760px) {
  /* Sized for a phone's chrome, it read as a footnote on a monitor — and the
     figure it carries is the one number a player checks most. */
  .float-balance { padding: 9px 20px; }
  .balance-label { font-size: .72rem; }
  .balance-amount { font-size: 1.5rem; }
  .balance-delta { font-size: 1.12rem; right: 20px; }

  .game-card.game-lead { min-height: 190px; }
  .game-card.game-lead .game-emoji { font-size: 3.6rem; }
  /* Six across still has room for a blurb now the page is wider. */
  .game-grid .game-card { min-height: 132px; padding: 14px 10px; }
  .game-grid .game-emoji { font-size: 2rem; }
  .game-grid .game-name { font-size: .88rem; }
  .game-grid .game-blurb { font-size: .72rem; }

  .floor-mark { margin-top: 28px; }
  .floor-mark img { width: 150px; height: 150px; }
  .floor-mark span { font-size: .78rem; }
}

/* Auto speed. A small toggle beside Auto, not a third way to spin. */
.btn-speed {
  flex: 0 0 auto;
  min-width: 52px;
  font-size: .82rem; font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.btn-speed.on {
  background: linear-gradient(180deg, #f0c548, #b8860b);
  border-color: var(--gold-lit);
  color: #1a1204;
}

/* ── Casino floor: groups ──────────────────────────────────────── */
.floor-group { margin-bottom: 16px; }
.group-label {
  display: flex; align-items: baseline; gap: 10px;
  margin: 0 0 9px;
  font-size: .74rem; font-weight: 800; letter-spacing: .18em;
  text-transform: uppercase; color: var(--gold-lit);
}
.group-label::after {
  content: ''; flex: 1; height: 1px;
  background: linear-gradient(90deg, rgba(240,197,72,.35), transparent);
}
.group-label em {
  font-style: normal; font-size: .68rem; letter-spacing: .08em;
  text-transform: none; color: var(--muted); font-weight: 600;
}

/* The banner alone goes edge to edge. Breaking out of the centred column with
   a full viewport width, while its text stays on the same line as the cards
   below — the room is framed by the banner, not stretched to match it. */
.floor-head {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  border-radius: 0;
  border-left: none; border-right: none;
  overflow: hidden;
  margin-top: 12px;
}
.floor-head-inner {
  max-width: 1360px;
  margin: 0 auto;
  padding: 0 20px;
}

@media (min-width: 760px) {
  .group-label { font-size: .78rem; }
  /* Three tables lead, poker the widest of them. */
  .game-featured { display: grid; grid-template-columns: 1.5fr 1fr 1fr; gap: 14px; }
  /* One column per non-featured game, so the machines stay on a single row.
     Bump this in step with ACTIVITY_GAMES: six columns and seven games left
     the last card orphaned on a row of its own. */
  .game-grid { grid-template-columns: repeat(7, 1fr); }
}

/* Cards turn over on the way back to the deck, so the hands need depth for a
   rotateY to read as a turn rather than as a squash. */

/* ── The riffle ─────────────────────────────────────────────────────
   ONE shuffle, used by every pile in the casino: Higher or Lower's deck and
   blackjack's. Blackjack used to have its own keyframes whose 0%/100% states
   were its own hardcoded fan offsets, which is why they could not be shared —
   the two decks had to be kept looking alike by hand.

   What makes it shareable is that a stack positions every layer inline from
   JS and publishes that offset as --rest, so a keyframe composes its movement
   ONTO the resting transform rather than replacing it. A keyframe with a bare
   transform would throw the card to wherever that transform lands and snap it
   back afterwards, which is exactly how the old one misbehaved.

   Every layer moves, not the first three. A stack is drawn back-to-front, so
   nth-child(1..3) are the DEEPEST cards; animating only those left the two on
   top — the ones actually being looked at — sitting perfectly still.

   The halves split apart and mesh back together, alternating by layer, with a
   small stagger so they interleave rather than snapping shut in one piece. */
.card-stack.shuffling .bj-card { animation: riffleR 560ms cubic-bezier(.34,.1,.3,1) both; }
.card-stack.shuffling .bj-card:nth-child(odd) { animation-name: riffleL; }
.card-stack.shuffling .bj-card:nth-child(1) { animation-delay: 0ms; }
.card-stack.shuffling .bj-card:nth-child(2) { animation-delay: 26ms; }
.card-stack.shuffling .bj-card:nth-child(3) { animation-delay: 52ms; }
.card-stack.shuffling .bj-card:nth-child(4) { animation-delay: 78ms; }
.card-stack.shuffling .bj-card:nth-child(5) { animation-delay: 104ms; }
@keyframes riffleL {
  0%   { transform: var(--rest, translate(0, 0)); }
  30%  { transform: var(--rest, translate(0, 0)) translate(-22px, -6px) rotate(-9deg); }
  58%  { transform: var(--rest, translate(0, 0)) translate(-13px, 2px) rotate(-4deg); }
  80%  { transform: var(--rest, translate(0, 0)) translate(3px, -1px) rotate(1.5deg); }
  100% { transform: var(--rest, translate(0, 0)); }
}
@keyframes riffleR {
  0%   { transform: var(--rest, translate(0, 0)); }
  30%  { transform: var(--rest, translate(0, 0)) translate(22px, -6px) rotate(9deg); }
  58%  { transform: var(--rest, translate(0, 0)) translate(13px, 2px) rotate(4deg); }
  80%  { transform: var(--rest, translate(0, 0)) translate(-3px, -1px) rotate(-1.5deg); }
  100% { transform: var(--rest, translate(0, 0)); }
}


/* ── The public demo, framed on a listing ───────────────────────────
   Everything in this block is scoped to body.is-demo, which is set in
   signInAsGuest and nowhere else, so none of it reaches the Activity's
   own floor.

   The listing gives the iframe a fixed height, shorter than the window
   the floor was drawn for, so the floor arrived scrolling in both
   directions. Nothing is cropped and no game is dropped — the same ten
   are all still on the floor, drawn tighter so the whole thing lands
   inside the frame. */
body.is-demo #screens { padding-top: 48px; }

body.is-demo .floor-head {
  margin-top: 8px;
  padding: 10px 16px 9px;
  margin-bottom: 12px;
}
body.is-demo .floor-head h1 { font-size: 1.2rem; }
body.is-demo .floor-sub { font-size: .76rem; }

body.is-demo .game-card.game-lead { min-height: 140px; }
body.is-demo .game-card.game-lead .game-emoji { font-size: 2.5rem; }
body.is-demo .game-card.game-lead .game-name { font-size: 1.15rem; }
body.is-demo .game-grid .game-card { min-height: 110px; padding: 12px 9px; }
body.is-demo .game-grid .game-emoji { font-size: 1.7rem; }

/* Seven columns leave a card no slack. Once a vertical scrollbar appears it
   takes width away, the longest unbroken word ("ROULETTE") stops fitting,
   and the grid pushes out a horizontal bar on top of the vertical one.
   Letting a card shrink past its longest word stops that second bar. */
body.is-demo .game-grid .game-card { min-width: 0; }
body.is-demo .game-grid .game-name { overflow-wrap: anywhere; }

/* The house mark is the only thing on the floor that is pure decoration,
   and the listing already carries the crest right above the frame. The
   wordmark stays; the 96px logo over it is what costs the height. */
body.is-demo .floor-mark { margin: 12px auto 4px; }
body.is-demo .floor-mark img { display: none; }

/* ── The demo's game screens ────────────────────────
   Scoped to body.is-demo, as above: none of this reaches the Activity.

   Rules and payout tables come off every game. Each of these sections is
   a "Payouts" / "House rules" / "How it pays" / "What beats what" card —
   teaching material, and one of the tallest things on the screen. Crash's
   is the single exception: its .paytable is the last-rounds strip and it
   carries the provably-fair button, so that one stays. */
body.is-demo .screen:not(#screen-crash) .paytable { display: none; }
body.is-demo .paytable { padding: 10px 14px 12px; }

/* Three recent rounds, no more. Crash's chips are left longer on purpose:
   they are one wrapped row of small tiles and cost no height. */
body.is-demo .history-row:nth-child(n+4) { display: none; }
body.is-demo .crash-history > *:nth-child(n+9) { display: none; }

/* The frame around the game. */
body.is-demo .machine { padding: 16px 16px 6px; }
body.is-demo .table-side { padding: 14px; }
body.is-demo .session-bar { margin-bottom: 10px; }

/* Poker: a smaller table with bigger cards on it. The felt keeps its 15/11
   ratio — the clearance between the pods, the board and the deck is built
   into that number — so it is narrowed rather than flattened, and the
   height comes down with the width. Poker's --cell is a fixed pixel value
   rather than a share of the felt, so the cards do not shrink along with
   it and the two changes are independent.

   What bounds the card sizes is the 126px pod they sit in: two cards at
   --cell x 1.02 plus the gap has to stay inside that, which puts your own
   pair's ceiling just under 60px. */
body.is-demo .pkr-machine { padding: 8px; }
body.is-demo .pkr-felt { max-width: 88%; --cell: 50px; }
body.is-demo .pkr-hole .bj-card { --cell: 34px; }
body.is-demo .pkr-hole .bj-card:not(.down) { --cell: 40px; }
body.is-demo .pkr-seat.you .pkr-hole .bj-card { --cell: 59px; }

/* Blackjack: the cards were coming out at their minimum. Everything on that
   felt is sized in cqw — a share of the felt's own width — and the felt is
   sized from --felt-h, which is clamp(320px, 100vh - 400px, 520px). Framed
   on a listing, 100vh is the iframe rather than the window, so that lands
   on its 320px floor, the felt comes out ~267px across, and every clamp
   below it bottoms out. Raising the clamps is what makes the hands legible
   here; the felt itself is left alone, so no chair moves. */
body.is-demo .bj-felt { --cell: clamp(34px, 13cqw, 52px); }
body.is-demo .bj-you-box { --seat-cell: clamp(26px, 10cqw, 40px); }
body.is-demo .bj-seats { --seat-cell: clamp(21px, 8cqw, 28px); }
