/* DartsHub cast surface — design tokens, stage and the Cricket gameplay screen.
 * Tokens and values come from the Claude Design handoffs ("DartsHub Screencast
 * System Design" and "Cricket ML Layout"). The shared setup / match card /
 * transition screens live in shared-screens.css. */

/* Batman Forever Alternate — freeware display face. Confirm the licence before
 * shipping to real users. */
@font-face {
  font-family: 'Batmfa';
  src: url('fonts/batmfa.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

:root {
  --dh-surface-primary: linear-gradient(180deg, #001546 0%, #00091f 100%);
  --dh-brand-blue: #014cfd;
  --dh-action-blue: #0140d4;
  --dh-bright-blue: #429bfc;
  --dh-white: #ffffff;
  --dh-text-primary: #d1d5d8;
  --dh-text-secondary: #8e98a0;
  --dh-text-tertiary: rgba(142, 152, 160, 0.64);
  --dh-panel-08: rgba(209, 213, 216, 0.08);
  --dh-panel-16: rgba(209, 213, 216, 0.16);
  --dh-stroke-08: rgba(209, 213, 216, 0.08);
  --dh-stroke-24: rgba(209, 213, 216, 0.24);
  --dh-radius-m: 10px;
  --dh-radius-xl: 16px;
  --dh-ls-display: 0.04em;
  --dh-ls-micro: 0.08em;
  --dh-font-display: 'Batmfa', 'Bungee', system-ui, sans-serif;
  --dh-ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);

  /* Cricket segment states (Cricket ML Layout · "Stylized board · segment states") */
  --cr-score-here: #ffc93c;
  --cr-to-close: #1e88fc;
  --cr-dead: #747f8a;
  --cr-not-in-play: #0e1b33;
}

* { box-sizing: border-box; }

html,
body {
  margin: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #00091f;
  -webkit-font-smoothing: antialiased;
}

.stage {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1920px;
  height: 1080px;
  transform-origin: center;
  overflow: hidden;
  background: var(--dh-surface-primary);
  color: var(--dh-text-primary);
  font-family: var(--dh-font-display);
  letter-spacing: var(--dh-ls-display);
  text-transform: uppercase;
}

/* Every screen is laid out for 1920 × 1080 exactly and centred in the stage.
 * An ultrawide monitor is wider than the frame, and letting the standby
 * screens fill it stretched their furniture — a quote rail across the whole
 * 21:9, statistics columns drifting apart — while gameplay stayed composed.
 * One frame for all of them; the stage's own gradient fills the sides. */
.screen {
  position: absolute;
  inset: auto;
  top: 50%;
  left: 50%;
  width: 1920px;
  height: 1080px;
  transform: translate(-50%, -50%);
}

/* ── Keyframes (verbatim from the screencast handoff) ─────────────────────── */
@keyframes dh-pulse { 0%, 100% { opacity: .25 } 50% { opacity: 1 } }
@keyframes dh-breathe { 0%, 100% { opacity: .55 } 50% { opacity: 1 } }
@keyframes dh-sweep { 0% { transform: translateX(-100%) } 100% { transform: translateX(320%) } }
@keyframes dh-rise { from { opacity: 0; transform: translateY(36px) } to { opacity: 1; transform: translateY(0) } }
@keyframes dh-pop { 0% { opacity: 0; transform: scale(.82) } 60% { transform: scale(1.03) } 100% { opacity: 1; transform: scale(1) } }
@keyframes dh-flash { 0% { opacity: 0 } 18% { opacity: 1 } 100% { opacity: 0 } }
@keyframes dh-slam { 0% { opacity: 0; transform: scale(1.5); letter-spacing: .5em } 30% { opacity: 1; transform: scale(1); letter-spacing: .14em } 72% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(.96) } }
@keyframes dh-bar { 0% { transform: scaleX(0) } 40% { transform: scaleX(1) } 100% { transform: scaleX(1) } }
@keyframes dh-pulse-ring { 0%, 100% { opacity: 1 } 50% { opacity: .62 } }

/* ── Pairing (not in the handoff yet: on-brand placeholder) ───────────────── */
.pairing {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 48px;
  position: relative;
  height: 100%;
}
.pairing__hint { font-size: 30px; letter-spacing: .2em; color: var(--dh-text-secondary); }
.pairing__code {
  font-size: 220px;
  line-height: 1;
  color: #fff;
  letter-spacing: .18em;
  font-variant-numeric: tabular-nums;
}
.pairing__body { display: flex; align-items: center; gap: 96px; }
.pairing__qr {
  width: 300px;
  height: 300px;
  padding: 22px;
  background: #fff;
  border-radius: 24px;
}
.pairing__qr svg { display: block; width: 100%; height: 100%; }
.pairing--live .pairing__qr { animation: pair-out 420ms var(--dh-ease-out) both }

/* After pairing the code stays small in a corner, so a phone that dropped off
 * can find its way back without anyone walking to the TV. */
.code-chip {
  position: fixed;
  right: 18px;
  bottom: 14px;
  z-index: 10;
  font: 15px/1 var(--dh-font-display);
  letter-spacing: .22em;
  color: rgba(255, 255, 255, .38);
  pointer-events: none;
}
.code-chip[hidden] { display: none; }
.pairing__status { font-size: 26px; letter-spacing: .14em; color: var(--dh-bright-blue); animation: dh-breathe 2.2s infinite; }
.pairing__status--offline { color: #d11212; }

/* The handshake. Quiet on purpose: the room has just typed a code into a phone
 * and needs telling it worked, which is a confirmation, not a moment. The code
 * settles, one green ring leaves it with a thinner one behind, the waiting line
 * becomes a tick, and the screen bows out under the quotes coming up. */
.pairing__ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 520px;
  height: 520px;
  margin: -260px 0 0 -260px;
  border-radius: 9999px;
  border: 3px solid rgba(58, 210, 78, .5);
  opacity: 0;
  pointer-events: none;
  animation: pair-ring 1100ms var(--dh-ease-out) 60ms both;
}
.pairing__ring--trail { border-width: 2px; border-color: rgba(58, 210, 78, .3); animation-delay: 240ms; }
@keyframes pair-ring {
  0% { opacity: 0; transform: scale(.16) }
  18% { opacity: 1 }
  100% { opacity: 0; transform: scale(1.9) }
}
.pairing__bloom {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(closest-side at 50% 50%, rgba(58, 210, 78, .2) 0%, rgba(58, 210, 78, 0) 72%);
  animation: pair-bloom 1250ms var(--dh-ease-out) both;
}
@keyframes pair-bloom { 0% { opacity: 0 } 22% { opacity: 1 } 100% { opacity: 0 } }
/* The code has done its job: it tightens and settles rather than sitting there
 * waiting for input that has already arrived. */
.pairing--live .pairing__code { animation: pair-lock 640ms var(--dh-ease-out) both }
@keyframes pair-lock {
  0% { letter-spacing: .18em; transform: scale(1) }
  45% { letter-spacing: .15em; transform: scale(.968) }
  100% { letter-spacing: .16em; transform: scale(.982) }
}
.pairing--live .pairing__hint { animation: pair-out 420ms var(--dh-ease-out) both }
.pairing--live .pairing__status {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  color: #3ad24e;
  animation: pair-in 520ms var(--dh-ease-out) 120ms both;
}
.pairing__tick path { stroke-dasharray: 26; stroke-dashoffset: 26; animation: pair-tick 400ms var(--dh-ease-out) 260ms forwards }
@keyframes pair-tick { to { stroke-dashoffset: 0 } }
@keyframes pair-in { 0% { opacity: 0; transform: translateY(16px) } 100% { opacity: 1; transform: none } }
@keyframes pair-out { 0% { opacity: 1 } 100% { opacity: 0 } }
/* 770 + 480 = the 1250ms the screen is given: it is gone as the quotes arrive. */
.pairing--live { animation: pair-exit 480ms var(--dh-ease-out) 770ms both }
@keyframes pair-exit { 0% { opacity: 1; transform: translateY(0) } 100% { opacity: 0; transform: translateY(-28px) } }
@media (prefers-reduced-motion: reduce) {
  .pairing__ring, .pairing__bloom { display: none }
  .pairing--live, .pairing--live .pairing__code, .pairing--live .pairing__status,
  .pairing--live .pairing__hint, .pairing__tick path { animation: none }
}

/* ── Cricket gameplay ─────────────────────────────────────────────────────── */
/* Backdrop: the same blue bloom as the setup and match card screens
 * (.backdrop in shared-screens.css). */
.cricket__board,
.x01__board {
  position: absolute;
  left: 50%;
  top: 600px;
  width: 980px;
  height: 980px;
  transform: translate(-50%, -50%);
}
.cricket__board svg,
.x01__board svg { display: block; }

/* A dart landing. The board takes the knock: a short recoil pivoting on the
 * hit, so the motion comes from where the dart went in. One percent is enough
 * — at 980px across a living room that already reads as impact, and anything
 * more turns the stage into a trampoline. The crosshair stamps in over it and
 * a ring goes out from the point. */
.board--impact { animation: board-impact 420ms var(--dh-ease-out) both; }
@keyframes board-impact {
  0% { transform: scale(1) }
  10% { transform: scale(1.011) }
  34% { transform: scale(.9965) }
  58% { transform: scale(1.0025) }
  100% { transform: scale(1) }
}
.board__hit--land {
  transform-box: fill-box;
  transform-origin: center;
  animation: board-stamp 240ms var(--dh-ease-out) both;
}
@keyframes board-stamp {
  0% { opacity: 0; transform: scale(2.4) }
  48% { opacity: 1; transform: scale(.86) }
  100% { opacity: 1; transform: scale(1) }
}
.board__shock {
  transform-box: fill-box;
  transform-origin: center;
  animation: board-shock 460ms var(--dh-ease-out) both;
}
@keyframes board-shock {
  0% { opacity: .8; transform: scale(.14) }
  70% { opacity: .2 }
  100% { opacity: 0; transform: scale(1) }
}
/* A run building. One dart in the target is a good dart; two is a finish — or
 * a Shanghai — one dart away, and the board should say so before the third is
 * thrown. What is still lit burns brighter, and the dart that lands next hits
 * harder: a longer recoil, a wider ring, and at three a second ring behind it.
 * The bed's step-up is declared later in this sheet and so wins while it is
 * running; the throb takes the bed over once the step-up is done. */
.board--impact-2 { animation: board-impact-2 520ms var(--dh-ease-out) both }
@keyframes board-impact-2 {
  0% { transform: scale(1) }
  10% { transform: scale(1.019) }
  34% { transform: scale(.994) }
  58% { transform: scale(1.004) }
  100% { transform: scale(1) }
}
.board--impact-3 { animation: board-impact-3 640ms var(--dh-ease-out) both }
@keyframes board-impact-3 {
  0% { transform: scale(1) }
  9% { transform: scale(1.028) }
  30% { transform: scale(.991) }
  52% { transform: scale(1.007) }
  74% { transform: scale(.998) }
  100% { transform: scale(1) }
}
.board__shock--wide { animation: board-shock-wide 620ms var(--dh-ease-out) both }
@keyframes board-shock-wide {
  0% { opacity: .45; transform: scale(.08) }
  70% { opacity: .12 }
  100% { opacity: 0; transform: scale(1) }
}
.board--hot-1 .route--now { filter: brightness(1.22) drop-shadow(0 0 16px rgba(255, 201, 60, .55)) }
.board--hot-2 .route--now {
  filter: brightness(1.5) drop-shadow(0 0 30px rgba(255, 201, 60, .9));
  animation: route-throb 860ms var(--dh-ease-out) infinite;
}
.board--hot-1 .route--later { filter: brightness(1.3) }
.board--hot-2 .route--later { filter: brightness(1.6) }
@keyframes route-throb { 0%, 100% { opacity: 1 } 50% { opacity: .68 } }

@media (prefers-reduced-motion: reduce) {
  .board--impact, .board--impact-2, .board--impact-3, .board__hit--land { animation: none }
  .board--hot-2 .route--now { animation: none }
  .board__shock { display: none }
}

/* ── A number moving, a bar growing ───────────────────────────────────────── */
/* The gold "+N" the Academy games print off a banked score
 * (academy_score_pop.dart): #FFD37A, 700ms, in over the first fifth and out
 * over the rest, rising as it goes.
 *
 * The phone has empty space above the score to rise into. The cast card, sized
 * around the biggest possible board, has none — every edge of it is the head
 * row, the stats or the checkout — so the pop carries the card's own chip
 * (radius 10, 2px border, glass) and reads over whatever it passes. Out of
 * flow either way: no layout moves for it. */
.score-pop {
  position: absolute;
  right: 0;
  /* Above the number, not over it: the score is the one thing on the card that
   * must stay readable while it changes. */
  bottom: calc(100% + 6px);
  pointer-events: none;
  padding: 3px 12px 5px;
  border-radius: 10px;
  background: rgba(11, 13, 17, .74);
  border: 2px solid rgba(255, 201, 60, .45);
  backdrop-filter: blur(16px);
  font-size: 36px;
  line-height: 1;
  color: #ffd37a;
  text-shadow: 0 0 18px rgba(255, 201, 60, .45);
  animation: score-pop 700ms var(--dh-ease-out) both;
}
.seats--compact .score-pop { font-size: 30px; }
.player--solo .score-pop { font-size: 48px; }
@keyframes score-pop {
  0% { opacity: 0; transform: translateY(10px) scale(.92) }
  20% { opacity: 1; transform: translateY(0) scale(1) }
  100% { opacity: 0; transform: translateY(-34px) scale(1) }
}

/* A bar that grew starts from where it was and runs out to the new value on
 * the same curve, with a flare of the same gold as it goes. Two keyframes
 * because the bars grow along different properties. */
.leg-progress__fill--grew { animation: bar-grow-width 620ms var(--dh-ease-out) }
@keyframes bar-grow-width {
  from { width: var(--grow-from) }
  35% { filter: brightness(1.5) saturate(1.15) }
  to { filter: none }
}
.sh-cell--grew::after { animation: bar-grow-height 620ms var(--dh-ease-out) }
@keyframes bar-grow-height {
  from { height: calc(var(--grow-from) * (100% - var(--sh-label))) }
  35% { filter: brightness(1.5) saturate(1.15) }
  to { filter: none }
}

/* Cricket counts in marks, not lengths, so its progress grows a cell at a
 * time: the one that just landed runs out from its own edge through the same
 * gold, and the row flashes when the third mark closes the number. No
 * fill-mode and nothing in box-shadow — the cells carry their state there, and
 * a filled keyframe would leave a finished mark holding this animation's
 * values. The row is mirrored in the right column but its three cells are not:
 * the first mark is the left-hand cell on either side of the board. */
.marks__cell--new { transform-origin: left center; animation: mark-in 420ms var(--dh-ease-out) }
@keyframes mark-in {
  0% { transform: scaleX(.16); background-color: #ffc93c }
  55% { transform: scaleX(1.03) }
  100% { transform: scaleX(1) }
}
.marks__row--closing { animation: mark-closed 620ms var(--dh-ease-out) }
@keyframes mark-closed {
  0% { background: rgba(255, 201, 60, .38) }
  100% { background: transparent }
}
@media (prefers-reduced-motion: reduce) {
  .score-pop { display: none }
  .leg-progress__fill--grew, .sh-cell--grew::after,
  .marks__cell--new, .marks__row--closing { animation: none }
}

.cricket__nav,
.x01__nav {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 3;
  height: 148px;
  display: grid;
  /* minmax(0,…): a long line shrinks inside its side of the nav instead of
   * widening it and pushing the dart discs off the centre of the board. */
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  column-gap: 40px;
  align-items: center;
  padding: 0 64px;
}
/* No bar and no edge: the nav's ground is a scrim that fades out, so the
 * gameplay backdrop carries straight through it. It lives on a pseudo-element
 * because the mask that fades the blur with the tint — a hard blur line being
 * as visible as a hard colour one — would otherwise fade the nav's own text. */
.cricket__nav::before,
.x01__nav::before {
  content: '';
  position: absolute;
  inset: 0;
  /* Behind the nav's own text: a positioned pseudo-element otherwise paints
   * over in-flow content and swallows the title. */
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(0, 9, 31, .62) 0%, rgba(0, 9, 31, .34) 62%, rgba(0, 9, 31, 0) 100%);
  backdrop-filter: blur(14px);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 52%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 0%, #000 52%, transparent 100%);
}
/* Three lines: the game, what the game is asking for, where the match stands.
 * One fact a line keeps every line short whatever the state of the game, and
 * a line that still runs long is cut rather than let into the discs. */
.cricket__mode,
.x01__mode { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.cricket__mode-title,
.x01__mode-title { font-size: 52px; line-height: 1; color: #fff; }
.cricket__mode-sub,
.x01__mode-sub,
.cricket__mode-leg,
.x01__mode-leg { font-size: 24px; line-height: 1; letter-spacing: var(--dh-ls-micro); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* What the game is asking for changes through a leg, so it is the brighter of
 * the two; where the match stands is there when somebody looks for it. */
.cricket__mode-sub,
.x01__mode-sub { color: rgba(255, 255, 255, .86); }
.cricket__mode-leg,
.x01__mode-leg { color: var(--dh-text-secondary); }
.cricket__mode-sub:empty,
.x01__mode-sub:empty { display: none; }
.cricket__visit,
.x01__visit { display: flex; align-items: center; gap: 24px; }
.cricket__status,
.x01__status { display: flex; justify-content: flex-end; align-items: center; }
.cricket__autoscoring,
.cast-autoscoring {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 30px;
  border-radius: 9999px;
  background: rgba(11, 13, 17, .44);
  border: 1px solid rgba(76, 175, 80, .30);
  backdrop-filter: blur(16px);
  box-shadow: 0 0 28px rgba(76, 175, 80, .14);
  font-size: 24px;
  line-height: 1;
  letter-spacing: var(--dh-ls-micro);
  color: #fff;
  white-space: nowrap;
}
.cricket__autoscoring svg,
.cast-autoscoring svg { flex: 0 0 auto; filter: drop-shadow(0 0 6px rgba(76, 175, 80, .65)); }

/* Dart indicator disc (Figma · X - Nav Bar / Dart Indicator) */
.disc {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 96px;
  height: 96px;
  border-radius: 9999px;
  flex-shrink: 0;
  color: #fff;
  font-size: 38px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.disc--long { font-size: 32px; }
/* A recorded dart is the success green whatever it hit; a busted one keeps its
 * bed in the danger red (DartIndicator.withShot). */
.disc--hit { background: #1d8528; }
.disc--bust,
.disc--miss { background: #d11212; }
.disc--next { background: #0140d4; animation: dh-pulse-ring 1.8s var(--dh-ease-out) infinite; }
/* The checkout the app suggests for a slot not yet thrown: the phone's own
 * hint indicator — its secondary #D29042, the bed, and the dart in the
 * corner. */
.disc--hint { background: #d29042; font-size: 32px; }
.disc__hint-mark {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 30px;
  height: 30px;
  color: #fff;
  display: grid;
  place-items: center;
}
.disc__hint-mark svg { width: 30px; height: 30px; }
.disc--empty { background: rgba(204, 230, 230, 0.04); color: rgba(209, 214, 217, 0.35); }

/* Player column (left player mirrored on the right) */
.player {
  position: absolute;
  top: 196px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 26px;
}
.player--left { left: 64px; align-items: flex-start; }
.player--right { right: 64px; align-items: flex-end; }
.player__head { display: flex; align-items: center; gap: 24px; }
.player--right .player__head { flex-direction: row-reverse; }
.player__avatar { width: 120px; height: 120px; border-radius: 9999px; overflow: hidden; flex-shrink: 0; }
.player__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.player__who { display: flex; flex-direction: column; gap: 12px; }
.player--left .player__who { align-items: flex-start; }
.player--right .player__who { align-items: flex-end; }
.player__name { font-size: 48px; line-height: 1; white-space: nowrap; color: #fff; }
.player__progress { display: flex; flex-direction: column; gap: 8px; }
.player__progress-row { display: flex; align-items: center; gap: 12px; }
.player--right .player__progress-row { flex-direction: row-reverse; }
.player__progress-label { font-size: 20px; line-height: 1; letter-spacing: var(--dh-ls-micro); color: var(--dh-text-secondary); }
.player__bars { display: flex; gap: 6px; }
.player--right .player__bars { flex-direction: row-reverse; }
.player__bar { display: block; width: 34px; border-radius: 3px; background: rgba(255, 255, 255, .11); }
.player__bar--set { height: 12px; }
.player__bar--leg { height: 8px; border-radius: 8px; }
/* The standing has to read from across the room, so a won pill is lit, not
 * just filled: legs green, sets gold — the colour says which ladder it is. */
.player__bar--won { background: #3ad24e; box-shadow: 0 0 14px rgba(58, 210, 78, .6), inset 0 0 0 1px rgba(255, 255, 255, .28); }
.player__bar--set.player__bar--won { background: var(--cr-score-here); box-shadow: 0 0 16px rgba(255, 201, 60, .65), inset 0 0 0 1px rgba(255, 255, 255, .3); }
.player__bar--current { background: rgba(255, 255, 255, .34); }
.player__score { position: relative; font-size: 150px; line-height: .92; white-space: nowrap; color: #fff; font-variant-numeric: tabular-nums; }

.player--idle .player__name,
.player--idle .player__score,
.player--idle .player__stat-value { color: var(--dh-text-secondary); }
.player--idle .player__avatar { filter: saturate(.75) brightness(.85); }

/* Marks rows: bull, 20 … 15 */
.marks { width: 360px; display: flex; flex-direction: column; gap: 8px; }
.marks__row { display: flex; align-items: center; gap: 14px; height: 42px; }
.player--right .marks__row { flex-direction: row-reverse; }
.marks__label { width: 64px; flex: 0 0 auto; font-size: 30px; line-height: 30px; text-align: right; }
/* Four letters in the column of a two-digit number. */
.marks__label--bull { font-size: 20px; }
.player--right .marks__label { text-align: left; }
.marks__end { width: 22px; flex: 0 0 auto; display: flex; justify-content: center; }
.marks__cells { flex: 1; display: flex; gap: 9px; }
.marks__cells--scorable { box-shadow: inset 0 0 0 2px rgba(255, 201, 60, .40); border-radius: 7px; padding: 2px; }
.marks__cell { flex: 1; height: 22px; border-radius: 5px; background: rgba(4, 10, 26, .62); box-shadow: inset 0 0 0 2px rgba(209, 213, 216, .15); }
.marks__cells--exposed .marks__cell:not(.marks__cell--hit) { box-shadow: inset 0 0 0 2px rgba(255, 77, 85, .55); }
.marks__cell--hit { background: var(--cr-to-close); box-shadow: 0 0 14px #1e88fc88; }
.marks__row--closed .marks__label { color: var(--cr-score-here); }
.marks__row--closed .marks__cell { background: var(--cr-score-here); box-shadow: 0 0 14px #ffc93c88; }
.marks__row--open .marks__label { color: var(--cr-to-close); }
.marks__row--dead .marks__label { color: rgba(116, 127, 138, .62); }
.marks__dead {
  position: relative;
  flex: 1;
  height: 22px;
  border-radius: 5px;
  background: rgba(11, 18, 34, .92);
  box-shadow: inset 0 0 0 2px rgba(116, 127, 138, .32);
}
.marks__dead::after {
  content: '';
  position: absolute;
  left: 8%;
  right: 8%;
  top: 50%;
  height: 2px;
  background: rgba(116, 127, 138, .78);
}

.player__stats { display: flex; gap: 40px; }
.player__stat { display: flex; flex-direction: column; gap: 10px; }
.player--left .player__stat { align-items: flex-start; }
.player--right .player__stat { align-items: flex-end; }
.player__stat-label { font-size: 22px; line-height: 1; letter-spacing: var(--dh-ls-micro); color: var(--dh-text-secondary); }
.player__stat-value { font-size: 56px; line-height: 1; color: #fff; font-variant-numeric: tabular-nums; }

/* ── Seats: three to six players ──────────────────────────────────────────
 * The board never shrinks, so the players share the two side columns and
 * nothing else. Seats alternate left and right in throw order, and the tier
 * (two or three a side) follows the fuller column so both sides match. The
 * card is the 1v1 card with its type scale stepped down — no new components. */
.seats {
  position: absolute;
  top: 150px;
  width: 440px;
  height: 908px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  /* Three and five players leave one column short. It fills from the top, so
   * seat 2 sits level with seat 1 instead of drifting down the frame. */
  justify-content: flex-start;
  gap: 20px;
}
.seats--left { left: 32px; }
.seats--right { right: 32px; }
.screen--game .seats .player {
  position: static;
  display: grid;
  /* minmax(0,…) or a wide child — a twenty-cell Shanghai strip, a long name —
   * pushes the column past the card instead of shrinking inside it. */
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  grid-template-areas: 'head head' 'score stats' 'body body' 'progress progress';
  align-items: center;
  column-gap: 18px;
  row-gap: 10px;
  flex: 0 0 auto;
  min-height: 0;
  width: 100%;
  box-sizing: border-box;
  padding: 22px 26px;
  border-radius: var(--dh-radius-xl);
  background: var(--dh-panel-08);
  backdrop-filter: blur(16px);
  box-shadow: inset 0 0 0 2px var(--dh-stroke-08);
}
/* At the oche: brighter glass, a heavier ring and a glow that lifts the card
 * off the backdrop, so the turn is obvious from across the room. */
.screen--game .seats .player:not(.player--idle) {
  background: rgba(1, 76, 253, .24);
  box-shadow:
    inset 0 0 0 4px var(--dh-brand-blue),
    inset 0 18px 60px rgba(1, 76, 253, .28),
    0 0 44px rgba(1, 76, 253, .38);
}
.seats .player:not(.player--idle) .player__score { text-shadow: 0 0 34px rgba(66, 155, 252, .5); }
.seats .player__head { grid-area: head; gap: 18px; min-width: 0; }
.seats--compact .player__progress { gap: 5px; }
.seats--compact .player__progress-row { gap: 8px; }
.seats--compact .player__progress-label { font-size: 14px; }
.seats--compact .player__bar { width: 24px; }
.seats--compact .player__bar--set { height: 9px; }
.seats--compact .player__bar--leg { height: 7px; }
/* A long name has a slot, not the whole card: nowrap alone let "Bullseye Bex"
 * run out past the edge. The solo card is a fixed column too, so it clamps
 * the same way. */
.seats .player__who,
.player--solo .player__who { min-width: 0; }
.player--solo .player__head { min-width: 0; }
.seats .player__name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* The solo card has the height a seat does not: a long name wraps there
 * rather than losing its end to an ellipsis. */
.player--solo .player__name { max-width: 100%; white-space: normal; line-height: 1.04; }
.seats .player__score { grid-area: score; }
/* The stats ride beside the score, on its inner side, and the space that
 * frees goes to the route and the leg bar. */
.seats .player__stats { grid-area: stats; justify-self: start; }
.seats .player--right .player__stats,
.seats.seats--right .player .player__stats { justify-self: end; }
.seats.seats--right .player {
  grid-template-columns: minmax(0, 1fr) minmax(0, auto);
  grid-template-areas: 'head head' 'stats score' 'body body' 'progress progress';
}
.seats .x01-checkout { grid-area: body; }
.seats .marks { grid-area: body; }
.seats .leg-progress { grid-area: progress; }

/* How far through the leg, for comparing six cards at a glance. */
.leg-progress {
  position: relative;
  width: 100%;
  height: 16px;
  border-radius: 9999px;
  background: rgba(4, 10, 26, .62);
  box-shadow: inset 0 0 0 2px var(--dh-stroke-08);
  overflow: hidden;
}
.leg-progress__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: 9999px;
  background: linear-gradient(90deg, rgba(1, 76, 253, .55), var(--dh-bright-blue));
}
/* The highest finish: past this line a checkout is on. */
.leg-progress__finish {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: rgba(255, 201, 60, .55);
}
.seats .player--leader .leg-progress__fill {
  background: linear-gradient(90deg, rgba(255, 201, 60, .45), var(--cr-score-here));
}
.seats--compact .leg-progress { height: 12px; }
.seats .x01-checkout { min-height: 0; gap: 10px; }
.seats .player__stats { gap: 26px; }
/* Beside the score the labels have a column, not a paragraph: one line each. */
.seats .player__stat-label { white-space: nowrap; }
.seats .player__stat { gap: 6px; }

/* Two a side — three and four players. */
.seats--tall .player { height: 444px; }
.seats--tall .player__avatar { width: 92px; height: 92px; }
/* 'Bullseye Bex' at 31 needs 300px of a 278px slot: 28 fits it whole. */
.seats--tall .player__name { font-size: 28px; }
.seats--tall .x01__score { font-size: 150px; }
/* Three digits beside a stat column do not fit at the two-digit size: 501 is
 * half again as wide as 81, and the grid sizes the score column to its text. */
.seats--tall .x01__score--wide { font-size: 124px; }
.seats--tall .player__stats { flex-direction: column; gap: 10px; }
.seats--tall .player__stat-value { font-size: 26px; }
.seats--tall .player__stat-label { font-size: 14px; }
.seats--tall .player__stat { gap: 2px; }
.seats--tall .x01-route { gap: 9px; }
.seats--tall .x01-route__step { padding: 8px 16px; font-size: 25px; }
.seats--tall .x01-route__none { padding: 8px 16px; font-size: 20px; }

/* Three a side — five and six players. The card has 289px: the leg pips, the
 * darts count and the "Checkout" label go, because at this size the score and
 * the gold route are what carry across the room. */
.screen--game .seats--compact .player { gap: 8px; padding: 14px 20px; }
.seats--compact .x01-checkout .player__stat-label { display: none; }
.seats--compact .player { height: 289px; }
.seats--compact .player__head { gap: 14px; }
.seats--compact .player__avatar { width: 64px; height: 64px; }
.seats--compact .player__name { font-size: 26px; }
.seats--compact .x01__score { font-size: 118px; }
.seats--compact .x01__score--wide { font-size: 96px; }
/* Same column as the tall tier: a three-digit score beside a row of stats
 * runs past 440. */
.seats--compact .player__stats { flex-direction: column; gap: 7px; }
.seats--compact .player__stat-value { font-size: 22px; }
.seats--compact .player__stat-label { font-size: 13px; }
.seats--compact .player__stat { gap: 2px; }
.seats--compact .player__stat:nth-child(3) { display: none; }
.seats--compact .x01-route { gap: 7px; }
.seats--compact .x01-route__step { padding: 6px 12px; font-size: 21px; }
.seats--compact .x01-route__none { padding: 6px 12px; font-size: 17px; }
.seats--compact .x01-route__arrow { width: 18px; height: 18px; }

/* Waiting their turn. The 1v1 screen only greys the name and the score, which
 * is enough when there is one other card; with five others the whole card has
 * to step back. Everything that glows — the gold route, the blue marks, the
 * leg bar, the avatar — comes down together, and the card surface flattens,
 * so the lit card is the one at the oche. The leader's gold survives it. */
.screen--game .seats .player--idle {
  background: rgba(209, 213, 216, .035);
  box-shadow: inset 0 0 0 2px rgba(209, 213, 216, .06);
}
.seats .player--idle { opacity: .78; }
.seats .player--idle .player__avatar { filter: saturate(.5) brightness(.68); }
.seats .player--idle .player__stat-label,
.seats .player--idle .player__progress-label { color: var(--dh-text-tertiary); }
.seats .player--idle .x01-route__step,
.seats .player--idle .x01-route__none { opacity: .5; }
.seats .player--idle .x01-route__arrow { opacity: .4; }
.seats .player--idle .leg-progress__fill { filter: saturate(.45) brightness(.8); }
.seats .player--idle .leg-progress__finish { opacity: .5; }
.seats .player--idle .marks__cell--hit,
.seats .player--idle .marks__row--closed .marks__cell { filter: saturate(.62) brightness(.86); }
.seats .player--idle .marks__label { opacity: .8; }
/* The leader is still the leader while they sit down. */
.seats .player--leader.player--idle { opacity: .94; }

/* The leader — fewest left in X01, most points in Cricket — carries the gold:
 * an embossed score, and the card's edge too when it isn't their turn (the
 * blue ring has to keep meaning "at the oche", so gold never takes it over).
 * A tie lights nobody, as the 1v1 average rail already works. */
/* The leader is marked by the card's edge, not by its score: a gold number
 * beside five white ones pulled the eye away from whoever was throwing. */
.screen--game .seats .player--leader.player--idle {
  background: rgba(255, 201, 60, .09);
  box-shadow: inset 0 0 0 2px rgba(255, 201, 60, .42), 0 0 30px rgba(255, 201, 60, .10);
}


/* A bot says so, next to the name, in the ink of a label rather than a badge
 * competing with the score. */
.player__bot {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 11px 4px 9px;
  border-radius: 9999px;
  background: var(--dh-panel-08);
  box-shadow: inset 0 0 0 1px var(--dh-stroke-24);
  font-size: 18px;
  line-height: 1;
  letter-spacing: var(--dh-ls-micro);
  color: var(--dh-text-secondary);
  white-space: nowrap;
}
/* The tags share one row under the name, and the row is as tall with a tag in
 * it as without: a bot on one card, or a player coming into range, leaves
 * every card's figures where they were. */
.player__tags { display: flex; align-items: center; gap: 8px; min-height: 28px; }
.player--right .player__who > .player__tags { flex-direction: row-reverse; }
.seats--tall .player__tags { min-height: 26px; }
.seats--compact .player__tags { min-height: 19px; gap: 6px; }
.player__bot svg { flex: none; }
.player--right .player__bot { flex-direction: row-reverse; }
.seats--tall .player__bot { font-size: 16px; padding: 3px 9px 3px 8px; }
.seats--compact .player__bot { font-size: 14px; padding: 2px 8px; gap: 5px; }
.seats--compact .player__bot svg { width: 15px; height: 15px; }

/* ── Shanghai ─────────────────────────────────────────────────────────────
 * The round strip is the game: twenty cells, one per number in the order. A
 * played round fills by how much of the 3× it took, a blank reads grey, the
 * round in play is outlined, and a Shanghai takes the whole cell in gold. */
.sh-strip {
  display: flex;
  gap: 3px;
  width: 100%;
  min-width: 0;
}
/* No tray behind the bar: the round stands on a hairline baseline and the
 * card shows through everything it has not taken. */
.sh-cell {
  /* The label, where it is shown, sits under the axis: the baseline and the
   * bar both stand on top of that strip rather than across it. */
  --sh-label: 0px;
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  height: 44px;
  border-radius: 4px;
  overflow: hidden;
}
.sh-cell::before {
  content: '';
  position: absolute;
  inset: auto 0 var(--sh-label) 0;
  height: 2px;
  background: rgba(209, 213, 216, .14);
}
/* The fill rises from the baseline: nine marks fills the bar. */
.sh-cell::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--sh-label);
  border-radius: 4px 4px 0 0;
  height: calc(var(--sh-fill, 0) * (100% - var(--sh-label)));
  background: linear-gradient(180deg, var(--cr-to-close), rgba(30, 136, 252, .72));
}
/* Thrown and missed: a stub on the baseline, not an empty frame. */
.sh-cell--blank::before { height: 4px; background: rgba(255, 77, 85, .62); }
.sh-cell--now::before { height: 4px; background: var(--dh-bright-blue); box-shadow: 0 0 12px rgba(66, 155, 252, .7); }
.sh-cell--shanghai::after { height: calc(100% - var(--sh-label)); background: var(--cr-score-here); }

.sh-flag {
  align-self: flex-start;
  padding: 8px 18px;
  border-radius: 9999px;
  background: var(--cr-score-here);
  color: #1a1300;
  font-size: 24px;
  line-height: 1;
  letter-spacing: var(--dh-ls-micro);
  box-shadow: 0 0 28px rgba(255, 201, 60, .5);
}
.seats .sh-strip { grid-area: body; gap: 2px; }
/* Twenty numbers in a 388px card is noise at sofa distance, and the nav
 * already says which round and target are in play: the seats keep the run as
 * a bar and leave the labels to the solo card, which has the width for them. */
.seats--tall .sh-cell { height: 84px; }
.seats--compact .sh-cell { height: 56px; }
/* The Shanghai card: a top row of two columns — who they are, and what they
 * have — then the ladders, then the run. */
/* Both columns take the same arrangement — who on the left, figures on the
 * right — rather than mirroring, so a six-card screen reads one way. */
.game--shanghai .seats .player,
.game--shanghai .seats.seats--right .player {
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  grid-template-areas: 'ident figures' 'progress progress' 'body body';
  align-items: start;
  column-gap: 24px;
}
/* The solo column is 396px: an identity column beside a three-digit total
 * leaves neither any room, so solo stacks and gives the run the full width. */
.game--shanghai .player--solo {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: 'ident' 'figures' 'progress' 'body';
  row-gap: 22px;
}
.game--shanghai .player--solo .sh-ident { flex-direction: row; align-items: center; gap: 20px; }
.game--shanghai .player--solo .player__name { font-size: 34px; }
.sh-ident {
  grid-area: ident;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
/* The total and its rates sit against the card's right edge: the identity
 * column is the ragged side, the figures the straight one. */
.sh-figures {
  grid-area: figures;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  min-width: 0;
}
.game--shanghai .player--solo .sh-figures { align-items: flex-start; }
.sh-ident .player__bot { align-self: flex-start; }
.game--shanghai .player__progress { grid-area: progress; }
.game--shanghai .sh-strip { grid-area: body; }
.sh-ident .player__name { line-height: 1.04; white-space: normal; }
/* Every card on the screen keeps the room the longest name needs, and the
 * room a bot's chip needs when a bot is playing: a name on two lines or a
 * chip on one card must not push that card's figures out of line with the
 * card across the board. */
.sh-ident .player__name { min-height: calc(var(--name-rows, 1) * 1.04em); }
.game--shanghai .seats--tall .player__avatar { width: 112px; height: 112px; }
.game--shanghai .seats--tall .player__name { font-size: 23px; }
.game--shanghai .seats--compact .player__avatar { width: 78px; height: 78px; }
.game--shanghai .seats--compact .player__name { font-size: 24px; }

/* Shanghai totals are three digits beside two rates, so the score takes the
 * seat sizes rather than the card's default 150. */
.game--shanghai .seats .player__stats { flex-direction: row; gap: 18px; }
.game--shanghai .seats--tall .player__score { font-size: 92px; }
.game--shanghai .seats--compact .player__score { font-size: 84px; }
.game--shanghai .seats--compact .player__stats { gap: 14px; }
.game--shanghai .seats--compact .player__stat-value { font-size: 22px; }
.game--shanghai .seats--compact .player__stat-label { font-size: 13px; }
.game--shanghai .seats--compact .sh-cell { height: 48px; }
.game--shanghai .seats--compact .sh-flag { padding: 5px 12px; font-size: 17px; }
.game--shanghai .seats--tall .sh-flag { padding: 6px 14px; font-size: 20px; }

/* Solo: the rounds in full, in the other column. */
.player--solo .sh-strip { width: 396px; gap: 3px; }
.player--solo .sh-cell { height: 104px; }
.game--shanghai .player--solo .player__score { font-size: 170px; }
.rounds-panel {
  position: absolute;
  right: 44px;
  top: 196px;
  width: 420px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.rounds__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.rounds__row {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 11px 18px;
  border-radius: var(--dh-radius-m);
  background: var(--dh-panel-08);
  backdrop-filter: blur(16px);
}
.rounds__row--now { background: rgba(1, 76, 253, .17); box-shadow: inset 0 0 0 2px var(--dh-brand-blue); }
.rounds__n { flex: 1; font-size: 19px; letter-spacing: var(--dh-ls-micro); color: var(--dh-text-secondary); white-space: nowrap; }
.rounds__target { font-size: 26px; color: var(--dh-bright-blue); }
.rounds__score { font-size: 38px; line-height: 1; color: #fff; font-variant-numeric: tabular-nums; }
.rounds__score--max { color: var(--cr-score-here); }
.rounds__of { font-size: 19px; color: var(--dh-text-secondary); white-space: nowrap; }

/* ── Solo: one player, and the leg in the other column ────────────────────
 * Nobody to compare against, so the second column carries the leg so far —
 * the sheet a practising player otherwise keeps in their head. */
/* The board is 980 wide and centred, so both columns stop short of 470px. */
.player--solo .player__avatar { width: 120px; height: 120px; }
.player--solo .player__name { font-size: 46px; }
.player--solo .x01__score { font-size: 200px; }
.player--solo .player__stat-value { font-size: 62px; }

/* Solo Cricket: identity, then the points with the rates beside them, then
 * the seven targets as full-width bars — the same anatomy as a seat card, at
 * the size one player can afford. The column stops short of the board at 470. */
.game--cricket .player--solo {
  display: grid;
  width: 396px;
  grid-template-columns: auto 1fr;
  grid-template-areas: 'head head' 'score rates' 'marks marks';
  align-items: center;
  column-gap: 24px;
  row-gap: 26px;
}
.game--cricket .player--solo .player__head { grid-area: head; }
/* The marks want the width, so the identity block gives some back: at 34 a
 * two-word name holds one line inside the 396 column. */
.game--cricket .player--solo .player__avatar { width: 96px; height: 96px; }
.game--cricket .player--solo .player__name { font-size: 34px; }
.game--cricket .player--solo .player__score { grid-area: score; font-size: 130px; }
.game--cricket .player--solo .player__stats { grid-area: rates; justify-self: start; flex-direction: column; gap: 10px; }
.game--cricket .player--solo .player__stat-value { font-size: 40px; }
.game--cricket .player--solo .marks { grid-area: marks; width: 100%; }
.game--cricket .player--solo .marks__row { height: 46px; }
.game--cricket .player--solo .marks__cell,
.game--cricket .player--solo .marks__dead { height: 26px; }

.leglog {
  position: absolute;
  right: 44px;
  top: 196px;
  width: 420px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.aside__head { display: flex; justify-content: space-between; align-items: baseline; }
.leglog__title { font-size: 34px; line-height: 1; color: #fff; }
.leglog__hint { font-size: 20px; line-height: 1; letter-spacing: var(--dh-ls-micro); color: var(--dh-text-secondary); }
.aside__rule { height: 1px; background: var(--dh-stroke-24); }
.leglog__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.leglog__visit {
  display: flex;
  backdrop-filter: blur(16px);
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  padding: 12px 18px;
  border-radius: var(--dh-radius-m);
  background: var(--dh-panel-08);
}
.leglog__visit--now { background: rgba(1, 76, 253, .17); box-shadow: inset 0 0 0 2px var(--dh-brand-blue); }
.leglog__n { font-size: 19px; white-space: nowrap; letter-spacing: var(--dh-ls-micro); color: var(--dh-text-secondary); }
.leglog__score { font-size: 44px; line-height: 1; color: #fff; font-variant-numeric: tabular-nums; }
.leglog__score--ton { color: var(--cr-score-here); }
.leglog__left { font-size: 23px; line-height: 1; white-space: nowrap; color: var(--dh-text-secondary); font-variant-numeric: tabular-nums; }
.aside__foot { display: flex; gap: 40px; padding-top: 4px; }

/* Cricket in seats: the marks are a grid from three players up (decided
 * 2026-09-29), so the card has the height for the points as well. */
.game--cricket .seats .player { row-gap: 8px; }
/* Cricket has two rates, not three stats, and the seven rows need the height
 * more than the rates need a column: they sit side by side beside the points,
 * which is what keeps the 15 row inside the card. */
.game--cricket .seats .player__stats { flex-direction: row; gap: 20px; }
.game--cricket .seats .player__stat { gap: 2px; }
.game--cricket .seats .marks { width: 100%; }
.game--cricket .seats--tall .player__avatar { width: 72px; height: 72px; }
.game--cricket .seats--tall .player__name { font-size: 30px; }
.game--cricket .seats--tall .player__score { font-size: 104px; }
.game--cricket .seats--tall .player__stat-value { font-size: 26px; }
.game--cricket .seats--tall .player__stat-label { font-size: 15px; }
.game--cricket .seats--tall .player__stat { gap: 3px; }

.game--cricket .seats--compact .player { row-gap: 8px; padding: 12px 20px; }
.game--cricket .seats--compact .player__avatar { width: 52px; height: 52px; }
.game--cricket .seats--compact .player__name { font-size: 24px; }
.game--cricket .seats--compact .player__score { font-size: 84px; }
/* Three players and up: the seven rows lie down into an axis, 15 to Bull.
 * Each target is a column of three marks stacked bottom-up, with its label
 * under the axis — one way of reading the marks at every seat count from
 * three, and one that reads from the sofa where a 17px row did not. It also
 * gives back the height the points and the rates want. */
.game--cricket .seats .marks { flex-direction: row; gap: 9px; }
.game--cricket .seats .marks__row,
.game--cricket .seats .player--right .marks__row {
  flex: 1 1 0;
  min-width: 0;
  flex-direction: column-reverse;
  align-items: stretch;
  justify-content: flex-start;
  height: auto;
  gap: 6px;
}
.game--cricket .seats .marks__label,
.game--cricket .seats .player--right .marks__label {
  width: auto;
  font-size: 22px;
  line-height: 22px;
  text-align: center;
}
/* Both columns say it: the mirrored-label rule above would otherwise set the
 * right-hand cards' Bull at the size of a number. */
.game--cricket .seats .marks__label--bull,
.game--cricket .seats .player--right .marks__label--bull { font-size: 17px; }
/* A four-letter word needs more column than a two-digit number. Both columns
 * say it, so this has to outrank the mirrored-row rule above. */
.game--cricket .seats .marks__row--bull,
.game--cricket .seats .player--right .marks__row--bull { flex: 1.5 1 0; }
/* The marks fill bottom-up, so the tower grows towards the number as it closes. */
.game--cricket .seats .marks__cells,
.game--cricket .seats .player--right .marks__cells { flex: 0 0 auto; flex-direction: column-reverse; gap: 3px; }
/* Stacked, the pips keep their own height instead of sharing the row's. */
.game--cricket .seats .marks__cell { flex: 0 0 auto; height: 19px; border-radius: 5px; }
.game--cricket .seats .marks__dead { flex: 0 0 auto; height: 63px; border-radius: 5px; }
.game--cricket .seats .marks__cells--scorable { padding: 1px; }
/* The tick and the dead cross have no column to sit in: the gold fill and the
 * struck-through bar already say closed and dead. */
.game--cricket .seats .marks__end { display: none; }
/* Two a side has the height for a bigger tower and a label to match. */
.game--cricket .seats--tall .marks { gap: 9px; }
/* 'Bull' at the tall label size is wider than a column and a half. */
.game--cricket .seats--tall .marks__row--bull,
.game--cricket .seats--tall .player--right .marks__row--bull { flex: 2.2 1 0; }
.game--cricket .seats--tall .marks__row,
.game--cricket .seats--tall .player--right .marks__row { gap: 10px; }
.game--cricket .seats--tall .marks__cells,
.game--cricket .seats--tall .player--right .marks__cells { gap: 5px; }
.game--cricket .seats--tall .marks__cell { height: 36px; border-radius: 7px; }
.game--cricket .seats--tall .marks__dead { height: 118px; border-radius: 7px; }
.game--cricket .seats--tall .marks__label,
.game--cricket .seats--tall .player--right .marks__label { font-size: 28px; line-height: 28px; }
.game--cricket .seats--tall .marks__label--bull,
.game--cricket .seats--tall .player--right .marks__label--bull { font-size: 16px; }

/* Solo Cricket: what is still open, and how many marks each still wants. */
.toclose {
  position: absolute;
  right: 44px;
  top: 196px;
  width: 420px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.toclose__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.toclose__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 12px 18px;
  border-radius: var(--dh-radius-m);
  background: var(--dh-panel-08);
  backdrop-filter: blur(16px);
}
.toclose__target { min-width: 104px; font-size: 40px; line-height: 1; color: var(--cr-to-close); }
.toclose__target--bull { font-size: 30px; }
.toclose__pips { display: flex; gap: 8px; }
.toclose__pip {
  display: block;
  width: 34px;
  height: 16px;
  border-radius: 5px;
  background: rgba(4, 10, 26, .62);
  box-shadow: inset 0 0 0 2px rgba(209, 213, 216, .15);
}
.toclose__pip--hit { background: var(--cr-to-close); box-shadow: 0 0 14px #1e88fc88; }
.toclose__left { font-size: 22px; line-height: 1; white-space: nowrap; color: var(--dh-text-secondary); }

/* ── X01 gameplay (X01 Cast Stage) ────────────────────────────────────────── */
/* Three-digit scores sit at 140px, not Cricket's 150, to clear the board's numbers. */
.x01__score { font-size: 140px; }
.screen--game .player { gap: 30px; }

.x01-checkout { display: flex; flex-direction: column; gap: 14px; min-height: 100px; }
.player--left .x01-checkout { align-items: flex-start; }
.player--right .x01-checkout { align-items: flex-end; }
.x01-route { display: flex; align-items: center; gap: 14px; }
/* The chip, as the overlay design system draws it: white label on the
 * segment's colour at 22%, a 2px border of it at 85%, radius 10, padding
 * 14/26. Not a gold pill — that was this screen's own invention. */
.x01-route__step {
  display: grid;
  place-items: center;
  padding: 14px 26px;
  border-radius: 10px;
  font-size: 34px;
  line-height: 1;
  color: #fff;
  background: rgba(255, 201, 60, .22);
  border: 2px solid rgba(255, 201, 60, .85);
}
/* The bed for the next dart is called out on the board; the chips stay a set. */
.x01-route__step--now { box-shadow: 0 0 22px rgba(255, 201, 60, .35); }
/* Waiting: the same route, stepped back — outline only, and no lit first bed,
 * which belongs to whoever is at the oche. */
.x01-route--waiting .x01-route__step {
  color: rgba(255, 255, 255, .72);
  background: rgba(255, 201, 60, .12);
  border-color: rgba(255, 201, 60, .42);
  box-shadow: none;
}
.x01-route--waiting .x01-route__arrow { opacity: .45; }

.x01-route__none {
  display: flex;
  align-items: center;
  padding: 14px 26px;
  border-radius: 10px;
  font-size: 26px;
  letter-spacing: var(--dh-ls-micro);
  color: var(--dh-text-tertiary);
  background: rgba(209, 213, 216, .06);
  border: 2px solid rgba(209, 213, 216, .18);
}

.x01-trend {
  width: 400px;
  padding: 14px 18px 12px;
  border-radius: var(--dh-radius-m);
  background: rgba(4, 10, 26, .32);
  box-shadow: inset 0 0 0 2px rgba(209, 213, 216, .08);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.x01-trend--lead { background: rgba(255, 201, 60, .07); box-shadow: inset 0 0 0 2px rgba(255, 201, 60, .26); }
.x01-trend__head { display: flex; justify-content: space-between; align-items: baseline; }
.player--right .x01-trend__head { flex-direction: row-reverse; }
.x01-trend__title { font-size: 20px; line-height: 1; letter-spacing: .09em; color: var(--dh-text-tertiary); }
.x01-trend__value { font-size: 44px; line-height: 1; color: var(--dh-text-primary); font-variant-numeric: tabular-nums; }
.x01-trend--lead .x01-trend__title { color: rgba(255, 201, 60, .9); }
.x01-trend--lead .x01-trend__value { color: #ffc93c; }
.x01-trend__chart { display: block; }
.x01-trend__foot { font-size: 16px; line-height: 1; letter-spacing: .06em; color: var(--dh-text-tertiary); }
.player--right .x01-trend__foot { text-align: right; }
.x01__hot { color: var(--dh-bright-blue) !important; }
/* A busted visit: the same red the busted darts carry in the navbar. */
.x01__bust { color: #fb6161 !important; font-size: .82em; letter-spacing: .06em; }

/* Moments play over the board; gameplay stays visible around them. */
.moment {
  --ring: var(--dh-bright-blue);
  --glow: rgba(66, 155, 252, .55);
  position: absolute;
  left: 50%;
  top: 600px;
  width: 760px;
  height: 760px;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  pointer-events: none;
  z-index: 5;
}
.moment[hidden] { display: none; }
.moment--gold { --ring: #ffc93c; --glow: rgba(255, 201, 60, .55); }
.moment--bust { --ring: #fb6161; --glow: rgba(209, 18, 18, .55); }
.moment__medal {
  position: relative;
  width: 520px;
  height: 520px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  text-align: center;
  background: radial-gradient(circle at 50% 38%, #123a8f 0%, #06164a 62%, #030b27 100%);
  box-shadow: inset 0 0 0 10px var(--ring), inset 0 0 0 16px rgba(0, 0, 0, .35), 0 0 120px var(--glow);
}
.moment--bust .moment__medal { background: radial-gradient(circle at 50% 38%, #5a0d16 0%, #2a0610 62%, #14030a 100%); }
.moment__big { font-size: 190px; line-height: .9; color: #fff; text-shadow: 0 0 40px var(--glow); }
.moment__cap { margin-top: 18px; font-size: 34px; letter-spacing: .22em; color: var(--ring); }
.moment__sub { margin-top: 10px; font-size: 26px; letter-spacing: .12em; color: var(--dh-text-primary); }
.moment__rays {
  position: absolute;
  inset: -130px;
  border-radius: 50%;
  opacity: 0;
  background: repeating-conic-gradient(from 0deg, var(--glow) 0deg 4deg, transparent 4deg 18deg);
  -webkit-mask: radial-gradient(circle, transparent 42%, #000 50%, transparent 72%);
  mask: radial-gradient(circle, transparent 42%, #000 50%, transparent 72%);
}
.moment--play .moment__medal { animation: x01-medal var(--moment-ms, 2200ms) var(--dh-ease-out) both; }
.moment--play .moment__rays { animation: x01-rays var(--moment-ms, 2200ms) linear both; }
@keyframes x01-medal {
  0% { transform: scale(.2); opacity: 0 }
  14% { transform: scale(1.1); opacity: 1 }
  22% { transform: scale(1) }
  82% { transform: scale(1); opacity: 1 }
  100% { transform: scale(.9); opacity: 0 }
}
@keyframes x01-rays {
  0% { transform: rotate(0) scale(.6); opacity: 0 }
  20% { opacity: .55 }
  100% { transform: rotate(40deg) scale(1.08); opacity: 0 }
}

/* Checkout route steps on the board: the next bed steps up when the dart
 * before it hit; an abandoned plan fades out. */
.x01__board .route--activate { animation: x01-route-activate 520ms var(--dh-ease-out) both; }
.x01__board .route--fading { animation: x01-route-out 700ms var(--dh-ease-out) both; }
@keyframes x01-route-activate { 0% { opacity: .36 } 45% { opacity: 1; filter: brightness(1.6) } 100% { opacity: 1; filter: none } }
@keyframes x01-route-out { from { opacity: 1 } to { opacity: 0 } }
@media (prefers-reduced-motion: reduce) {
  .x01__board .route, .moment--play .moment__medal, .moment--play .moment__rays { animation: none !important; }
  .x01__board .route--fading { display: none; }
}

/* ── X01 score bands (score overlays handoff, LOCKED) ─────────────────────── */
/* Keyframes verbatim from the Score Bands prototype, renamed dh-* → sb-* so
 * they can't collide with the screens' own dh-flash. */
.band { position: absolute; inset: 0; z-index: 6; pointer-events: none; }
@keyframes sb-dim { 0% { opacity:0 } 14% { opacity:1 } 82% { opacity:1 } 100% { opacity:0 } }
@keyframes sb-flash { 0% { opacity:0 } 5% { opacity:1 } 26% { opacity:0 } 100% { opacity:0 } }
@keyframes sb-glow { 0% { opacity:0; transform:translate(-50%,-50%) scale(.35) } 16% { opacity:1; transform:translate(-50%,-50%) scale(1.06) } 30% { transform:translate(-50%,-50%) scale(1) } 80% { opacity:1 } 100% { opacity:0; transform:translate(-50%,-50%) scale(1.04) } }
@keyframes sb-wave { 0% { opacity:0; transform:translate(-50%,-50%) scale(.22) } 8% { opacity:1 } 62% { opacity:.5 } 100% { opacity:0; transform:translate(-50%,-50%) scale(1) } }
@keyframes sb-med-stamp { 0% { opacity:0; transform:scale(2.3) rotate(-11deg) } 13% { opacity:1; transform:scale(.955) rotate(-3deg) } 21% { transform:scale(1.03) rotate(-3deg) } 31% { transform:scale(1) rotate(-3deg) } 84% { opacity:1; transform:scale(1) rotate(-3deg) } 100% { opacity:0; transform:scale(1.02) rotate(-3deg) } }
@keyframes sb-digit-stamp { 0% { opacity:0; transform:scale(2.6) rotate(9deg) } 12% { opacity:1; transform:scale(.94) rotate(2.5deg) } 20% { transform:scale(1.04) rotate(2.5deg) } 30% { transform:scale(1) rotate(2.5deg) } 84% { opacity:1; transform:scale(1) rotate(2.5deg) } 100% { opacity:0; transform:scale(1.02) rotate(2.5deg) } }
@keyframes sb-ink2 { 0% { opacity:0; transform:translate(-50%,-50%) scale(.4) } 12% { opacity:0 } 22% { opacity:.9; transform:translate(-50%,-50%) scale(1) } 56% { opacity:0; transform:translate(-50%,-50%) scale(1.6) } 100% { opacity:0 } }
@keyframes sb-cap { 0% { opacity:0; transform:translateY(24px) } 34% { opacity:1; transform:translateY(0) } 84% { opacity:1 } 100% { opacity:0 } }
@keyframes sb-shake-1 { 0%,100% { transform:translate(0,0) } 10% { transform:translate(-4px,2px) } 22% { transform:translate(3px,-2px) } 34% { transform:translate(-2px,-1px) } 46% { transform:translate(0,0) } }
@keyframes sb-shake-2 { 0%,100% { transform:translate(0,0) } 8% { transform:translate(-7px,4px) } 18% { transform:translate(6px,-4px) } 28% { transform:translate(-4px,-3px) } 38% { transform:translate(3px,2px) } 48% { transform:translate(0,0) } }
@keyframes sb-shake-3 { 0%,100% { transform:translate(0,0) } 6% { transform:translate(-12px,8px) } 14% { transform:translate(10px,-7px) } 22% { transform:translate(-8px,-5px) } 30% { transform:translate(6px,4px) } 40% { transform:translate(-3px,2px) } 50% { transform:translate(0,0) } }
@keyframes sb-shake-4 { 0%,100% { transform:translate(0,0) } 5% { transform:translate(-17px,11px) } 11% { transform:translate(14px,-10px) } 17% { transform:translate(-12px,-7px) } 23% { transform:translate(10px,7px) } 30% { transform:translate(-7px,4px) } 38% { transform:translate(4px,-3px) } 48% { transform:translate(0,0) } }
@keyframes sb-shake-5 { 0%,100% { transform:translate(0,0) } 4% { transform:translate(-23px,15px) } 9% { transform:translate(20px,-13px) } 14% { transform:translate(-17px,-10px) } 19% { transform:translate(14px,10px) } 25% { transform:translate(-10px,6px) } 32% { transform:translate(7px,-5px) } 40% { transform:translate(-3px,2px) } 50% { transform:translate(0,0) } }

/* Big fish. The handoff lands the medallion and stops, which is the quietest
 * of the four art states for the biggest checkout on the board. It breaches
 * instead: up through the surface with a roll and an overshoot, and the flare,
 * the rings, the jets and the drops all wait for the moment it breaks through
 * rather than starting with it. */
@keyframes bf-leap {
  0% { opacity:0; transform:translateY(420px) scale(.72) rotate(-9deg) }
  8% { opacity:1 }
  20% { transform:translateY(-46px) scale(1.06) rotate(4deg) }
  28% { transform:translateY(10px) scale(.99) rotate(-2deg) }
  36% { transform:translateY(0) scale(1) rotate(0deg) }
  86% { opacity:1; transform:translateY(0) scale(1) rotate(0deg) }
  100% { opacity:0; transform:translateY(-26px) scale(1.02) }
}
@keyframes bf-water { 0% { opacity:0 } 14% { opacity:1 } 84% { opacity:.8 } 100% { opacity:0 } }
@keyframes bf-flare {
  0% { opacity:0; transform:scaleX(.24) scaleY(.2) }
  14% { opacity:1; transform:scaleX(.9) scaleY(1.5) }
  36% { opacity:.95; transform:scaleX(1.06) scaleY(1) }
  100% { opacity:0; transform:scaleX(1.16) scaleY(.6) }
}
@keyframes bf-ripple {
  0% { opacity:0; transform:translate(-50%,-50%) scale(.16) }
  12% { opacity:1 }
  66% { opacity:.45 }
  100% { opacity:0; transform:translate(-50%,-50%) scale(1) }
}
@keyframes bf-jet {
  0% { opacity:0; transform:translateY(-100%) rotate(var(--jr)) scaleY(.1) }
  14% { opacity:1; transform:translateY(-100%) rotate(var(--jr)) scaleY(1) }
  60% { opacity:.7 }
  100% { opacity:0; transform:translateY(-100%) rotate(var(--jr)) scaleY(1.2) }
}
/* The gold half of the breach: a starburst behind the fish, a bloom of light
 * off it, three rings out, and confetti that keeps falling after all of it. */
@keyframes bf-burst {
  0% { opacity:0; transform:scale(.2) rotate(-14deg) }
  14% { opacity:1; transform:scale(1.02) rotate(0deg) }
  100% { opacity:0; transform:scale(1.3) rotate(10deg) }
}
@keyframes bf-bloom {
  0% { opacity:0; transform:scale(.24) }
  16% { opacity:1; transform:scale(1.04) }
  100% { opacity:0; transform:scale(1.22) }
}
@keyframes bf-goldring {
  0% { opacity:0; transform:scale(.12) }
  10% { opacity:1 }
  70% { opacity:.5 }
  100% { opacity:0; transform:scale(1) }
}
@keyframes bf-conf {
  0% { opacity:0; transform:translateY(0) rotate(0deg) }
  6% { opacity:1 }
  84% { opacity:1 }
  100% { opacity:0; transform:translateY(var(--cy)) rotate(var(--cr)) }
}
@keyframes bf-droplet {
  0% { opacity:0; transform:translate(0,0) scale(.3) }
  10% { opacity:1 }
  62% { opacity:.9 }
  100% { opacity:0; transform:translate(var(--dx),var(--dy)) scale(1) }
}

/* Double double finish: two beds arriving from opposite sides to meet in the
 * middle. The right chip comes in on a hard ease-in so it arrives fast, and
 * where it lands is the moment. */
@keyframes dd-dim { 0% { opacity:0 } 10% { opacity:1 } 86% { opacity:1 } 100% { opacity:0 } }
@keyframes dd-out { 0%, 86% { opacity:1 } 100% { opacity:0 } }
@keyframes dd-flash { 0% { opacity:0 } 6% { opacity:.75 } 100% { opacity:0 } }
@keyframes dd-wave { 0% { opacity:0; transform:translate(-50%,-50%) scale(.2) } 10% { opacity:1 } 100% { opacity:0; transform:translate(-50%,-50%) scale(1) } }
@keyframes dd-cap { 0% { opacity:0; transform:translateY(24px) } 20% { opacity:1; transform:translateY(0) } 84% { opacity:1 } 100% { opacity:0 } }
@keyframes dd-spin { 0% { transform:rotateY(0deg) } 100% { transform:rotateY(360deg) } }
@keyframes dd-chip-l { 0% { opacity:0; transform:translateX(-1200px) } 70% { opacity:1; transform:translateX(12px) } 100% { opacity:1; transform:translateX(0) } }
@keyframes dd-chip-r { 0% { opacity:0; transform:translateX(1200px) } 100% { opacity:1; transform:translateX(0) } }
@keyframes dd-jolt { 0%, 100% { transform:translateX(0) } 20% { transform:translateX(-34px) } 50% { transform:translateX(6px) } }
@keyframes dd-shake { 0%, 100% { transform:translate(0,0) } 6% { transform:translate(-14px,9px) } 14% { transform:translate(12px,-8px) } 22% { transform:translate(-9px,-6px) } 32% { transform:translate(6px,4px) } 44% { transform:translate(-3px,2px) } 56% { transform:translate(0,0) } }

/* Ton+ finish: the scope. Letterbox bars, a reticle gliding bed to bed, the
 * segments lighting as it locks and staying lit. Every move is slow-fast-slow,
 * and nothing is loud until the double lands. The reticle's own path is built
 * per checkout and ships with the overlay. */
@keyframes tp-dim { 0% { opacity:0 } 12% { opacity:1 } 88% { opacity:1 } 100% { opacity:0 } }
@keyframes tp-out { 0%, 88% { opacity:1 } 100% { opacity:0 } }
@keyframes tp-in { 0% { opacity:0; transform:translateY(18px) } 100% { opacity:1; transform:translateY(0) } }
@keyframes tp-fade-in { 0% { opacity:0 } 100% { opacity:1 } }
@keyframes tp-gone { 0% { opacity:1 } 100% { opacity:0 } }
@keyframes tp-recede { 0% { opacity:1 } 100% { opacity:.18 } }
@keyframes tp-wave { 0% { opacity:0; transform:translate(-50%,-50%) scale(.2) } 10% { opacity:1 } 100% { opacity:0; transform:translate(-50%,-50%) scale(1) } }
@keyframes tp-flash { 0% { opacity:0 } 6% { opacity:.5 } 100% { opacity:0 } }
@keyframes tp-ghost { 0% { opacity:0; transform:scale(1.6) rotate(-6deg) } 14% { opacity:1; transform:scale(.97) rotate(-3deg) } 24% { transform:scale(1) rotate(-3deg) } 86% { opacity:1; transform:scale(1) rotate(-3deg) } 100% { opacity:0; transform:scale(1) rotate(-3deg) } }
@keyframes tp-sharp { 0% { opacity:0; transform:scale(1.8) rotate(6deg) } 14% { opacity:1; transform:scale(.97) rotate(2deg) } 24% { transform:scale(1) rotate(2deg) } 86% { opacity:1; transform:scale(1) rotate(2deg) } 100% { opacity:0; transform:scale(1) rotate(2deg) } }
@keyframes tp-cap { 0% { opacity:0; transform:translateY(20px) } 18% { opacity:1; transform:translateY(0) } 84% { opacity:1 } 100% { opacity:0 } }
@keyframes tp-deep { 0%, 100% { transform:translate(0,0) } 6% { transform:translate(-10px,12px) } 16% { transform:translate(8px,-7px) } 28% { transform:translate(-4px,4px) } 42% { transform:translate(2px,-1px) } 60% { transform:translate(0,0) } }
@keyframes tp-bar-t { 0% { transform:translateY(-100%) } 100% { transform:translateY(0) } }
@keyframes tp-bar-b { 0% { transform:translateY(100%) } 100% { transform:translateY(0) } }
@keyframes tp-snap-t { 0% { transform:translateY(0) } 40% { transform:translateY(26px) } 100% { transform:translateY(0) } }
@keyframes tp-snap-b { 0% { transform:translateY(0) } 40% { transform:translateY(-26px) } 100% { transform:translateY(0) } }
/* The lit segment flashes, dips, and holds: it has to stay readable under the
 * number that prints over it. */
@keyframes tp-seg { 0% { opacity:0 } 8% { opacity:1 } 30% { opacity:.55 } 45% { opacity:.9 } 100% { opacity:.8 } }
@keyframes tp-seg-edge { 0% { opacity:0; stroke-width:6 } 20% { opacity:1 } 100% { opacity:1; stroke-width:1.6 } }
@keyframes tp-lock { 0% { transform:scale(1.5); opacity:.4 } 60% { transform:scale(.94); opacity:1 } 100% { transform:scale(1); opacity:1 } }

/* ── Marks per round (marks-per-round handoff) ────────────────────────────── */
/* The visit print for the games that count in marks. Everything else it needs
 * — dim, glow, waves, the two-beat stamp, the caption, the shake — is the
 * score-band grammar above; these three are its own. */
.marks-band { position: absolute; inset: 0; z-index: 6; pointer-events: none; }
@keyframes mk-tile {
  0% { opacity:0; transform:translateY(-40px) scale(1.5) }
  9% { opacity:1; transform:translateY(0) scale(.94) }
  14% { transform:scale(1.03) }
  19% { transform:scale(1) }
  86% { opacity:1; transform:scale(1) }
  100% { opacity:0; transform:scale(.96) }
}
/* The glyph draws itself: one slash for a single, a cross for a double, a
 * cross in a ring for a treble. */
@keyframes mk-draw { 0% { stroke-dashoffset:1 } 100% { stroke-dashoffset:0 } }
@keyframes mk-pop { 0% { opacity:0; transform:translate(-50%,-50%) scale(.4) } 20% { opacity:.9 } 100% { opacity:0; transform:translate(-50%,-50%) scale(1.9) } }
@media (prefers-reduced-motion: reduce) {
  .marks-band [style*="mk-tile"], .marks-band [style*="mk-pop"] { animation: none }
}

/* ── Elimination (elimination handoff) ────────────────────────────────────── */
/* The one overlay that happens to a card rather than to the middle of the
 * screen. The knock and the score match are classes on the elements
 * themselves, so a screen re-rendering mid-overlay cannot lose them; the rest
 * is drawn in the layer, from the card's own rect. */
.elim-band { position: absolute; inset: 0; z-index: 6; pointer-events: none; }
.el-hit { animation: var(--el-hit) }
.el-matching .player__score { animation: var(--el-match) }
/* From the strike, the overlay owns the number: the card's own score would
 * otherwise sit under the shattered copy and the nought at the same time. */
.el-struck .player__score { opacity: 0 }
@keyframes el-match { 0%, 100% { color:#fff; text-shadow:none } 20%, 70% { color:var(--mc); text-shadow:0 0 30px var(--mc) } }
@keyframes el-lock { 0% { opacity:0; transform:scale(1.5) rotate(12deg) } 60% { opacity:1; transform:scale(.97) rotate(0deg) } 72% { transform:scale(1.02) } 100% { opacity:1; transform:scale(1) } }
@keyframes el-lock-out { 0% { opacity:1 } 100% { opacity:0; transform:scale(1.08) } }
@keyframes el-chip { 0% { opacity:0; transform:translate(-50%,16px) scale(.85) } 30% { opacity:1; transform:translate(-50%,0) scale(1.06) } 42% { transform:translate(-50%,0) scale(1) } 84% { opacity:1 } 100% { opacity:0 } }
/* Each digit leaves on its own line, and does not come back. */
@keyframes el-shatter { 0% { opacity:1; transform:translate(0,0) rotate(0deg) } 100% { opacity:0; transform:translate(var(--dx),var(--dy)) rotate(var(--dr)) scale(.7) } }
@keyframes el-card-hit { 0% { transform:translate(0,0) } 8% { transform:translate(-14px,8px) } 18% { transform:translate(11px,-6px) } 30% { transform:translate(-7px,4px) } 44% { transform:translate(4px,-2px) } 60%, 100% { transform:translate(0,0) } }
@keyframes el-card-flash { 0% { opacity:0 } 6% { opacity:1 } 40% { opacity:.35 } 100% { opacity:0 } }
@keyframes el-title { 0% { opacity:0; transform:scale(1.7) } 10% { opacity:1; transform:scale(.95) } 16% { transform:scale(1.03) } 22% { transform:scale(1) } 84% { opacity:1; transform:scale(1) } 100% { opacity:0; transform:scale(1.02) } }
@keyframes el-slot { 0% { opacity:0; transform:scale(.6) } 100% { opacity:1; transform:scale(1) } }
@keyframes el-slot-new { 0% { opacity:0; transform:scale(2.4) rotate(-40deg) } 40% { opacity:1; transform:scale(.9) rotate(4deg) } 60% { transform:scale(1.08) rotate(0deg) } 100% { opacity:1; transform:scale(1) } }
@keyframes el-out { 0% { opacity:0; transform:translateY(10px) } 100% { opacity:1; transform:translateY(0) } }
/* Two more frame quakes, carrying the screen's centring like the rest. */
@keyframes sb-quake-2 {
  0%, 100% { transform: translate(-50%, -50%) }
  8% { transform: translate(calc(-50% - 7px), calc(-50% + 4px)) }
  18% { transform: translate(calc(-50% + 6px), calc(-50% - 4px)) }
  28% { transform: translate(calc(-50% - 4px), calc(-50% - 3px)) }
  38% { transform: translate(calc(-50% + 3px), calc(-50% + 2px)) }
  48% { transform: translate(-50%, -50%) }
}
@keyframes sb-quake-3 {
  0%, 100% { transform: translate(-50%, -50%) }
  6% { transform: translate(calc(-50% - 12px), calc(-50% + 8px)) }
  14% { transform: translate(calc(-50% + 10px), calc(-50% - 7px)) }
  22% { transform: translate(calc(-50% - 8px), calc(-50% - 5px)) }
  30% { transform: translate(calc(-50% + 6px), calc(-50% + 4px)) }
  40% { transform: translate(calc(-50% - 3px), calc(-50% + 2px)) }
  50% { transform: translate(-50%, -50%) }
}
@media (prefers-reduced-motion: reduce) {
  .el-hit, .el-matching .player__score { animation: none }
}

/* ── Around the Clock combos (around-the-clock handoff) ───────────────────── */
/* The streak print: twenty nodes on a ring, lighting in the order they were
 * hit, with an arc drawn along the track behind them. Everything after the
 * chain — dim, glow, waves, the two-beat stamp, the caption, the shake — is
 * the score-band grammar above. */
.combo-band { position: absolute; inset: 0; z-index: 6; pointer-events: none; }
@keyframes ac-ring { 0% { opacity:0; transform:scale(1.14) rotate(-10deg) } 8% { opacity:1; transform:scale(1) rotate(0deg) } 88% { opacity:1; transform:scale(1) } 100% { opacity:0; transform:scale(.97) } }
@keyframes ac-node { 0% { transform:scale(1) } 30% { transform:scale(1.38) } 60% { transform:scale(.94) } 100% { transform:scale(1) } }
@keyframes ac-lit { 0% { opacity:0 } 100% { opacity:1 } }
@keyframes ac-arc { 0% { stroke-dashoffset:var(--len) } 100% { stroke-dashoffset:0 } }
@keyframes ac-spark { 0% { opacity:0; transform:rotate(var(--a)) translateY(-300px) scaleY(.2) } 16% { opacity:1 } 100% { opacity:0; transform:rotate(var(--a)) translateY(-560px) scaleY(1) } }
@keyframes ac-spin { 0% { transform:translate(-50%,-50%) rotate(0deg) } 100% { transform:translate(-50%,-50%) rotate(360deg) } }
@keyframes ac-rays { 0% { opacity:0 } 20% { opacity:1 } 84% { opacity:1 } 100% { opacity:0 } }

/* ── X01 overlay states (six-state + champagne handoffs) ──────────────────── */
/* A named state replaces the plain band for the visit that fired it. The tier
 * ladder, the two-beat print and the shared keyframes are the score-band
 * system above; what follows is only what these states add.
 *
 * Frame shake is the one thing that cannot be a plain shake keyframe: the
 * screen is centred with translate(-50%, -50%), so a keyframe that sets
 * `transform` would drop the centring and throw the frame across the stage.
 * Every quake carries the centring in its own transforms. */
.moment-state { position: absolute; inset: 0; z-index: 6; pointer-events: none; }
.sb-quaking { animation: var(--sb-quake) }
@keyframes sb-quake-1 {
  0%, 100% { transform: translate(-50%, -50%) }
  10% { transform: translate(calc(-50% - 4px), calc(-50% + 2px)) }
  22% { transform: translate(calc(-50% + 3px), calc(-50% - 2px)) }
  34% { transform: translate(calc(-50% - 2px), calc(-50% - 1px)) }
  46% { transform: translate(-50%, -50%) }
}
@keyframes sb-quake-4 {
  0%, 100% { transform: translate(-50%, -50%) }
  5% { transform: translate(calc(-50% - 17px), calc(-50% + 11px)) }
  11% { transform: translate(calc(-50% + 14px), calc(-50% - 10px)) }
  17% { transform: translate(calc(-50% - 12px), calc(-50% - 7px)) }
  23% { transform: translate(calc(-50% + 10px), calc(-50% + 7px)) }
  30% { transform: translate(calc(-50% - 7px), calc(-50% + 4px)) }
  38% { transform: translate(calc(-50% + 4px), calc(-50% - 3px)) }
  48% { transform: translate(-50%, -50%) }
}
@keyframes sb-quake-5 {
  0%, 100% { transform: translate(-50%, -50%) }
  4% { transform: translate(calc(-50% - 23px), calc(-50% + 15px)) }
  9% { transform: translate(calc(-50% + 20px), calc(-50% - 13px)) }
  14% { transform: translate(calc(-50% - 17px), calc(-50% - 10px)) }
  19% { transform: translate(calc(-50% + 14px), calc(-50% + 10px)) }
  25% { transform: translate(calc(-50% - 10px), calc(-50% + 6px)) }
  32% { transform: translate(calc(-50% + 7px), calc(-50% - 5px)) }
  40% { transform: translate(calc(-50% - 3px), calc(-50% + 2px)) }
  50% { transform: translate(-50%, -50%) }
}
/* Madhouse: low and uneasy, and it never quite settles. */
@keyframes sb-rumble {
  0% { transform: translate(-50%, -50%) }
  3% { transform: translate(calc(-50% - 6px), calc(-50% + 14px)) }
  7% { transform: translate(calc(-50% + 4px), calc(-50% - 9px)) }
  11% { transform: translate(calc(-50% - 3px), calc(-50% + 5px)) }
  15% { transform: translate(-50%, -50%) }
  22% { transform: translate(calc(-50% + 5px), calc(-50% + 11px)) }
  27% { transform: translate(calc(-50% - 4px), calc(-50% - 7px)) }
  32% { transform: translate(calc(-50% + 2px), calc(-50% + 4px)) }
  38% { transform: translate(-50%, -50%) }
  62% { transform: translate(calc(-50% - 2px), calc(-50% + 3px)) }
  78% { transform: translate(calc(-50% + 2px), calc(-50% - 2px)) }
  100% { transform: translate(-50%, -50%) }
}
/* Bust: the red flash is the state's own colour, not a screen-blended white. */
@keyframes sb-flash-red { 0% { opacity:0 } 4% { opacity:1 } 14% { opacity:.35 } 22% { opacity:.85 } 44% { opacity:0 } 100% { opacity:0 } }
@keyframes sb-chip { 0% { opacity:0; transform:translateY(26px) scale(.86) } 40% { opacity:1; transform:translateY(0) scale(1.06) } 55% { transform:scale(1) } 84% { opacity:1 } 100% { opacity:0 } }
/* Madhouse: the bats scatter out of the scene to where each one is going. */
@keyframes sb-bat {
  0% { opacity:0; transform:translate(-50%,-50%) translate(0,60px) scale(.2) rotate(0deg) }
  14% { opacity:1 }
  76% { opacity:.9 }
  100% { opacity:0; transform:translate(-50%,-50%) translate(var(--bx),var(--by)) scale(1) rotate(var(--br)) }
}
@keyframes sb-flap { 0%, 100% { transform:scaleY(1) } 50% { transform:scaleY(.42) } }
/* Low rider: speed lines past the frame edges, alternating direction. */
@keyframes sb-streak { 0% { opacity:0; transform:translateX(var(--sx)) scaleX(.2) } 18% { opacity:1 } 64% { opacity:.8 } 100% { opacity:0; transform:translateX(calc(var(--sx) * -1)) scaleX(1) } }
/* Shanghai: the jackpot. */
@keyframes sj-pop { 0% { opacity:0; transform:scale(var(--s0)) } 62% { opacity:1 } 78% { transform:scale(1.06) } 100% { opacity:1; transform:scale(1) } }
@keyframes sj-rise { 0% { opacity:0; transform:translateY(26px) scale(.86) } 66% { opacity:1 } 100% { opacity:1; transform:translateY(0) scale(1) } }
@keyframes sj-spin { 0% { transform:rotate(0deg) } 100% { transform:rotate(360deg) } }
@keyframes sj-breathe { 0% { transform:scale(.96) } 100% { transform:scale(1.06) } }
@keyframes sj-drift { 0% { opacity:0; transform:translateY(0) } 14% { opacity:1 } 76% { opacity:.85 } 100% { opacity:0; transform:translateY(-230px) } }
/* Champagne finish: the bottle builds, the cork goes, the frame takes it. */
@keyframes cf-dim { 0% { opacity:0 } 10% { opacity:1 } 88% { opacity:1 } 100% { opacity:0 } }
@keyframes cf-medal { 0% { opacity:0; transform:scale(1.32) } 8% { opacity:1; transform:scale(.97) } 11% { transform:scale(1.01) } 13% { transform:scale(1) } 88% { opacity:1; transform:scale(1) } 100% { opacity:0; transform:scale(1.03) } }
@keyframes cf-tremble {
  0%, 10% { transform:translate(0,0) rotate(0deg) }
  12% { transform:translate(1px,0) rotate(.4deg) } 14% { transform:translate(-1px,1px) rotate(-.5deg) }
  16% { transform:translate(1px,-1px) rotate(.7deg) } 18% { transform:translate(-2px,1px) rotate(-.9deg) }
  20% { transform:translate(2px,-1px) rotate(1.2deg) } 22% { transform:translate(-2px,2px) rotate(-1.5deg) }
  24% { transform:translate(3px,-2px) rotate(1.9deg) } 25.5% { transform:translate(-3px,2px) rotate(-2.3deg) }
  27% { transform:translate(4px,-3px) rotate(2.6deg) }
  27.8% { transform:translate(-22px,26px) rotate(-6deg) scale(1.05) }
  31% { transform:translate(8px,-8px) rotate(2deg) scale(.99) }
  35% { transform:translate(-3px,3px) rotate(-.7deg) scale(1) }
  40%, 100% { transform:translate(0,0) rotate(0deg) scale(1) }
}
@keyframes cf-cork { 0%,10% { transform:translate(0,0) rotate(0deg) } 14% { transform:translate(1px,-1px) rotate(-4deg) } 18% { transform:translate(2px,-2px) rotate(5deg) } 22% { transform:translate(4px,-4px) rotate(-7deg) } 25% { transform:translate(6px,-6px) rotate(9deg) } 27.6% { opacity:1; transform:translate(9px,-9px) rotate(-6deg) } 27.8%,100% { opacity:0; transform:translate(9px,-9px) } }
@keyframes cf-neck { 0%,10% { opacity:0; transform:translate(-50%,-50%) scale(.3) } 27.5% { opacity:.85; transform:translate(-50%,-50%) scale(1) } 28.6% { opacity:.9; transform:translate(-50%,-50%) scale(1.5) } 38%,100% { opacity:0; transform:translate(-50%,-50%) scale(1.8) } }
@keyframes cf-flash { 0% { opacity:0 } 8% { opacity:.32 } 100% { opacity:0 } }
@keyframes cf-ring { 0% { opacity:0; transform:translate(-50%,-50%) scale(.15) } 10% { opacity:1 } 70% { opacity:.4 } 100% { opacity:0; transform:translate(-50%,-50%) scale(1) } }
@keyframes cf-title { 0% { opacity:0; transform:scale(1.5) } 12% { opacity:1; transform:scale(.96) } 18% { transform:scale(1.02) } 24% { transform:scale(1) } 82% { opacity:1; transform:scale(1) } 100% { opacity:0; transform:scale(1.02) } }
@keyframes cf-cap { 0% { opacity:0; transform:translateY(24px) } 14% { opacity:1; transform:translateY(0) } 82% { opacity:1 } 100% { opacity:0 } }
@media (prefers-reduced-motion: reduce) {
  .sb-quaking { animation: none }
  .moment-state canvas { display: none }
}

/* ── Leg won: the broadcast lower third over the dimmed game screen ───────── */
/* The gameplay layer under the winner card fills that same frame. */
.legwon__behind { position: absolute; inset: 0; opacity: .5; filter: saturate(.85); }
.legwon { position: absolute; inset: 0; }
.legwon__impact { position: absolute; inset: 0; background: #fff; opacity: 0; animation: lw-impact .45s var(--dh-ease-out) .4s both; }
@keyframes lw-impact { 0% { opacity: 0 } 10% { opacity: .45 } 100% { opacity: 0 } }

.legwon__lower {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 170px;
  height: 300px;
  display: flex;
  align-items: stretch;
  animation: lw-slam .5s cubic-bezier(.2, .9, .25, 1) both;
}
/* The card flies in, overshoots, and the screen takes the knock: the wiggle
 * that follows is the board settling after a hit. */
@keyframes lw-slam {
  0% { transform: translateX(-115%) skewX(6deg) }
  62% { transform: translateX(2.5%) skewX(-1.5deg) }
  82% { transform: translateX(-1%) skewX(.5deg) }
  100% { transform: translateX(0) skewX(0) }
}
/* The screen is centred with translate(-50%, -50%), so the knock has to carry
 * that centring: a keyframe that sets a bare translate drops it and throws the
 * whole frame half a screen down and right for the length of the shake. */
.screen--legwon { animation: lw-shake .5s var(--dh-ease-out) .4s both; }
@keyframes lw-shake {
  0% { transform: translate(-50%, -50%) }
  14% { transform: translate(calc(-50% - 16px), calc(-50% + 7px)) }
  30% { transform: translate(calc(-50% + 13px), calc(-50% - 6px)) }
  46% { transform: translate(calc(-50% - 9px), calc(-50% + 4px)) }
  62% { transform: translate(calc(-50% + 6px), calc(-50% - 3px)) }
  80% { transform: translate(calc(-50% - 3px), calc(-50% + 1px)) }
  100% { transform: translate(-50%, -50%) }
}

.legwon__tag {
  width: 430px;
  flex: 0 0 auto;
  background: var(--dh-brand-blue);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 10px;
  padding: 0 60px;
  clip-path: polygon(0 0, 100% 0, calc(100% - 64px) 100%, 0 100%);
}
.legwon--match .legwon__tag { background: var(--cr-score-here); }
.legwon__eb { font-size: 26px; letter-spacing: .3em; color: #fff; opacity: .9; }
.legwon--match .legwon__eb { color: #00091f; opacity: 1; }
.legwon__leg { font-size: 58px; line-height: 1; color: #fff; }
.legwon--match .legwon__leg { color: #00091f; }

.legwon__who {
  flex: 1;
  min-width: 0;
  background: rgba(11, 13, 17, .78);
  backdrop-filter: blur(16px);
  display: flex;
  align-items: center;
  gap: 40px;
  padding: 0 64px 0 44px;
  transform-origin: left center;
  animation: lw-wiggle 1s var(--dh-ease-out) .46s both;
}
@keyframes lw-wiggle {
  0% { transform: rotate(0) }
  18% { transform: rotate(.55deg) }
  38% { transform: rotate(-.38deg) }
  58% { transform: rotate(.22deg) }
  78% { transform: rotate(-.1deg) }
  100% { transform: rotate(0) }
}
.legwon__avatar { width: 196px; height: 196px; border-radius: 9999px; overflow: hidden; flex: 0 0 auto; border: 6px solid var(--cr-score-here); box-shadow: 0 0 50px rgba(255, 201, 60, .4); }
.legwon__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.legwon__id { min-width: 0; }
.legwon__name { font-size: 86px; line-height: 1; color: #fff; white-space: nowrap; }
.legwon__how { font-size: 28px; letter-spacing: var(--dh-ls-micro); color: var(--dh-text-secondary); margin-top: 10px; white-space: nowrap; }
.legwon__how b { color: var(--cr-score-here); font-weight: 400; }
.legwon__prog { margin-left: auto; display: flex; flex-direction: column; gap: 14px; align-items: flex-end; }
.legwon__prog-row { display: flex; align-items: center; gap: 14px; }
.legwon__prog-row span { font-size: 20px; letter-spacing: .1em; color: var(--dh-text-secondary); }
.legwon__bars { display: flex; gap: 8px; }
.legwon__bar { display: block; width: 54px; border-radius: 4px; background: rgba(255, 255, 255, .11); }
.legwon__bar--set { height: 14px; }
.legwon__bar--leg { height: 10px; border-radius: 8px; }
.legwon__bar.is-won { background: #3ad24e; box-shadow: 0 0 14px rgba(58, 210, 78, .6); }
.legwon__bar--set.is-won { background: var(--cr-score-here); box-shadow: 0 0 16px rgba(255, 201, 60, .65); }
.legwon__bar.is-new { background: rgba(255, 255, 255, .3); animation: lw-light .5s cubic-bezier(.2, .9, .25, 1) 1.05s both; }
.legwon__bar--set.is-new { animation-name: lw-light-set; }
@keyframes lw-light { to { background: #3ad24e; box-shadow: 0 0 22px rgba(58, 210, 78, .85) } }
@keyframes lw-light-set { to { background: #ffc93c; box-shadow: 0 0 24px rgba(255, 201, 60, .9) } }

.legwon__countdown { position: absolute; left: 50%; bottom: 64px; transform: translateX(-50%); width: 520px; height: 6px; border-radius: 9999px; background: rgba(209, 213, 216, .1); overflow: hidden; }
.legwon__countdown i { display: block; height: 100%; border-radius: 9999px; background: var(--dh-brand-blue); transform-origin: left; animation: lw-count 3s linear both; }
@keyframes lw-count { from { transform: scaleX(0) } to { transform: scaleX(1) } }

/* ── Statistics pages ─────────────────────────────────────────────────────── */
.stats { position: absolute; inset: 0; padding: 56px 96px; display: flex; flex-direction: column; gap: 26px; }
.stats__title { display: flex; align-items: baseline; gap: 28px; }
.stats__title b { font-size: 52px; line-height: 1; color: #fff; font-weight: 400; }
.stats__sub { font-size: 24px; letter-spacing: var(--dh-ls-micro); color: var(--dh-bright-blue); }
.stats__page { margin-left: auto; font-size: 22px; letter-spacing: .1em; color: var(--dh-text-secondary); font-variant-numeric: tabular-nums; }
.stats__table { width: 100%; flex: 1; border-collapse: collapse; font-size: 40px; font-variant-numeric: tabular-nums; }
.stats__table th { padding: 0 0 14px; font-weight: 400; }
.stats__table td { padding: 10px 0; border-top: 1px solid var(--dh-stroke-08); color: #fff; }
.stats__table td.is-best { color: var(--cr-score-here); }
.stats__player { display: flex; align-items: center; gap: 18px; font-size: 32px; color: #fff; }
.stats__player--right { justify-content: flex-end; }
.stats__player img { width: 64px; height: 64px; border-radius: 9999px; object-fit: cover; }
.stats__mid { font-size: 22px; letter-spacing: var(--dh-ls-micro); color: var(--dh-text-secondary); text-align: center; }
.stats__key { width: 34%; text-align: center; font-size: 26px; letter-spacing: .1em; color: var(--dh-text-secondary); }
.stats__value { display: flex; align-items: center; gap: 24px; }
.stats__value--right { justify-content: flex-end; }
.stats__spark { width: 250px; height: 42px; display: block; }
.stats__spark path { fill: none; stroke-width: 4; stroke-linejoin: round; stroke-dasharray: 700; stroke-dashoffset: 700; animation: stats-draw 1.2s var(--dh-ease-out) .2s forwards; }
.stats__spark--a path { stroke: var(--dh-bright-blue); }
.stats__spark--b path { stroke: #d9a260; }
@keyframes stats-draw { to { stroke-dashoffset: 0 } }
.stats__dots { position: absolute; left: 50%; bottom: 56px; transform: translateX(-50%); display: flex; gap: 14px; }
.stats__dots i { width: 14px; height: 14px; border-radius: 9999px; background: rgba(209, 213, 216, .2); }
.stats__dots i.is-current { background: var(--dh-bright-blue); }

@media (prefers-reduced-motion: reduce) {
  .legwon__lower, .legwon__who, .screen--legwon, .legwon__impact, .legwon__bar.is-new, .stats__spark path { animation-duration: .01ms; }
}


/* ── Round the Clock ──────────────────────────────────────────────────────
 * The card is the X01 card with a different figure: not what is left, but
 * the number they are throwing at. It says so above the number, because a
 * bare 14 on a scoreboard reads as a score. */
.rtc-target::before {
  content: attr(data-label);
  position: absolute;
  left: 0;
  top: -24px;
  font-size: 20px;
  line-height: 1;
  letter-spacing: var(--dh-ls-micro);
  color: var(--dh-text-secondary);
  white-space: nowrap;
}
.player--right .rtc-target::before { left: auto; right: 0; }
.seats .rtc-target::before { top: -12px; font-size: 14px; }
/* Five and six a side: the card has no line to spare, and the lit number on
 * the board is already saying what the figure is. */
.seats--compact .rtc-target::before { display: none; }
/* Outside the seats the card is a column, not a grid: the bar takes the
 * width the trend card has in X01. */
.game--rtc .player > .leg-progress,
.game--elimination .player > .leg-progress { width: 400px; }
.game--rtc .seats .player > .leg-progress,
.game--elimination .seats .player > .leg-progress { width: 100%; }
.game--rtc .player--solo .x01__score--wide { font-size: 150px; }
/* 'Done' is five characters where X01's 'Last' is three: at the full-size card
 * the row ran into the board's numbers, so it steps down and closes up. */
.game--rtc .player--left:not([data-seat]) .player__stats,
.game--rtc .player--right:not([data-seat]) .player__stats { gap: 26px; }
.game--rtc .player--left:not([data-seat]) .player__stat-value,
.game--rtc .player--right:not([data-seat]) .player__stat-value { font-size: 44px; }

/* ── Elimination ──────────────────────────────────────────────────────────
 * A knock-back chip is a checkout chip that names who it lands on. */
.el-strike { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.el-strike small {
  font-size: .44em;
  line-height: 1;
  letter-spacing: var(--dh-ls-micro);
  color: rgba(255, 255, 255, .8);
  white-space: nowrap;
}
/* Within reach of whoever is throwing. Red, because this is the one card on
 * the screen about to lose something. */
.el-range {
  display: inline-flex;
  align-items: center;
  padding: 4px 11px;
  border-radius: 9999px;
  background: rgba(255, 77, 85, .16);
  box-shadow: inset 0 0 0 1px rgba(255, 77, 85, .7);
  font-size: 18px;
  line-height: 1;
  letter-spacing: var(--dh-ls-micro);
  color: #ff8a90;
  white-space: nowrap;
}
.seats--tall .el-range { font-size: 16px; padding: 3px 9px; }
.seats--compact .el-range { font-size: 14px; padding: 2px 8px; }
.screen--game .seats .player--threat.player--idle {
  background: rgba(255, 77, 85, .07);
  box-shadow: inset 0 0 0 2px rgba(255, 77, 85, .5), 0 0 30px rgba(255, 77, 85, .12);
}
.seats .player--threat.player--idle { opacity: .94; }
.player--threat.player--idle .player__score { color: #ff8a90; }
/* Three knock backs under a three-digit score: the chips step down a size. */
.game--elimination .seats--tall .el-strike { padding: 7px 12px; font-size: 23px; }
.game--elimination .seats--compact .el-strike { padding: 5px 10px; font-size: 20px; }

/* ── Bermuda ──────────────────────────────────────────────────────────────
 * Shanghai's card and Shanghai's strip, with one cell Shanghai never has: the
 * round that took nothing, which cost half of everything. It stands the full
 * height of the strip, hatched, so it cannot be read as a good round. */
.sh-cell--halved::before { height: 4px; background: rgba(255, 77, 85, .95); }
.sh-cell--halved::after {
  height: calc(100% - var(--sh-label));
  background: repeating-linear-gradient(135deg, rgba(255, 77, 85, .88) 0 6px, rgba(255, 77, 85, .42) 6px 12px);
}
.game--bermuda .sh-strip { gap: 5px; }
.game--bermuda .seats .sh-strip { gap: 4px; }
.bm-halved { color: #ff6b72 !important; }
.seats .player--idle .sh-cell--halved::after { filter: saturate(.7) brightness(.85); }

/* The halving. The total lands whole, the line goes through at 520ms, the
 * halves come apart and the new total is stamped where it stood. */
.halved-band { position: absolute; inset: 0; z-index: 6; pointer-events: none; }
@keyframes bm-cut-up {
  0% { opacity:0; transform:scale(1.6) }
  9% { opacity:1; transform:scale(.97) }
  14%, 17.3% { opacity:1; transform:translate(0,0) rotate(0deg) scale(1) }
  30% { opacity:1; transform:translate(-24px,-44px) rotate(-5deg) }
  40% { opacity:0; transform:translate(-70px,-150px) rotate(-10deg) }
  100% { opacity:0; transform:translate(-70px,-150px) rotate(-10deg) }
}
@keyframes bm-cut-down {
  0% { opacity:0; transform:scale(1.6) }
  9% { opacity:1; transform:scale(.97) }
  14%, 17.3% { opacity:1; transform:translate(0,0) rotate(0deg) scale(1) }
  30% { opacity:1; transform:translate(24px,44px) rotate(5deg) }
  40% { opacity:0; transform:translate(70px,170px) rotate(11deg) }
  100% { opacity:0; transform:translate(70px,170px) rotate(11deg) }
}
@keyframes bm-slash {
  0%, 15% { opacity:0; transform:translate(-50%,-50%) rotate(-7deg) scaleX(0) }
  19% { opacity:1; transform:translate(-50%,-50%) rotate(-7deg) scaleX(1) }
  30% { opacity:.9; transform:translate(-50%,-50%) rotate(-7deg) scaleX(1) }
  38%, 100% { opacity:0; transform:translate(-50%,-50%) rotate(-7deg) scaleX(1.05) }
}
@media (prefers-reduced-motion: reduce) {
  .halved-band [style*="bm-cut"], .halved-band [style*="bm-slash"] { animation: none }
}

/* ── Rows that hold their line ───────────────────────────────────────────────
 * Every card on a screen is read against the others, so what a card holds
 * must not move what sits under it. A three-digit score is set smaller than a
 * two-digit one but keeps the taller line; a route, a knock back and 'no
 * checkout' are different heights and share the tallest one's row; and the
 * Shanghai flag rides the card's edge rather than taking a row of its own. */
.x01-route { min-height: var(--route-row, 66px); }
.seats--tall { --route-row: 45px; }
.seats--compact { --route-row: 37px; }
.game--elimination { --route-row: 86px; }
.game--elimination .seats--tall { --route-row: 56px; }
.game--elimination .seats--compact { --route-row: 48px; }
.seats--tall .x01__score--wide { line-height: calc(150px * .92); }
.seats--compact .x01__score--wide { line-height: calc(118px * .92); }
/* Relative, so the flag has its own card to hang on; the 1v1 card's offsets
 * do not apply to a seat. */
.screen--game .seats .player { position: relative; inset: auto; }
.seats .sh-flag { position: absolute; top: 0; right: 26px; transform: translateY(-50%); z-index: 1; }

/* Three a side is the tightest card there is, and the row of tags under the
 * name took nineteen pixels of it. Cricket gives them back out of the towers,
 * Elimination out of the knock-back chips, so nothing runs past the card or
 * into the bar under it. */
/* Every game: the name, the tags and the ladders sit closer together. */
.seats--compact .player__who { gap: 5px; }
.game--shanghai .seats--compact .sh-ident { gap: 6px; }
.game--cricket .seats--compact .player { row-gap: 6px; }
.game--cricket .seats--compact .marks__cell { height: 15px; }
.game--cricket .seats--compact .marks__dead { height: 51px; }
.game--cricket .seats--compact .marks__row,
.game--cricket .seats--compact .player--right .marks__row { gap: 4px; }
.game--elimination .seats--compact .el-strike { padding: 3px 9px; font-size: 18px; gap: 3px; }
.game--elimination .seats--compact { --route-row: 40px; }

