/* blackjack.global — game surface.
   Components lifted from the design-system gallery (design/gallery.html §4-§7)
   so the table, the strategy matrix and the content pages share one
   implementation. Every value is a token from tokens.css. No raw hex, no raw
   px radius, no raw duration, no raw easing. */

/* ============================================================================
   0. SHELL
   360px is a single column. The coach and stats rails join the table at 60rem
   and only then; below that they stack underneath it, because a 3-column game
   at 768px squeezes the card zone to nothing.
   ========================================================================== */
.game-page main {
  width: 100%;
  max-width: var(--container-xl);
  margin: 0 auto;
  padding: var(--space-5) var(--gutter) var(--space-9);
}

.gameshell {
  display: grid;
  gap: var(--space-5);
  align-items: start;
}
@media (min-width: 60rem) {
  .gameshell {
    grid-template-columns: minmax(0, 1fr) minmax(0, 20rem);
  }
  .gameshell__table { grid-column: 1; }
  .gameshell__rail  { grid-column: 2; grid-row: 1 / span 2; position: sticky; top: var(--space-4); }
  /* The matrix sits in the table column, directly under the settings panel.
     Spanning both columns left a dead gap the height of the rail. A grid
     child's default min-width is auto, so without minmax(0, …) above and
     min-width: 0 here a wide table stretches the shell into h-scroll. */
  .gameshell__wide  { grid-column: 1; grid-row: 2; }
}
.gameshell > * { min-width: 0; }

.gamehead {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: var(--space-5);
}
.gamehead__t { margin: 0; font-size: var(--text-2xl); }
.gamehead__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  min-width: 0;
}
/* The rule set control in the head slot (Thread 8, 1c). It is a real <select>,
   so it carries the same tap floor and the same 100% width cap as every other
   control on the page; the label sits above it at 360px and beside it once there
   is room, and the <select> is capped so a long option string cannot push the
   H1 row into a second line at 4K or overflow it at 360px. */
.gamehead__rules {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  align-items: center;
  min-width: 0;
  max-width: 100%;
}
.gamehead__rulesk {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--text-3);
  overflow-wrap: anywhere;
}
.gamehead__rules .select {
  width: auto;
  min-width: 0;
  max-width: 100%;
  flex: 1 1 auto;
}

.rail {
  display: grid;
  gap: var(--space-4);
  min-width: 0;
}
.panel {
  display: grid;
  gap: var(--space-3);
  align-content: start;
  padding: var(--space-4);
  background: var(--surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  min-width: 0;
}
.panel__k {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--text-3);
  overflow-wrap: anywhere;
}
.panel__h {
  margin: 0;
  font-size: var(--text-md);
  overflow-wrap: anywhere;
}

/* ============================================================================
   1. CARDS AND CHIPS  (gallery §4)
   ========================================================================== */
.card {
  position: relative;
  width: var(--card-w);
  aspect-ratio: 1 / var(--card-ar);
  background: linear-gradient(180deg, var(--card-face) 72%, var(--card-face-2) 100%);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  color: var(--card-ink);
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: calc(var(--card-w) * 0.1);
  font-family: var(--font-card);
  font-weight: var(--weight-black);
  letter-spacing: var(--track-tight);
  line-height: var(--leading-none);
}
.card--red { color: var(--card-red); }
.card__r { font-size: calc(var(--card-w) * 0.34); }
.card__s {
  width: calc(var(--card-w) * 0.3);
  height: calc(var(--card-w) * 0.3);
  align-self: flex-end;
  fill: currentColor;
}
.card--back {
  background: var(--card-back);
  background-image:
    linear-gradient(var(--card-back-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--card-back-line) 1px, transparent 1px);
  background-size: calc(var(--card-w) * 0.2) calc(var(--card-w) * 0.2);
  border: 1px solid var(--card-back-line);
}

/* A hand reserves the height of one card whether or not it is holding any, so
   dealing a card does not grow the felt and shove the whole page down. Splits
   and extra cards grow the row sideways, never taller. (Thread 8, 1a.) */
.hand { display: flex; min-height: calc(var(--card-w) * var(--card-ar)); }
.hand > .card + .card { margin-left: calc(var(--card-w) * -1 * var(--card-overlap)); }
.dealt { animation: deal var(--dur-deal) var(--ease-deal) both; }
.hand > .card:nth-child(2).dealt { animation-delay: var(--dur-deal-stagger); }
.hand > .card:nth-child(3).dealt { animation-delay: calc(var(--dur-deal-stagger) * 2); }
.hand > .card:nth-child(4).dealt { animation-delay: calc(var(--dur-deal-stagger) * 3); }
@keyframes deal {
  from { opacity: 0; transform: translate3d(38%, -160%, 0) rotate(-9deg); }
  to   { opacity: 1; transform: translate3d(0, 0, 0) rotate(var(--card-settle-rotate)); }
}

.chip {
  width: var(--chip-size);
  height: var(--chip-size);
  border-radius: var(--radius-chip);
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border: 0;
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-xs);
  /* The chip faces run from near-black to gold, so no single fill contrasts with
     the bar behind them. The 1px outer ring is the boundary that does, and it is
     the same interactive-boundary value every other control on the felt uses
     (1.4.11, Thread 14 M1). */
  box-shadow: var(--shadow-chip), 0 0 0 1px var(--table-rail-ui);
  cursor: pointer;
  position: relative;
  isolation: isolate;
  transition: transform var(--dur-chip-toss) var(--ease-chip);
}
/* the dashed edge is the one real chip signifier, masked to the outer ring
   only: across the whole face the numeral goes muddy. */
.chip::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  z-index: -1;
  background-image: repeating-conic-gradient(rgba(255,255,255,.30) 0deg 11deg, transparent 11deg 30deg);
  -webkit-mask: radial-gradient(closest-side, transparent 74%, #000 75%);
          mask: radial-gradient(closest-side, transparent 74%, #000 75%);
}
.chip::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  z-index: -1;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.16), inset 0 0 0 5px rgba(0,0,0,.10);
}
.chip:hover { transform: translateY(-4px); }
/* Selected state is a ring, not an outline. Both used outline at the same offset,
   so focusing the selected chip replaced the selection ring with the focus ring
   and the two states were indistinguishable. outline is now focus only.
   (Thread 14, M6.) */
.chip[aria-pressed="true"] {
  box-shadow: var(--shadow-chip), 0 0 0 1px var(--table-rail-ui),
              0 0 0 4px var(--brand);
}
.chip--sm { width: var(--chip-size-sm); height: var(--chip-size-sm); font-size: var(--text-2xs); cursor: default; }
.chip--1    { background-color: var(--chip-1);    color: var(--chip-1-ink); }
.chip--5    { background-color: var(--chip-5);    color: var(--chip-5-ink); }
.chip--25   { background-color: var(--chip-25);   color: var(--chip-25-ink); }
.chip--100  { background-color: var(--chip-100);  color: var(--chip-100-ink); }
.chip--500  { background-color: var(--chip-500);  color: var(--chip-500-ink); }
.chip--1000 { background-color: var(--chip-1000); color: var(--chip-1000-ink); }

/* ============================================================================
   2. TABLE  (gallery §5)
   ========================================================================== */
.table {
  position: relative;
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: radial-gradient(120% 90% at 50% 34%, var(--table-lift) 0%, var(--table-base) 68%);
  border: 1px solid var(--table-rail);
  box-shadow: inset 0 1px 0 var(--table-rail-hi), var(--shadow-e3);
  color: var(--table-text);
  display: grid;
  /* Explicit rows, one per region: HUD, felt, CTA slate, controls, CTA rail.
     They are explicit rather than auto-placed because the bet strip and the
     action bar have to share ONE cell (row 4). Auto-placement gave them a row
     each, so whichever one was on screen sized its own row and the board grew
     and shrank by the difference on every hand — 115px at 430px wide, where the
     chip row wraps. min-height below is a floor for an empty table, not the
     mechanism that holds the height steady; it masked the swap at 1440px and
     nowhere else. (Thread 8, 1a.) */
  grid-template-rows: auto 1fr auto auto auto;
  min-height: 30rem;
}
.table > .hud     { grid-row: 1; }
.table > .felt    { grid-row: 2; }
.table > .slate   { grid-row: 3; }
.table > .railcta { grid-row: 5; }
/* Row 4 is the control region. Both bars are placed in the same cell, so the
   row is always as tall as the taller of the two at that width and the swap
   costs nothing. Each bar keeps its own height and sits at the bottom of the
   cell; the slack above it is felt, which is what a shorter bar should look
   like. */
.table > .betbar,
.table > .actionbar { grid-row: 4; grid-column: 1; align-self: end; }
.table::after { /* seat arc */
  content: '';
  position: absolute;
  left: 50%;
  top: 58%;
  translate: -50% 0;
  width: min(80%, 34rem);
  aspect-ratio: 2.6 / 1;
  border-top: 1px dashed var(--table-line);
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
  pointer-events: none;
}

.hud {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--table-rail);
  font-size: var(--text-xs);
  color: var(--table-text-2);
}
.hud dl { display: flex; gap: var(--space-2); align-items: baseline; margin: 0; min-width: 0; }
.hud dt { color: var(--table-text-2); white-space: normal; overflow-wrap: anywhere; }
.hud dd {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  color: var(--table-text);
  font-variant-numeric: tabular-nums;
}

.felt {
  display: grid;
  grid-template-rows: auto 1fr;
  gap: var(--space-3);
  position: relative;
  z-index: var(--z-cards);
}

.zone {
  padding: var(--space-5) var(--space-4);
  display: grid;
  gap: var(--space-3);
  justify-items: center;
  position: relative;
  z-index: var(--z-cards);
}
.zone__label {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--table-text-2);
  text-align: center;
  overflow-wrap: anywhere;
}
.total {
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-num-sm);
  /* The figure is one line, so its box is pinned to min-height rather than left
     to the font's default line box: at 18px that box is 30.8px, taller than the
     28px reservation, so the empty slot and the filled slot were 2.8px apart and
     the felt still moved a few pixels when a total appeared. (Thread 8, 1a.) */
  line-height: var(--leading-none);
  min-width: 2.75rem;
  min-height: 1.75rem;
  padding: 0 var(--space-2);
  display: grid;
  place-items: center;
  border-radius: var(--radius-cell);
  background: rgba(0,0,0,.42);
  border: 1px solid var(--table-rail);
  font-variant-numeric: tabular-nums;
}
/* A total that is not known yet keeps its slot. Same reason as .hand above: the
   figure appears mid-hand, and it must not move the felt when it does. */
.table .total[hidden] { display: grid !important; visibility: hidden; }
.total--win  { background: var(--pos-quiet); border-color: var(--pos); color: var(--pos-dark); }
.total--bust { background: var(--neg-quiet); border-color: var(--neg); color: var(--neg); }
.betspot {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
  /* Holds the chip row's height before a wager is placed. */
  min-height: var(--chip-size-sm);
}

/* multi-hand: up to three seats. they scroll horizontally at 360px rather than
   shrinking the cards below legibility. */
.seats {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(min(100%, 13rem), 1fr);
  gap: var(--space-3);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding: 0 var(--space-2) var(--space-2);
  scroll-snap-type: x mandatory;
}
.seat {
  scroll-snap-align: center;
  display: grid;
  gap: var(--space-3);
  justify-items: center;
  align-content: start;
  padding: var(--space-3) var(--space-2);
  border-radius: var(--radius-lg);
  border: 1px solid transparent;
  min-width: 0;
}
.seat[data-active="1"] {
  border-color: var(--brand-line);
  background: color-mix(in srgb, var(--brand) 6%, transparent);
}
.seat[data-settled="1"] { opacity: 0.72; }
.seat__res {
  display: grid;
  justify-items: center;
  gap: 2px;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-cell);
  border: 1px solid var(--table-line);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  max-width: 100%;
  overflow-wrap: anywhere;
  text-align: center;
  /* The two lines are pinned to a stated leading rather than the font's default
     one, so the reservation below can be computed instead of guessed. */
  line-height: var(--leading-snug);
  /* Two lines of --text-2xs, the 2px row gap, the padding and the keyline: the
     verdict row is reserved from the moment the seat exists, so settlement fills
     a slot that was already there rather than adding one and pushing everything
     below the board down.
     Expressed from the tokens the box is built out of rather than as a measured
     pixel height, so a type-scale change carries into it. (Thread 8, 1a.) */
  min-height: calc(2 * var(--text-2xs) * var(--leading-snug)
                   + 2 * var(--space-1) + 2px + 2px);
}
.table .seat__res[hidden] { display: grid !important; visibility: hidden; }
.seat__res[data-res="win"],
.seat__res[data-res="blackjack"] {
  border-color: var(--brand-line);
  color: var(--gold-500);
}
.seat__res[data-res="bust"],
.seat__res[data-res="lose"] { color: var(--table-text-2); }
.seat__money { font-size: var(--text-2xs); color: inherit; }
.seat__k {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--table-text-2);
  overflow-wrap: anywhere;
  text-align: center;
}
/* three hands on one narrow screen: shrink the card, never the tap target. */
.seats[data-count="2"] { --card-w: clamp(2.25rem, 6vw, 4rem); }
.seats[data-count="3"] { --card-w: clamp(2rem, 5vw, 3.5rem); --card-overlap: .42; }

/* ============================================================================
   3. ACTION BAR  (gallery §5)
   glyph is locale-invariant chart notation, label is translated.
   ========================================================================== */
.actionbar {
  position: relative;
  z-index: var(--z-actionbar);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.25rem, 1fr));
  gap: var(--space-2);
  padding: var(--space-3);
  background: color-mix(in srgb, var(--table-base) 82%, #000);
  border-top: 1px solid var(--table-rail);
  padding-bottom: max(var(--space-3), env(safe-area-inset-bottom));
}
.act {
  display: grid;
  grid-template-rows: auto auto;
  gap: 2px;
  place-items: center;
  min-height: var(--label-2line-h);
  padding: var(--space-2) var(--space-1);
  background: var(--surface-3);
  border: 1px solid var(--border-ui);
  border-radius: var(--radius-md);
  cursor: pointer;
  color: var(--table-text);
  font-family: inherit;
  transition: transform var(--dur-instant) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
}
.act:hover { transform: translateY(-2px); }
.act:active { transform: translateY(1px); }
.act__g {
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-base);
  width: 1.5rem;
  height: 1.5rem;
  display: grid;
  place-items: center;
  border-radius: var(--radius-xs);
  color: var(--cell-ink);
}
.act__l {
  font-size: var(--text-2xs);
  line-height: var(--leading-tight);
  text-align: center;
  color: var(--table-text-2);
  overflow-wrap: anywhere;
  hyphens: auto;
}
.act__key {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--table-text-2);
}
@media (hover: none) { .act__key { display: none; } }
.act--hit   .act__g { background: var(--cell-hit); }
.act--stand .act__g { background: var(--cell-stand); }
.act--dbl   .act__g { background: var(--cell-dbl); }
.act--split .act__g { background: var(--cell-split); }
.act--surr  .act__g { background: var(--cell-surr); }
.act[disabled] {
  cursor: not-allowed;
  background: color-mix(in srgb, var(--table-base) 74%, #000);
  border-color: color-mix(in srgb, var(--table-rail) 70%, transparent);
}
/* Disabled is expressed by desaturating the glyph, not by fading the whole
   control: blanket opacity dropped the label under 4.5:1 in the light theme. */
.act[disabled] .act__g { filter: saturate(0.18) brightness(0.62); }
.act[disabled] .act__l,
.act[disabled] .act__key { color: var(--table-text-2); }
.act[disabled]:hover { transform: none; }

/* ---------------------------------------------------------------------------
   The felt is dark in BOTH themes (design system, locked). Every control that
   sits inside .table therefore works off table-local surface and text tokens
   rather than the page ones, which flip. Without this the light theme put
   near-white buttons and near-white label text on the dark felt.
   --------------------------------------------------------------------------- */
.table {
  --surface-1: color-mix(in srgb, var(--table-base) 92%, #fff);
  --surface-2: color-mix(in srgb, var(--table-base) 88%, #fff);
  --surface-3: color-mix(in srgb, var(--table-base) 82%, #fff);
  --surface-inset: color-mix(in srgb, var(--table-base) 88%, #000);
  --text-1: var(--table-text);
  --text-2: var(--table-text-2);
  --text-3: var(--table-text-2);
  /* --table-rail is a decorative felt edge and is not an interactive boundary.
     Remapping --border-ui onto it put every control on the felt under 3:1 and
     is exactly the failure the design system warns about: --border-ui bounds
     anything you can click, --border-subtle is decorative and exempt. The felt
     is theme-invariant, so its interactive boundary is one fixed value that
     clears 3:1 against the felt, the bar behind the controls and the control
     fill itself. (Thread 14, M1.) */
  --border-ui: var(--table-rail-ui);
  --border-subtle: color-mix(in srgb, var(--table-rail) 70%, transparent);
  --border-strong: var(--table-rail-ui);
}

/* Disabled controls on the felt: grey them properly instead of fading them.
   0.42 opacity over the dark felt turned the gold Deal button into unreadable
   brown and dropped every label under 4.5:1. */
.table .btn[disabled] {
  opacity: 1;
  background: color-mix(in srgb, var(--table-base) 74%, #000);
  border-color: color-mix(in srgb, var(--table-rail) 70%, transparent);
  color: var(--table-text-2);
}

/* The bet strip and the action bar swap in one cell, and only one of them is ever
   the truth. The inactive one is withdrawn with visibility, not display: it has
   to keep occupying the cell, or the cell collapses onto the bar that is left
   and the board changes height on every hand (Thread 8, 1a).

   visibility: hidden still satisfies what [hidden] was doing here: the subtree
   leaves the accessibility tree and its buttons cannot take focus, so a control
   the page believes is gone is not reachable by keyboard. game.js disables them
   on the same signal as well, so that holds without the CSS too.
   (Thread 14, B2 and N4, revised.) */
.table > .betbar[hidden],
.table > .actionbar[hidden] {
  display: grid !important;
  visibility: hidden;
  pointer-events: none;
}

/* the bet strip shares the action-bar slot between hands */
.betbar {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4);
  border-top: 1px solid var(--table-rail);
  background: color-mix(in srgb, var(--table-base) 82%, #000);
  padding-bottom: max(var(--space-4), env(safe-area-inset-bottom));
}
.betbar__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: center;
}
/* Six denominations is a fixed set, so the row is laid out rather than wrapped.
   Raising the chip floor to the 44px tap minimum (Thread 14, N1) pushed the
   sixth chip onto a line of its own at 360px; a three-column grid below the
   width where all six fit gives two even rows instead of an orphan. */
.betbar__chips {
  display: grid;
  grid-template-columns: repeat(6, var(--chip-size));
  justify-content: center;
}
@media (max-width: 30rem) {
  .betbar__chips { grid-template-columns: repeat(3, var(--chip-size)); }
}
.betbar__stake {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  align-items: baseline;
  justify-content: center;
  font-size: var(--text-xs);
  color: var(--table-text-2);
}
.betbar__stake b {
  font-family: var(--font-mono);
  font-size: var(--text-num-sm);
  color: var(--table-text);
  font-variant-numeric: tabular-nums;
}
.betbar__actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  gap: var(--space-2);
}
/* three buttons in a two-column auto-fit left the third on a row of its own,
   so below the width where all three fit they stack full width instead */
@media (max-width: 30rem) {
  .betbar__actions { grid-template-columns: 1fr; }
}

/* deal-speed + hand-count controls */
.ctrlrow {
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  align-items: flex-end;
}
/* A <select> is as wide as its longest option, which is how a 40% German
   expansion pushed the settings panel past 360px. Cap every child instead. */
.ctrlrow > * { min-width: 0; max-width: 100%; }
.ctrlrow select { width: 100%; min-width: 0; }

.segmented {
  display: inline-flex;
  justify-self: start;
  width: max-content;
  max-width: 100%;
  flex-wrap: wrap;
  padding: 2px;
  gap: 2px;
  background: var(--surface-inset);
  border: 1px solid var(--border-ui);
  border-radius: var(--radius-md);
}
.segmented button {
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding: var(--space-1) var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  cursor: pointer;
  overflow-wrap: anywhere;
}
.segmented button[aria-checked="true"] {
  background: var(--surface-3);
  color: var(--text-1);
  box-shadow: inset 0 0 0 1px var(--border-ui);
}

/* ============================================================================
   4. CTA HOOKS — Thread 11 owns the content. These are placement shells only.
   Tier 1 rail: ambient, never animates, not dismissible.
   Tier 2 slate: between hands, in the Deal slot, below Deal, dismissible.
   Tier 3 milestone: once per session, gold left keyline.
   ========================================================================== */
.railcta {
  position: relative;
  z-index: var(--z-rail-cta);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  align-items: center;
  padding: var(--space-2) var(--space-4);
  background: color-mix(in srgb, var(--table-base) 60%, #000);
  border-top: 1px solid var(--brand-line);
  font-size: var(--text-xs);
  color: var(--table-text-2);
}
.railcta__dot {
  width: var(--space-2);
  height: var(--space-2);
  border-radius: var(--radius-chip);
  background: var(--brand);
  flex: 0 0 auto;
}
.railcta__geo {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  opacity: 0.8;
}
.slate {
  display: grid;
  gap: var(--space-3);
  justify-items: center;
  text-align: center;
  padding: var(--space-5) var(--space-4);
}
.slate__h { font-size: var(--text-lg); font-weight: var(--weight-bold); overflow-wrap: anywhere; }
.slate__sub { font-size: var(--text-sm); color: var(--table-text-2); max-width: var(--measure-narrow); }
.slate__cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  justify-content: center;
  padding-top: var(--space-3);
  border-top: 1px solid var(--table-rail);
  width: 100%;
}
.milestone {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-5);
  background: var(--surface-1);
  border: 1px solid var(--brand-line);
  border-left: 3px solid var(--brand);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-e2);
}
.milestone__stat { display: flex; flex-wrap: wrap; gap: var(--space-5); }
.milestone__stat div { display: grid; gap: 2px; }
.milestone__k {
  font-size: var(--text-2xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--text-3);
  font-family: var(--font-mono);
  overflow-wrap: anywhere;
}
.milestone__v {
  font-family: var(--font-mono);
  font-size: var(--text-num-sm);
  font-weight: var(--weight-bold);
  color: var(--text-1);
  font-variant-numeric: tabular-nums;
}
[data-cta-slot][hidden] { display: none; }

/* ============================================================================
   5. COACH
   ========================================================================== */
.coach {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--kl, var(--border-ui));
  background: var(--surface-1);
  min-width: 0;
}
.coach[data-verdict="correct"] { --kl: var(--pos); }
.coach[data-verdict="mistake"] { --kl: var(--neg); }
.coach[data-verdict="idle"] .coach__cell { display: none; }
.coach[data-verdict="idle"]    { --kl: var(--border-ui); }
/* Coach mode off at this table (decision D3). Neutral, not alarming, and not
   pretending to be a verdict. */
.coach[data-verdict="ungraded"] { --kl: var(--border-ui); }
.coach[data-verdict="ungraded"] .coach__cell { display: none; }
.coach[data-verdict="ungraded"] .coach__verdict { color: var(--text-2); font-weight: var(--weight-medium); }
/* No chart, no cost. A dimmed zero still reads as a measured figure, so the row
   goes away entirely on an ungraded table (D3). */
.coach[data-verdict="ungraded"] .coach__ev { display: none; }
.coach__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  align-items: center;
}
.coach__cell {
  width: var(--space-9);
  height: var(--space-9);
  flex: 0 0 auto;
  border-radius: var(--radius-cell);
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-num-sm);
  color: var(--cell-ink);
  background: var(--surface-3);
}
.coach__cell.c-H { background: var(--cell-hit); }
.coach__cell.c-S { background: var(--cell-stand); }
.coach__cell.c-D { background: var(--cell-dbl); }
.coach__cell.c-P { background: var(--cell-split); }
.coach__cell.c-R { background: var(--cell-surr); }
.coach__verdict { font-size: var(--text-sm); font-weight: var(--weight-bold); overflow-wrap: anywhere; }
.coach__why { font-size: var(--text-sm); color: var(--text-2); margin: 0; overflow-wrap: anywhere; }
.coach__ev {
  /* Label and figure are one unit: a flex row lets a label wrap away from its
     own number, which reads as the wrong number under the wrong label. */
  display: grid;
  grid-template-columns: auto auto;
  justify-content: start;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  color: var(--text-3);
}
.coach__ev .badge { grid-column: 1 / -1; justify-self: start; margin-top: var(--space-1); }
.coach__ev b {
  font-family: var(--font-mono);
  font-size: var(--text-num-sm);
  font-variant-numeric: tabular-nums;
  color: var(--neg);
}
.coach__ev b[data-zero="1"] { color: var(--pos); }
.coach__cost {
  font-size: var(--text-sm);
  color: var(--text-2);
  margin: 0;
  overflow-wrap: anywhere;
}
.coach__severity {
  font-size: var(--text-xs);
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.coach__severity:empty { display: none; }

/* ============================================================================
   6. STATS
   ========================================================================== */
.stats {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
}
.stat {
  display: grid;
  gap: var(--space-1);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  border: 1px solid var(--border-subtle);
  min-width: 0;
}
.stat__k {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--text-3);
  overflow-wrap: anywhere;
  hyphens: auto;
}
.stat__v {
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-num-sm);
  line-height: var(--leading-none);
  font-variant-numeric: tabular-nums;
  color: var(--text-1);
}
.stat__v small { font-size: 0.55em; font-weight: var(--weight-medium); color: var(--text-3); margin-left: 2px; }
.stat__v[data-sign="pos"] { color: var(--pos); }
.stat__v[data-sign="neg"] { color: var(--neg); }

.mistakes { display: grid; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.mistakes li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: baseline;
  font-size: var(--text-sm);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border-subtle);
}
.mistakes li:last-child { border-bottom: 0; padding-bottom: 0; }
.mistakes span { overflow-wrap: anywhere; }
.mistakes b { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/* .chartnote (the chart-unavailable state) moved to base.css: the rule-variation
   pages under /rules/ render the same block and do not load game.css. One
   definition, two surfaces. */
.chartprov {
  margin: var(--space-3) 0 0;
  font-size: var(--text-xs);
  color: var(--text-3);
  overflow-wrap: anywhere;
}
.legend__h {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--text-2);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.emptystate {
  font-size: var(--text-sm);
  color: var(--text-3);
  margin: 0;
  padding: var(--space-4);
  border: 1px dashed var(--border-ui);
  border-radius: var(--radius-md);
  text-align: center;
  overflow-wrap: anywhere;
}

/* edge readout — replaces the score pill */
.edge { display: grid; gap: var(--space-2); min-width: 8rem; }
.edge__k {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--text-3);
  overflow-wrap: anywhere;
}
.edge__v {
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-num-md);
  line-height: var(--leading-none);
  font-variant-numeric: tabular-nums;
  color: var(--text-1);
}
.edge__v small { font-size: 0.5em; font-weight: var(--weight-medium); color: var(--text-3); margin-left: 2px; }
.edge__bar { display: flex; gap: 2px; }
.edge__bar i { height: var(--space-2); flex: 1; border-radius: 1px; background: var(--surface-3); }
.edge__bar i.on { background: var(--brand); }

/* ============================================================================
   7. STRATEGY MATRIX  (gallery §6) — rendered from Thread 9 chart data
   ========================================================================== */
.chartwrap { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: var(--space-2); }
table.chart {
  border-collapse: separate;
  border-spacing: var(--cell-gap);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}
table.chart caption {
  caption-side: top;
  text-align: left;
  padding-bottom: var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-2);
  overflow-wrap: anywhere;
}
table.chart th {
  font-weight: var(--weight-medium);
  color: var(--text-3);
  padding: 0 var(--space-1);
  min-width: var(--cell-size);
  text-align: center;
}
table.chart thead th { position: sticky; top: 0; background: var(--bg); z-index: 2; }
table.chart tbody th[colspan] {
  text-align: left;
  padding: var(--space-3) 0 var(--space-1);
  font-family: var(--font-sans);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--text-2);
  position: static;
  background: none;
}
table.chart tbody th {
  position: sticky;
  left: 0;
  background: var(--bg);
  text-align: right;
  padding-right: var(--space-2);
  z-index: 1;
  min-width: 3rem;
}
.cell {
  width: var(--cell-size);
  height: var(--cell-size);
  min-width: var(--cell-size);
  border-radius: var(--radius-cell);
  color: var(--cell-ink);
  font-weight: var(--weight-bold);
  text-align: center;
  vertical-align: middle;
  transition: transform var(--dur-instant) var(--ease-standard);
}
.cell:hover { transform: scale(1.14); position: relative; z-index: 3; }
.cell[data-live="1"] { outline: 2px solid var(--brand); outline-offset: 1px; position: relative; z-index: 4; }
.c-H { background: var(--cell-hit); }
.c-S { background: var(--cell-stand); }
.c-D { background: var(--cell-dbl); }
.c-P { background: var(--cell-split); }
.c-R { background: var(--cell-surr); }
.legend { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin-top: var(--space-4); }
.legend span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-2);
  overflow-wrap: anywhere;
}
.legend i {
  width: 1.25rem;
  height: 1.25rem;
  flex: 0 0 auto;
  border-radius: var(--radius-cell);
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  color: var(--cell-ink);
  font-style: normal;
}
@media (max-width: 640px) { :root { --cell-size: var(--cell-size-dense); } }

/* lookup mode: the 360px answer to a 10x18 matrix */
.lookup { display: grid; gap: var(--space-4); }
.lookup__io { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
.lookup__out {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-4);
  align-items: center;
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  border: 1px solid var(--border-subtle);
}
.lookup__cell {
  width: var(--space-11);
  height: var(--space-11);
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-num-md);
  color: var(--cell-ink);
}
.lookup__txt { font-size: var(--text-sm); overflow-wrap: anywhere; }

/* the matrix is unreadable below 40rem; lookup mode replaces it there. */
.chart-swap { min-width: 0; }
.chart-swap > .chartwrap { display: none; }
.chart-swap > .lookup { display: grid; }
@media (min-width: 40rem) {
  .chart-swap > .chartwrap { display: block; }
  .chart-swap > .lookup { display: none; }
  .chart-swap[data-mode="lookup"] > .chartwrap { display: none; }
  .chart-swap[data-mode="lookup"] > .lookup { display: grid; }
}

/* ============================================================================
   8. TOASTS + REDUCED MOTION + FORCED COLORS
   ========================================================================== */
/* Anchored to the foot of the felt, not to the viewport. A viewport-fixed toaster
   sat over whatever happened to be at the bottom of the screen, which on a
   430px-wide screen was the Theme control, with the shuffle notice clipping its
   Light button. The felt is the one region of this page that holds no control, so
   a notice placed there cannot cover one. Absolute inside .felt, which is
   position: relative, so the toast rides above the seats and stops short of the
   bet strip. (Thread 8, 1d.) */
.toaster {
  position: absolute;
  left: 50%;
  bottom: 0;
  translate: -50% 0;
  z-index: var(--z-toast);
  display: grid;
  gap: var(--space-2);
  width: min(calc(100% - var(--space-6)), 24rem);
  pointer-events: none;
}
.toast {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface-3);
  border: 1px solid var(--border-ui);
  box-shadow: var(--shadow-e2);
  font-size: var(--text-sm);
  color: var(--text-1);
  overflow-wrap: anywhere;
}

@media (prefers-reduced-motion: reduce) {
  .chip:hover, .act:hover, .cell:hover, .hub:hover { transform: none; }
}
/* The in-page Reduce option has to suppress the same lifts as the system
   preference, or it is a control that only half works (Thread 14, M3). */
html[data-motion="reduce"] .chip:hover,
html[data-motion="reduce"] .act:hover,
html[data-motion="reduce"] .cell:hover,
html[data-motion="reduce"] .hub:hover { transform: none; }

@media (forced-colors: active) {
  .act, .chip, .cell, .card, .coach__cell, .lookup__cell {
    forced-color-adjust: none;
    border: 1px solid CanvasText;
  }
  .act[disabled] { opacity: 1; color: GrayText; }
  .seat[data-active="1"] { outline: 2px solid Highlight; }
}

/* Locale-invariant chart notation. Rendered from an attribute rather than a
   text node so build/check_strings.py never sees a letter in the template. */
.act__g::before,
.act__key::before,
.keycap::before { content: attr(data-glyph); }
.keycap {
  display: inline-grid;
  place-items: center;
  min-width: 1.5rem;
  padding: 0 var(--space-1);
  border: 1px solid var(--border-ui);
  border-radius: var(--radius-xs);
  background: var(--surface-2);
  font-size: var(--text-xs);
}

/* ==========================================================================
   COUNTING DRILLS  (issue #10)

   The drill panel lives in the rail beside the coach and the statistics, so it
   inherits .panel and reuses .field / .select from the settings row rather than
   introducing a second form vocabulary. Only what is genuinely new is here.

   The figure is a two-column label-and-value grid so a label can never wrap away
   from the value it names. A card run is rank tiles, not the felt's suited cards:
   the drills work on rank values, so there is no suit to draw. A true-count drill
   deals 78 of them, which is why they wrap and stay small.
   ========================================================================== */
.drill__prompt {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--text-2);
  overflow-wrap: anywhere;
}
.drill__figure {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  min-width: 0;
}
.drill__figure:empty { display: none; }
.drill__figure > .emptystate { grid-column: 1 / -1; }
.drill__pips { display: flex; flex-wrap: wrap; gap: var(--space-1); min-width: 0; }
.drill__pip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.65rem;
  padding: 0.15rem 0.3rem;
  border: 1px solid var(--border-ui);
  border-radius: var(--radius-cell);
  background: var(--surface-2);
  color: var(--text-1);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  line-height: var(--leading-none);
}
.drill__ramp { display: grid; gap: var(--space-2); }
.drill__answer { margin-top: var(--space-2); }
.drill__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
/* Buttons carry the tap floor from tokens.css; it is never overridden here. */
.drill__actions > .btn { flex: 1 1 auto; }
.drill__feedback {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--text-2);
  overflow-wrap: anywhere;
  border-left: 3px solid var(--kl, transparent);
  padding-left: var(--space-3);
}
.drill__feedback[data-verdict="idle"] { padding-left: 0; }
.drill__feedback[data-verdict="correct"] { --kl: var(--pos); }
.drill__feedback[data-verdict="mistake"] { --kl: var(--neg); }

/* ===========================================================================
   PUBLISHED WORKING (issue #10, brief 2a)

   The derivation of a drill answer. It is a numbered list because the steps are
   ordered arithmetic and the player needs to find the first step that differs
   from their own. Numerals are tabular so a column of figures lines up, and every
   figure is mono so a minus sign cannot be mistaken for kerning.

   Tables inside a step are the same two-column label-and-value grid the drill
   figure uses, so a tag table and a card run read as the same kind of object.
   ========================================================================== */
.working {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-ui);
  min-width: 0;
}
.working__h {
  margin: 0 0 var(--space-1);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-1);
}
.working__intro {
  margin: 0 0 var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-2);
  overflow-wrap: anywhere;
}
.working__steps {
  margin: 0;
  padding-left: 1.35rem;
  display: grid;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-2);
  min-width: 0;
}
.working__steps > li {
  overflow-wrap: anywhere;
  font-variant-numeric: tabular-nums;
}
.working__table { display: grid; gap: var(--space-1); min-width: 0; }
.working__caption { font-size: var(--text-xs); color: var(--text-2); }
.working__grid {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-1) var(--space-2);
  min-width: 0;
}
.working__v {
  font-variant-numeric: tabular-nums;
  color: var(--text-1);
  overflow-wrap: anywhere;
}
.working__source {
  margin: var(--space-2) 0 0;
  font-size: var(--text-xs);
  color: var(--text-2);
  overflow-wrap: anywhere;
}
.working__source a { color: inherit; }
