/* TrigammonX v2.2.0 — 01-base-layout.css
 * Auto-extracted verbatim from TrigammonX-v2.1.9.html's single <style> block
 * as part of the v2.2.0 modularisation pass (base layout, reset, root variables, branded header, primary page/panel/button/layout foundations, dice animation keyframes).
 * See README-ARCHITECTURE.md for exact cascade/load order.
 */
* { box-sizing: border-box; margin: 0; padding: 0; }

/* ── v180c: Simulation DOM Freeze — page-stability CSS ──
   overflow-anchor:none stops the browser from "fixing" scroll position
   as content above the viewport changes height, which was contributing
   to the page jumping during a fast Auto Match run. The min-heights on
   panels below prevent the right panel / bottom area from expanding and
   collapsing every time their content is rendered.

   v4.1.1 (Pip Race recording stability): the unconditional
   `html { overflow-anchor: none; }` that used to sit here was removed.
   With it in place the browser never re-anchored the viewport when a
   live stat / clock block above it changed height, so during normal play
   the whole Game Trends / Pip Race area drifted up and down on screen
   (hard to screen-record). Native scroll anchoring is now left ON for
   interactive play; the simulation / headless batch paths still switch it
   off via the body-class rules below (an `overflow-anchor: none` ancestor
   of the anchor node suppresses anchoring for the document scroll box),
   so the v180c Auto Match behaviour is unchanged. */

body.simulation-batch-active {
  overflow-anchor: none;
}

body.simulation-batch-active * {
  scroll-behavior: auto !important;
}

body.simulation-batch-active .move-log {
  overflow-anchor: none;
}

body.simulation-batch-active .bottom-admin-shell,
body.simulation-batch-active .under-board-actions,
body.simulation-batch-active .under-board-controls,
body.simulation-batch-active .under-board-borne-off,
body.simulation-batch-active .right-panel {
  contain: layout paint;
}

body.simulation-batch-active #board-rotation-shell {
  transition: none !important;
}

body.simulation-batch-active .pip-fill {
  transition: none !important;
}

body.simulation-batch-active button {
  transition: none !important;
}

/* ── v180d: Headless Simulation Runner ──
   Fixed floating status box, plus stronger transition/animation freezes
   so a Simulation Auto Match run touches as little of the page as
   possible. The box itself is position:fixed and pointer-events:none,
   so it never affects page layout or height. */
.headless-sim-status {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 9999;
  width: 260px;
  padding: 10px 12px;
  background: rgba(4, 6, 14, 0.96);
  border: 1px solid #ffd700;
  border-radius: 10px;
  color: #ffd700;
  font-family: 'Courier New', monospace;
  font-size: 12px;
  line-height: 1.45;
  box-shadow: 0 0 18px rgba(0,0,0,0.65);
  pointer-events: none;
}

body.headless-simulation-active {
  overflow-anchor: none;
}

body.headless-simulation-active * {
  scroll-behavior: auto !important;
}

body.headless-simulation-active #board-rotation-shell,
body.headless-simulation-active .pip-fill,
body.headless-simulation-active button,
body.headless-simulation-active .checker {
  transition: none !important;
  animation: none !important;
}

/* ════════════════════════════════════════════════════════════════
   v185 — PREMIUM TABLETOP / DIGITAL HUD VISUAL SYSTEM
   ════════════════════════════════════════════════════════════════
   Design tokens. Every color below traces back to this palette —
   warm gold for primary accents/emphasis, cool soft blue for
   secondary glow and informational states, deep navy/black for the
   base. No new neon hues are introduced anywhere in this pass. */
:root {
  --tx-bg-deep:      #05070d;   /* page background floor */
  --tx-bg-panel:     #0c1120;   /* panel fill */
  --tx-bg-panel-2:   #0a0e1a;   /* secondary/recessed fill (admin, inputs) */
  --tx-line:         #232c47;   /* hairline borders */
  --tx-line-soft:    rgba(120,140,200,0.16);
  --tx-gold:         #d8b46a;   /* primary accent — warm, not bright yellow */
  --tx-gold-bright:  #f0cf8c;
  --tx-gold-dim:     rgba(216,180,106,0.35);
  --tx-blue-glow:    #6fa8d8;   /* secondary accent — soft cool blue */
  --tx-blue-dim:     rgba(111,168,216,0.25);
  /* v253: readability pass — raised across ALL tiers (not Pro-only). The
     old warm-off-white/mid-grey/dark-grey trio read as too low-contrast
     for ordinary player-facing labels against the dark tabletop
     background. White/near-white is now the default for anything the
     player is expected to read; --tx-text-faint is reserved for
     genuinely secondary/de-emphasised text only. */
  --tx-text:         #FFFFFF;
  --tx-text-dim:     #E0E4EA;
  --tx-text-faint:   #B8C0CC;
  --tx-radius:       10px;
  --tx-radius-sm:    7px;

  /* v186: dimensional/bevel system — raised-panel "physical" depth.
     Every beveled surface in this pass draws from these same four
     shadow recipes so the depth language stays consistent rather than
     each panel inventing its own shadow stack. */
  --tx-bevel-top:    rgba(255,255,255,0.07);   /* light catching the top edge */
  --tx-bevel-bottom: rgba(0,0,0,0.55);          /* shadow pooling at the base */
  --tx-raised:
    0 1px 0 rgba(255,255,255,0.06) inset,
    0 -2px 6px rgba(0,0,0,0.35) inset,
    0 6px 16px rgba(0,0,0,0.45),
    0 1px 0 rgba(0,0,0,0.8);
  --tx-raised-lg:
    0 1px 0 rgba(255,255,255,0.07) inset,
    0 -3px 10px rgba(0,0,0,0.4) inset,
    0 14px 30px rgba(0,0,0,0.5),
    0 2px 0 rgba(0,0,0,0.85);
}

body {
  margin: 0;
  color: var(--tx-text);
  font-family: 'Segoe UI', Arial, sans-serif;
  font-size: 13px;
  padding: 18px 16px 28px;
  min-height: 100vh;
  /* v186: added a corner vignette — the table surface reads as lit from
     above and receding into shadow at the edges, reinforcing the raised
     "tabletop" framing without altering the existing background image
     or its darkening overlay. */
  background:
    radial-gradient(ellipse 1200px 700px at 50% -4%, rgba(111,168,216,0.07), transparent 60%),
    radial-gradient(ellipse 1600px 1100px at 50% 38%, rgba(0,0,0,0), rgba(0,0,0,0.35) 100%),
    linear-gradient(rgba(2,3,8,0.86), rgba(2,3,8,0.92)),
    /* v2.2.0: was url('trigammon-background-tech.png') when this rule lived
       inline in TrigammonX-v2.1.9.html's own <style> block, resolved relative
       to the HTML file's own location. Now that this rule lives in an
       external stylesheet under trigammonx-v2.2.0/css/, a CSS url() resolves
       relative to THIS FILE's location instead (per the CSS spec), so the
       path is corrected to walk back up to the site root where the asset
       itself still lives, unrenamed and unmoved. */
    url('../../trigammon-background-tech.png') center center / cover no-repeat fixed;
  background-color: var(--tx-bg-deep);
}

/* v257: the separate v256 "Pro background presets" mechanism
   (body.tx-custom-background / --tx-custom-page-bg) is superseded by the
   unified Full UI Theme system below — see .tx-fulltheme-solid-bg and
   the body.tx-full-* theme blocks in the "v257 — UNIFIED FULL UI THEMES"
   section further down this stylesheet. */

h1 {
  color: var(--tx-gold);
  font-size: 16px;
  margin-bottom: 10px;
  letter-spacing: 1px;
  text-align: center;
}

/* ── Branded header (v185) ──
   The one deliberately "designed" moment on the page — a quiet gold
   serif wordmark with a hairline rule beneath it. Everything else on
   the page stays disciplined so this reads as the signature element. */
.tx-header {
  width: 100%;
  max-width: 1500px;
  margin: 0 auto 18px;
  text-align: center;
}
/* v3.5.2 Part A: approved gold wordmark image — replaces .tx-header-mark
   (below, kept completely intact as the onerror fallback text heading).
   Deliberately smaller/more refined than the old text treatment's visual
   footprint (per spec: "make it SMALLER... it should feel premium, not
   dominant"): height-driven with a modest clamp, width:auto preserves the
   asset's own aspect ratio exactly (no stretching), max-width:100% is a
   defensive guard against any horizontal overflow on very narrow
   viewports (the clamp already keeps it well inside that bound in
   practice), object-fit:contain is a further defensive fallback matching
   the same pattern already used by .goc-brand-logo/.goc-medallion-trophy
   elsewhere in this app. No breakpoint-specific override is needed — the
   vw-based clamp alone scales it down proportionally on tablet/smaller
   layouts exactly as .tx-header-mark's own font-size clamp already did. */
.tx-header-wordmark-img {
  /* v3.5.3 Part D: modest ~10% clamp bump (34/56 -> 38/62, min/max alike)
     per spec's "slightly refine it... modestly increasing its visual
     prominence" — still capped by the same vw-based clamp mechanism (so
     it still scales down proportionally and can never overflow/clip on
     narrow viewports) and still well short of the pre-v3.5.2 oversized
     text heading's own footprint. A polish-only size tune; no layout,
     markup, or fallback-behaviour change. */
  display: block; height: clamp(38px, 6vw, 62px); width: auto;
  max-width: 100%; object-fit: contain;
  margin: 0 auto;
  filter: drop-shadow(0 0 14px rgba(216,180,106,0.25));
}
.tx-header-mark {
  font-family: Georgia, 'Times New Roman', serif;
  font-size: clamp(28px, 4.4vw, 44px);
  font-weight: 700;
  letter-spacing: 6px;
  color: var(--tx-gold-bright);
  /* v186: layered text-shadow gives the wordmark a faint engraved/
     embossed quality — a bright top edge and a darker drop beneath,
     on top of the existing soft glow. */
  text-shadow:
    0 0 22px rgba(216,180,106,0.30),
    0 1px 0 rgba(255,238,210,0.25),
    0 2px 2px rgba(0,0,0,0.7);
  line-height: 1.1;
}
.tx-header-tagline {
  font-family: 'Courier New', monospace;
  /* v300: modestly more prominent per Task 9 — still clearly subordinate
     to .tx-header-mark (the TRIGAMMONX title) in size/weight/opacity.
     v3.5.2 Part A: nudged slightly further (font-size 13px->14px, full
     opacity) now that the header's own dominant element is the smaller,
     more refined wordmark IMAGE rather than the old oversized text — the
     spec's "make Backgammon for Three slightly more prominent... do not
     make the tagline compete with the wordmark" is still respected: it
     stays a small, uppercase, monospace, secondary-colour line, nowhere
     near the wordmark's own visual weight.
     v5.1.3: margin-top removed — now sits in .tx-header-meta-row, a
     shared flex row with .tx-beta-badge, so the row itself owns the
     top spacing instead of double-spacing both children. */
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 4px;
  text-transform: uppercase;
  color: var(--tx-blue-glow);
  margin-top: 0;
  opacity: 1;
}
/* v5.1.3: premium Beta-status line under the wordmark — "Public Beta •
   All Pro Features Unlocked". Restrained solid gold (not the brighter
   --tx-gold-bright used by the wordmark/pill), mixed case (never
   ALL CAPS per spec), deliberately smaller than both the wordmark and
   even the tagline so it reads as a quiet subtitle, not a second
   heading. Wraps naturally on very narrow viewports rather than
   clipping/overflowing. */
.tx-header-subtitle {
  margin-top: 6px;
  font-family: 'Courier New', monospace;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--tx-gold);
  opacity: 0.9;
  padding: 0 12px;
}
/* v5.1.3: houses .tx-beta-badge + .tx-header-tagline side by side on one
   row (previously two stacked lines) so introducing .tx-header-subtitle
   above nets to no extra header height overall. Wraps to two lines only
   if a viewport is too narrow for both on one. */
.tx-header-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 8px;
}
.tx-header-music-quick {
  width: fit-content;
  max-width: calc(100% - 24px);
  margin: 10px auto 0;
  padding: 6px 9px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
  border: 1px solid rgba(216,180,106,0.38);
  border-radius: 999px;
  background: rgba(8,12,22,0.82);
  box-shadow: 0 4px 16px rgba(0,0,0,0.20);
}
.tx-header-music-toggle {
  min-height: 30px;
  padding: 4px 11px;
  border: 1px solid var(--tx-gold-dim);
  border-radius: 999px;
  background: rgba(216,180,106,0.10);
  color: var(--tx-gold-bright);
  font: 700 11px/1 'Courier New', monospace;
  letter-spacing: 0.5px;
  cursor: pointer;
}
.tx-header-music-toggle:hover,
.tx-header-music-toggle:focus-visible {
  border-color: var(--tx-gold-bright);
  background: rgba(216,180,106,0.17);
  outline: none;
}
.tx-header-music-toggle.is-off {
  color: var(--tx-text-dim);
  border-color: var(--tx-line);
  background: rgba(255,255,255,0.035);
}
.tx-header-music-toggle.needs-start {
  border-color: var(--tx-gold-bright);
  box-shadow: 0 0 0 1px rgba(216,180,106,0.18), 0 0 12px rgba(216,180,106,0.14);
}
.tx-header-music-volume {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: min(210px, 66vw);
  color: var(--tx-text-dim);
  font: 700 9px/1 'Courier New', monospace;
  letter-spacing: 0.7px;
  text-transform: uppercase;
}
.tx-header-music-volume input[type="range"] {
  width: clamp(92px, 14vw, 150px);
  accent-color: var(--tx-gold-bright);
  cursor: pointer;
}
.tx-header-music-volume-value {
  min-width: 32px;
  text-align: right;
  color: var(--tx-text);
  font-size: 10px;
}
@media (max-width: 520px) {
  .tx-header-music-quick { gap: 6px; border-radius: 14px; }
  .tx-header-music-volume { min-width: 0; }
  .tx-header-music-volume-label { display: none; }
  .tx-header-music-volume input[type="range"] { width: min(120px, 34vw); }
}

.tx-header-rule {
  width: min(420px, 70%);
  height: 1px;
  margin: 14px auto 0;
  background: linear-gradient(90deg,
    transparent,
    var(--tx-gold-dim) 30%,
    var(--tx-gold-dim) 70%,
    transparent);
}
.tx-header-version {
  margin-top: 8px;
  font-family: 'Courier New', monospace;
  font-size: 10px;
  letter-spacing: 1px;
  color: var(--tx-text-faint);
}

/* ── Public Beta v1.0: small professional Beta badge beside the
   TRIGAMMONX wordmark — intentional/polished, not a developer/debug
   badge. Purely presentational. ── */
.tx-beta-badge {
  display: inline-block;
  margin-top: 0; /* v5.1.3: sits in .tx-header-meta-row now, row owns the top spacing */
  padding: 3px 11px;
  font-family: 'Courier New', monospace;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--tx-gold-bright);
  border: 1px solid var(--tx-gold-dim);
  border-radius: 999px;
  background: rgba(216,180,106,0.08);
}

/* ── v5.1.3: #beta-banner is now button-height only — the old two-line
   explanatory paragraph (.beta-banner-text/.beta-banner-pro-note) is
   gone (that message now lives, compressed, in .tx-header-subtitle),
   so this container no longer needs card styling (panel fill/border/
   shadow) to read as a coherent unit; it is just a centered wrapper
   around one compact button. Always in normal document flow, never a
   modal/overlay/popup. ── */
.beta-banner {
  display: flex;
  justify-content: center;
  margin: 0 auto 14px;
  padding: 0;
}
.beta-feedback-btn {
  flex: 0 0 auto;
  font-family: 'Courier New', monospace;
  font-size: 11px;
  font-weight: bold;
  letter-spacing: 0.5px;
  padding: 7px 14px;
  border: 1px solid var(--tx-gold-dim);
  border-radius: var(--tx-radius-sm);
  background: transparent;
  color: var(--tx-gold-bright);
  cursor: pointer;
  white-space: nowrap;
}
.beta-feedback-btn:hover {
  background: rgba(216,180,106,0.12);
}

/* ── Public Beta v1.0: small unobtrusive note beside the Commentary
   selector. Purely presentational, matches .commentary-pack-status'
   visual weight. ── */
.commentary-adult-note {
  font-size: 10px;
  color: var(--tx-text-faint);
  margin: 2px 0 8px;
  letter-spacing: 0.2px;
}

/* ── Public Beta v1.0: Move History (Pro) is intentionally not exposed
   to public Beta players yet (still under owner testing internally).
   Presentation-level hide only — the underlying moveHistoryTurns
   recording/save/Take-Back logic (see JS) is completely untouched and
   remains dormant; this rule only stops the card from rendering. ── */
#move-history-card {
  display: none;
}

/* ── Page layout: board column (title+clocks+board) | right panel (v176) ── */
.page-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 18px;
  align-items: start;
  width: 100%;
  max-width: 1500px;
  margin: 0 auto;
}

.board-column {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

.page-title {
  width: 100%;
  text-align: center;
  margin-bottom: 12px;
  font-family: 'Courier New', monospace;
  font-size: 10px;
  font-weight: normal;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--tx-text-faint);
}

/* v262: landscape tip banner — hidden by default and on every viewport
   except very narrow portrait phones (see media query below). Always in
   normal document flow (never position:fixed/absolute), so it can never
   block, cover, or disable board/controls — it just pushes content down
   a few pixels like any other card, and is fully dismissible. */
.landscape-tip-banner {
  display: none;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  box-sizing: border-box;
  padding: 7px 10px;
  margin-bottom: 10px;
  background: rgba(216,180,106,0.08);
  border: 1px solid rgba(216,180,106,0.35);
  border-radius: 6px;
  font-family: 'Courier New', monospace;
  font-size: 11px;
  color: var(--tx-text-faint);
}
.landscape-tip-banner.tip-active {
  display: none; /* overridden to flex only inside the narrow-portrait media query below */
}
.lt-dismiss {
  flex: 0 0 auto;
  background: transparent;
  border: none;
  color: var(--tx-text-faint);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 4px;
}
.lt-dismiss:hover {
  color: var(--tx-gold);
}
@media (max-width: 480px) and (orientation: portrait) {
  .landscape-tip-banner.tip-active {
    display: flex;
  }
}

.clock-panel-wrap {
  width: 100%;
  display: flex;
  justify-content: center;
  margin-bottom: 12px;
}

/* ── Shared panel styles (v185: control-console treatment;
   v186: beveled/raised depth) ──
   A faint gradient border (via background-clip layering) plus the
   shared --tx-raised shadow recipe gives every panel a slight
   "physical, lifted off the table" quality without going neon. */
.left-panel,
.right-panel {
  background:
    linear-gradient(180deg, rgba(216,180,106,0.06), transparent 40%),
    var(--tx-bg-panel);
  border: 1px solid var(--tx-line);
  border-top-color: rgba(255,255,255,0.08);
  border-radius: var(--tx-radius);
  padding: 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  position: sticky;
  top: 10px;
  box-shadow: var(--tx-raised-lg);
}

.right-panel {
  align-self: start;
  position: static;
}

.panel-title {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--tx-gold);
  border-bottom: 1px solid var(--tx-line);
  padding-bottom: 8px;
  margin-bottom: 4px;
  font-family: 'Segoe UI', sans-serif;
}

.panel-section-title {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--tx-text-dim);
  margin-top: 6px;
  margin-bottom: 3px;
  padding-top: 6px;
  border-top: 1px solid var(--tx-line-soft);
}
.panel-section-title:first-child {
  padding-top: 0;
  border-top: none;
  margin-top: 0;
}

/* ── v171: general dropdown readability ── */
select {
  background: var(--tx-bg-panel-2);
  color: var(--tx-text);
  border: 1px solid var(--tx-gold-dim);
  border-radius: 5px;
  padding: 5px 8px;
}
select option {
  background: var(--tx-bg-panel-2);
  color: var(--tx-text);
}
select:focus {
  outline: none;
  border-color: var(--tx-gold);
  box-shadow: 0 0 8px var(--tx-gold-dim);
}

/* ── Admin panel buttons — compact vertical stack ── */
.admin-panel button {
  width: 100%;
  text-align: left;
  padding: 6px 10px;
  font-size: 11px;
}

/* ── Player panel ── */
.player-panel .player-action-btns {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-top: 4px;
}
.player-panel .player-action-btns button {
  width: 100%;
  padding: 9px 14px;
  font-size: 13px;
  text-align: center;
}
/* Roll Dice + End Turn get extra emphasis */
.btn-roll-dice {
  border-color: var(--tx-gold) !important;
  color: var(--tx-gold-bright) !important;
  font-size: 14px !important;
}
.btn-roll-dice:hover { background: var(--tx-gold) !important; color: #14110a !important; }
.btn-end-turn {
  border-color: var(--tx-blue-glow) !important;
  color: var(--tx-blue-glow) !important;
}
.btn-end-turn:hover { background: var(--tx-blue-glow) !important; color: #0a121a !important; }
/* v242 Fix 3, restyled v2.2.7: unmistakable "ready" state, toggled only by
   setEndTurnReady() — legality/toggle logic itself is unchanged, this is a
   visual-only strengthening. Owner feedback: the prior blue-filled
   treatment didn't stand out enough next to Roll Dice. Now uses the same
   warm-gold family as Roll Dice/the certificate accents (theme-aware via
   --tx-gold/--tx-gold-bright/--tx-gold-dim, no new colour tokens), with
   dark high-contrast text, a stronger border, a visible glow, and slightly
   bolder text — so READY unmistakably reads as "the next action" without
   competing with Take Back (which keeps its existing subdued amber). The
   NOT-ready state (no `.end-turn-ready` class) is untouched and stays
   subdued. A tasteful pulse is opt-in only under
   prefers-reduced-motion: no-preference — the static glow/fill alone is
   already unmistakable for reduced-motion users. */
.btn-end-turn.end-turn-ready {
  background: linear-gradient(180deg, var(--tx-gold-bright) 0%, var(--tx-gold) 100%) !important;
  color: #1a1204 !important;
  border-color: var(--tx-gold-bright) !important;
  border-width: 2px !important;
  font-weight: 700 !important;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.35),
    0 0 14px 3px var(--tx-gold-dim),
    0 0 2px 1px var(--tx-gold-bright);
  transform: scale(1.03);
}
.btn-end-turn.end-turn-ready:hover {
  filter: brightness(1.06);
}
/* v2.3.9 fix: the ready state's own `transform: scale(1.03)` above has
   higher specificity (two classes) than the base `button:active`
   rule's `transform: translateY(2px)` (one type selector + one
   pseudo-class), so it silently WON every real press — Take Back/Roll
   Dice/Clear Move Marker all visibly compress into their "shelf" on
   click (button:active, 01-base-layout.css), but a ready End Turn
   button never did, staying frozen at scale(1.03) with zero press
   feedback. Confirmed directly via Chrome DevTools Protocol
   CSS.forcePseudoState (real :active pseudo-class, not a simulated
   mousedown, which Chromium does not reliably honor for :active
   matching): Roll Dice's active-state transform measured
   matrix(1,0,0,1,0,2) (the expected 2px press), End Turn's ready-state
   active-state transform measured matrix(1.03,0,0,1.03,0,0) — i.e.
   completely unchanged from its resting state. That is the concrete,
   measurable gap behind "must not appear... less actionable than the
   neighbouring primary controls when ready": every other Player Action
   button visibly "gives" under a real press and this one alone did not.
   Fixed by giving the ready state its own :active rule that layers the
   same press compression + flattened shadow language as the shared
   button:active rule, on top of (not instead of) the gold identity —
   comparable tactile affordance, same accent colour, no redesign. */
.btn-end-turn.end-turn-ready:active {
  transform: translateY(2px) scale(1.01);
  box-shadow:
    inset 0 1px 3px rgba(0,0,0,0.35),
    0 0 10px 2px var(--tx-gold-dim);
}
/* v2.4.0 fix: the REAL, always-reproducible cause of Owner feedback that
   a ready End Turn button "is still almost greyed out" — reproduced on
   every single turn tested (Playwright driving executeMove() through a
   full Triple Dice game; see the change report for the full trace).
   Root cause is a CSS specificity/staleness bug, not a perception issue
   and not something the v2.3.9 :active-only fix above touched:
     - 04-themes-responsive.css has `body.must-move .btn-end-turn
       { opacity: 0.5; }` (two-class specificity), intended to dim End
       Turn only while a legal move is still outstanding (must-move is
       computed fresh inside renderAll(), from the same
       getMaxDiceConstrainedMoves() call isCurrentTurnComplete() uses).
     - `.btn-end-turn.end-turn-ready` (this file) is ALSO two-class
       specificity. Equal specificity means later source order wins —
       04-themes-responsive.css loads after this file, so must-move's
       0.5 opacity silently overrides the ready state's gold treatment
       whenever both classes are present on the page at once.
     - executeMove() (05-gameplay-interaction.js) only calls
       renderPieces()/renderBarPieces() after applying a move — never
       the full renderAll() that recomputes must-move. So the instant a
       player's LAST die completes a turn, checkTurnCompleteAfterMove()
       calls setEndTurnReady(true) (added .end-turn-ready, enabled the
       button) while document.body still carries `must-move` from the
       render before that final move — and nothing re-runs renderAll()
       to clear it until some later, unrelated redraw. That stale class
       is what was actually dimming the button — confirmed via direct
       measurement: getComputedStyle(endTurnBtn).opacity read "0.5" at
       the exact instant end-turn-ready + enabled were both already
       true, on every turn tested, not just occasionally.
   Fix is presentation-only and additive: a single, more-specific rule
   (three classes) that restores full opacity for the READY state
   specifically, wherever must-move happens to still be set. It does
   NOT touch setEndTurnReady(), must-move's own toggle logic, or its
   real job (correctly dimming a genuinely NOT-ready End Turn while a
   move is still required — :not(:disabled) below deliberately excludes
   the one pre-existing non-regression case where `.end-turn-ready` can
   render alongside `disabled` before Triple Dice's first
   checkTurnCompleteAfterMove() call of a turn — see the v2.3.9 change
   report — so a genuinely disabled button never gets brightened). */
body.must-move .btn-end-turn.end-turn-ready:not(:disabled) {
  opacity: 1;
}
@media (prefers-reduced-motion: no-preference) {
  .btn-end-turn.end-turn-ready {
    animation: endTurnReadyPulse 1.8s ease-in-out infinite;
  }
  /* v2.3.9: a held press should read as a deliberate, physical action,
     not keep visibly pulsing underneath the player's finger/cursor. */
  .btn-end-turn.end-turn-ready:active {
    animation: none;
  }
}
@keyframes endTurnReadyPulse {
  0%, 100% {
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.35),
      0 0 14px 3px var(--tx-gold-dim),
      0 0 2px 1px var(--tx-gold-bright);
  }
  50% {
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.35),
      0 0 22px 6px var(--tx-gold-dim),
      0 0 4px 2px var(--tx-gold-bright);
  }
}

/* v3.7.2 (Task 13 — Touch Targets for Pause/Resume/End Turn): on a
   coarse-pointer (touch-primary) device only — desktop mouse users keep
   the existing compact sizing exactly as-is, per the explicit "do not
   make buttons visually enormous on desktop" requirement — every Player
   Actions / Game Flow / Tournament Clock control gets a real ~44-48px
   minimum touch target (current sizing measured ~30-34px for
   .under-board-actions button, ~26-28px for .txm-clock-btn-resume) plus
   comfortable spacing, so a finger tap has a fair-sized target without a
   visual redesign. `(pointer: coarse)` targets the input mechanism
   itself (works for iPad/Android regardless of screen width, unlike a
   max-width media query, and correctly leaves a touch-capable laptop
   with a precise trackpad/mouse untouched). No 300ms legacy tap-delay
   exists anywhere in this codebase to remove (verified: no
   FastClick/300ms-delay workaround present, and every clickable control
   already uses plain onclick/DOM click listeners, which modern mobile
   Safari/Chrome dispatch immediately once `<meta name="viewport">` sets
   width=device-width — already the case here). Guarding against
   accidental double-triggering is likewise already structural, not new
   CSS: every action here (`playGame()`, `endTurn()`,
   `resumeTournamentClockOrGame()`) is a plain, idempotent-by-state
   function — a second rapid tap either no-ops (e.g. continueGame()'s own
   `if (!gamePaused) return;`) or is naturally blocked because the first
   tap's own state change (e.g. `gamePaused`/`.disabled`) already made the
   control read as the opposite action or already disabled before a
   second event can land. */
@media (pointer: coarse) {
  .under-board-actions button,
  .under-board-controls button,
  .txm-clock-btn-resume {
    min-height: 46px;
    padding-top: 12px;
    padding-bottom: 12px;
  }
  .under-board-row {
    gap: 10px;
  }
  /* A real, unmistakable "accepted this tap" feedback moment for every
     button on touch — not just End Turn — satisfying "obvious
     pressed/accepted visual feedback" and "one accepted tap must change
     state once" without altering any control's own click handler. */
  .under-board-actions button:active,
  .under-board-controls button:active,
  .txm-clock-btn-resume:active {
    filter: brightness(1.15);
  }
  /* Task 14 (Prominent End Turn): a slightly stronger ready-state scale
     specifically on touch, where the button is necessarily smaller
     relative to a fingertip than to a mouse cursor. Layered on top of
     (never replacing) the existing gold/glow/pulse identity above. */
  .btn-end-turn.end-turn-ready {
    min-height: 50px;
    transform: scale(1.05);
    font-size: 15px !important;
  }
  .btn-end-turn.end-turn-ready:active {
    transform: translateY(2px) scale(1.02);
  }
}

/* v263: Clear Move Marker and Take Back promoted from ad-hoc inline
   border-color/color styles to proper classes, so all four Player Action
   buttons share identical underlying construction. Border width (1px),
   radius (var(--tx-radius-sm)) and the layered depth box-shadow were
   already shared via the base button/.under-board-actions button rules
   above — untouched here. What was actually inconsistent was width
   (these two lacked .wide-action, so they sat at 130px vs. Roll Dice/End
   Turn's 150px) and hover treatment (no filled-hover state). Both now get
   .wide-action in the markup (matching width/shape) and the same
   filled-hover pattern as Roll Dice/End Turn. Colours are unchanged —
   grey for Clear Move Marker, amber for Take Back — this only changes
   construction, not colour meaning. */
.btn-clear-marker {
  border-color: #636e72 !important;
  color: #636e72 !important;
}
.btn-clear-marker:hover { background: #636e72 !important; color: #0d0d1a !important; }
.btn-take-back {
  border-color: #fdcb6e !important;
  color: #fdcb6e !important;
}
.btn-take-back:hover { background: #fdcb6e !important; color: #0d0d1a !important; }

/* ── Board panel ── */
.board-panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  width: 100%;
  max-width: 980px;
  position: relative;
  /* v188 safe layering hotfix: isolation:isolate gives .board-panel its
     own stacking context so the z-index scheme below (plinth=0,
     children=1, board-stage=2) is self-contained and cannot be
     disrupted by z-index values elsewhere on the page. */
  isolation: isolate;
}

/* v186: a wider, flatter "plinth" shape sitting behind #board-stage —
   gives the stage a genuine sense of resting on a raised tabletop base
   rather than floating flat against the page. Purely decorative: sized
   via percentage/inset so it never participates in flex layout, has no
   pointer-events, and sits at a lower z-index than the board itself.
   Public Beta v1.0 alignment fix: left/width now derive from the exact
   same width formula #board-stage itself uses (min(72vw,840px) — see
   #board-stage below, untouched) instead of an independent 76vw/880px
   figure that only coincidentally matched #board-stage at some widths.
   #board-stage is centred in .board-panel via flex margin:auto, which
   (correctly, per spec) collapses to flush-left once its own width
   formula exceeds .board-panel's available width — this happens at
   certain intermediate desktop widths (~1024-1150px, e.g. ~1128px)
   because of the fixed 300px right-column sidebar. The old left:50%/
   translateX(-50%) centered the plinth on .board-panel's own midpoint
   unconditionally, which is correct only while #board-stage also fits;
   once #board-stage collapses flush-left, that midpoint no longer
   matches #board-stage's actual centre, which is what produced the
   visible left/right mismatch. The new left: expression below
   reproduces the identical "centre, or clamp flush-left once
   over-constrained" arithmetic directly (max(0px, ...)), so the plinth
   tracks #board-stage's true rendered centre at every width — including
   that intermediate range — while still extending 20px evenly beyond
   #board-stage on both sides whenever there is room to. #board-stage
   itself (width/margin/flex behaviour) is completely untouched. */
.board-panel::before {
  content: '';
  position: absolute;
  left: calc(max(0px, (100% - min(72vw, 840px)) / 2) - 20px);
  top: -10px;
  width: calc(min(72vw, 840px) + 40px);
  height: min(74vw, 862px);
  max-width: 880px;
  max-height: 862px;
  background: linear-gradient(180deg, #11162a 0%, #0a0e1c 60%, #060810 100%);
  border-radius: 18px;
  box-shadow:
    0 30px 60px rgba(0,0,0,0.6),
    0 4px 0 rgba(0,0,0,0.7),
    inset 0 2px 0 rgba(255,255,255,0.04);
  z-index: 0;
  pointer-events: none;
}

/* v188 safe layering hotfix: every direct child of .board-panel gets
   position:relative + z-index:1 so it always paints above the
   decorative plinth ::before (z-index:0), keeping buttons, fields,
   and panels clickable and visible regardless of plinth height. */
.board-panel > * {
  position: relative;
  z-index: 1;
}

/* ── Override .board-wrap to fill its container ── */
.board-wrap {
  position: relative;
  display: block;
  width: 100%;
  line-height: 0;
  overflow: visible;
  margin: 0;
}

/* ── Info bar ── */
.info-bar {
  width: 100%;
  padding: 8px 14px;
  background: var(--tx-bg-panel-2);
  border: 1px solid var(--tx-line);
  border-radius: var(--tx-radius-sm);
  color: var(--tx-text-dim);
  font-size: 12px;
  min-height: 32px;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.4);
}

/* ── Under-board control bar (v174) ──
   Houses the most-used action buttons directly below the board/info
   bar instead of far down the right panel. */
/* ── Under-board Borne Off (v175) ──
   Sits directly below the info bar so Blue/Green/Red trays are visible
   side-by-side without scrolling down into the right panel. */
.under-board-borne-off {
  width: 100%;
  background:
    linear-gradient(180deg, rgba(216,180,106,0.05), transparent 35%),
    var(--tx-bg-panel);
  border: 1px solid var(--tx-line);
  border-top-color: rgba(255,255,255,0.07);
  border-radius: var(--tx-radius);
  padding: 12px 14px;
  box-shadow: var(--tx-raised);
}

.under-board-section-title {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  color: var(--tx-gold);
  margin-bottom: 8px;
}

.under-board-borne-off .borne-off-display {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.under-board-borne-off .borne-player-box {
  margin-bottom: 0;
}

@media (max-width: 760px) {
  .under-board-borne-off .borne-off-display {
    grid-template-columns: 1fr;
  }
}

/* ── v254: Bar + Borne Off combined row ──
   Bar becomes a narrow left column (~20-25% width), Borne Off takes the
   remaining ~75-80% — same #bar-display / #borne-off-display markup and
   rendering as before, only the layout around them changed. Stacks
   vertically on narrow screens (same 760px breakpoint already used
   elsewhere under the board) so the Borne Off checker trays never get
   squeezed down to an unusable width. */
.under-board-bar-borneoff-row {
  width: 100%;
  display: flex;
  align-items: stretch;
  gap: 10px;
}

.under-board-bar-borneoff-row .under-board-bar-col {
  flex: 0 0 22%;
  width: 22%;
  min-width: 120px;
}

.under-board-bar-borneoff-row .under-board-bar-col .udp-col-bar {
  height: 100%;
  max-width: none;
}

.under-board-bar-borneoff-row .under-board-borneoff-col {
  flex: 1 1 78%;
  min-width: 0;
}

@media (max-width: 760px) {
  .under-board-bar-borneoff-row {
    flex-direction: column;
  }
  .under-board-bar-borneoff-row .under-board-bar-col {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
  }
}

.under-board-controls {
  width: 100%;
  background:
    linear-gradient(180deg, rgba(216,180,106,0.05), transparent 35%),
    var(--tx-bg-panel);
  border: 1px solid var(--tx-line);
  border-top-color: rgba(255,255,255,0.07);
  border-radius: var(--tx-radius);
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  box-shadow: var(--tx-raised);
}

/* v190b: primary action block — sits immediately below the dice/status
   panel so Roll Dice / End Turn are always close to the dice display.
   Shares all visual styling with .under-board-controls; kept as a
   separate class so the simulation-batch suppression, mobile overrides,
   and button sizing can be targeted independently if needed. */
.under-board-actions {
  width: 100%;
  background:
    linear-gradient(180deg, rgba(216,180,106,0.05), transparent 35%),
    var(--tx-bg-panel);
  border: 1px solid var(--tx-line);
  border-top-color: rgba(255,255,255,0.07);
  border-radius: var(--tx-radius);
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  box-shadow: var(--tx-raised);
}

/* v265: Table Talk / Commentary, relocated directly below Player Actions /
   Game Flow — same visual language (background/border/radius/shadow/
   padding) as .under-board-actions/.under-board-controls immediately
   above and below it, so it reads as one continuous "playing the game"
   control stack rather than a leftover Pro card. */
.under-board-commentary {
  width: 100%;
  background:
    linear-gradient(180deg, rgba(216,180,106,0.05), transparent 35%),
    var(--tx-bg-panel);
  border: 1px solid var(--tx-line);
  border-top-color: rgba(255,255,255,0.07);
  border-radius: var(--tx-radius);
  padding: 12px 14px;
  box-shadow: var(--tx-raised);
}
.under-board-commentary .panel-title { margin-top: 0; }

/* v3.6.0: Background Music — same visual language as
   .under-board-actions/.under-board-commentary immediately above, so the
   new card reads as one more step in the same "playing the game" control
   stack rather than a bolted-on panel. */
.under-board-music {
  width: 100%;
  background:
    linear-gradient(180deg, rgba(216,180,106,0.05), transparent 35%),
    var(--tx-bg-panel);
  border: 1px solid var(--tx-line);
  border-top-color: rgba(255,255,255,0.07);
  border-radius: var(--tx-radius);
  padding: 12px 14px;
  box-shadow: var(--tx-raised);
}
.under-board-music .panel-title { margin-top: 0; }

/* v2.1.6: Did You Know? / Beta Updates & Fixes — main-page row. Reuses
   .goc-lower-row/.goc-dyk-card/.goc-beta-card (already global, not
   scoped to #goc-card) verbatim for visual consistency with the same
   content reused inside the Winner/Results dashboard — this id rule
   only adds the vertical rhythm this stacked card layout needs
   (siblings here don't get it from a parent gap).
   v2.1.7: moved lower on the page (now after the public Game Trends
   charts, before Developer/Admin — see the HTML comment at its new
   location) so it reads as clearly secondary to live gameplay/Table
   Talk; this rule itself is unchanged. */
#tx-info-row { margin: 16px 0 0; }

/* v266: Move History (Pro) — same visual language as its under-board
   neighbors (.under-board-commentary etc.), collapsed by default via
   the .mh-collapsed class already present in the HTML on page load,
   exactly like .qs-collapsed for Quick Start. Player-facing only —
   completely separate card/CSS from the Technical Move Log. */
.move-history-card {
  width: 100%;
  background:
    linear-gradient(180deg, rgba(216,180,106,0.05), transparent 35%),
    var(--tx-bg-panel);
  border: 1px solid var(--tx-line);
  border-top-color: rgba(255,255,255,0.07);
  border-radius: var(--tx-radius);
  padding: 12px 14px;
  box-shadow: var(--tx-raised);
}
.mh-title {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--tx-gold-bright);
  font-weight: bold;
  margin-bottom: 8px;
  cursor: pointer;
  user-select: none;
}
.move-history-card.mh-collapsed .mh-title { margin-bottom: 0; }
.move-history-card.mh-collapsed .mh-panel { display: none; }

/* ── v3.7.2 Task 4: generic collapsible-section mechanism, applied to
   several existing secondary panels (Pro Themes, Play Options/Analysis,
   Game Insights, Game Trends, Table Talk/Voice, Background Music) so they
   don't dominate tablet play. Same idea as .qs-collapsed/.mh-collapsed/
   .hx-collapsed above — a class toggled by JS (toggleTxCollapsible(),
   06-stats-history.js) that hides everything in the card except its own
   clickable .panel-title header. Game Setup/Player Types/Dice Mode/Play
   Mode and all start/opening controls are deliberately NOT part of this
   mechanism — those must never be buried. ── */
.tx-collapsible > .panel-title {
  cursor: pointer;
  user-select: none;
}
.tx-collapsible.tx-collapsed > *:not(.panel-title) {
  display: none;
}

.mh-panel {
  max-height: 260px;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.mh-empty {
  font-size: 11px;
  color: var(--tx-text-dim);
  font-style: italic;
}
.mh-turn-group {
  border-left: 2px solid var(--tx-line);
  padding-left: 8px;
  min-width: 0;   /* allow long names to wrap/truncate instead of forcing horizontal overflow */
}
.mh-turn-blue  { border-left-color: #4aa3ff; }
.mh-turn-green { border-left-color: #55efc4; }
.mh-turn-red   { border-left-color: #ff6b6b; }
.mh-turn-header {
  font-size: 11px;
  font-weight: bold;
  color: var(--tx-text, #eee);
  word-break: break-word;
  overflow-wrap: anywhere;
}
.mh-dice-line {
  font-size: 10px;
  color: var(--tx-text-dim);
  font-family: 'Courier New', monospace;
  margin: 2px 0 3px;
}
.mh-move-line {
  font-size: 11px;
  color: var(--tx-text, #eee);
  font-family: 'Courier New', monospace;
  padding-left: 4px;
  word-break: break-word;
  overflow-wrap: anywhere;
}

@media (max-width: 480px) {
  .mh-panel { max-height: 200px; }
}

/* v3.3.0: Game History / Match History — same visual language as Move
   History (Pro) immediately above (.history-card mirrors .move-history-
   card's card chrome; .hx-* mirrors .mh-*), but this card is NOT hidden
   from public players (contrast with #move-history-card { display:none }
   above) — it is the v3.3.0 release's own headline feature. Collapsed by
   default via .hx-collapsed, exactly like .mh-collapsed for Move History
   (Pro) and .qs-collapsed for Quick Start. */
.history-card {
  width: 100%;
  background:
    linear-gradient(180deg, rgba(216,180,106,0.05), transparent 35%),
    var(--tx-bg-panel);
  border: 1px solid var(--tx-line);
  border-top-color: rgba(255,255,255,0.07);
  border-radius: var(--tx-radius);
  padding: 12px 14px;
  box-shadow: var(--tx-raised);
}
.hx-title {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--tx-gold-bright);
  font-weight: bold;
  margin-bottom: 8px;
  cursor: pointer;
  user-select: none;
}
.history-card.hx-collapsed .hx-title { margin-bottom: 0; }
.history-card.hx-collapsed .hx-controls,
.history-card.hx-collapsed .hx-panel { display: none; }

.hx-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.hx-game-select-label {
  font-size: 10px;
  color: var(--tx-text-dim);
  display: flex;
  align-items: center;
  gap: 5px;
}
.hx-game-select {
  font-family: 'Courier New', monospace;
  font-size: 10px;
  background: var(--tx-bg-panel-alt, rgba(0,0,0,0.25));
  color: var(--tx-text, #eee);
  border: 1px solid var(--tx-line);
  border-radius: 4px;
  padding: 2px 5px;
}
.hx-export-btn {
  font-family: 'Courier New', monospace;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  background: var(--tx-bg-panel-alt, rgba(0,0,0,0.25));
  color: var(--tx-gold-bright);
  border: 1px solid var(--tx-line);
  border-radius: 4px;
  padding: 3px 8px;
  cursor: pointer;
}
.hx-export-btn:hover { border-color: var(--tx-gold-bright); }

.hx-panel {
  max-height: 300px;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.hx-empty {
  font-size: 11px;
  color: var(--tx-text-dim);
  font-style: italic;
}
.hx-window-note {
  font-size: 10px;
  color: var(--tx-text-faint);
  font-style: italic;
  padding-bottom: 2px;
  border-bottom: 1px dashed var(--tx-line);
}

/* Turn blocks — same left-border player-colour convention as .mh-turn-*. */
.hx-turn-group {
  border-left: 2px solid var(--tx-line);
  padding-left: 8px;
  min-width: 0;
}
.hx-turn-blue  { border-left-color: #4aa3ff; }
.hx-turn-green { border-left-color: #55efc4; }
.hx-turn-red   { border-left-color: #ff6b6b; }
.hx-turn-header {
  font-size: 11px;
  font-weight: bold;
  color: var(--tx-text, #eee);
  word-break: break-word;
  overflow-wrap: anywhere;
}
.hx-turn-line {
  font-size: 10px;
  color: var(--tx-text-dim);
  font-family: 'Courier New', monospace;
  padding-left: 2px;
  word-break: break-word;
  overflow-wrap: anywhere;
}

/* Marker blocks — one scannable line each (Task 8: "make major markers
   visually easy to scan"). Colour is purely a scan aid, never a judgment
   of play quality — text itself stays strictly factual (see hxMarkerText()
   in JS; Task 8 explicitly forbids AI judgment language like "blunder"). */
.hx-marker {
  font-size: 11px;
  font-family: 'Courier New', monospace;
  font-weight: bold;
  padding: 4px 6px;
  border-radius: 4px;
  border-left: 3px solid var(--tx-line);
  word-break: break-word;
  overflow-wrap: anywhere;
}
.hx-marker-game-started   { border-left-color: var(--tx-gold-bright); color: var(--tx-gold-bright); }
.hx-marker-opening-roll   { border-left-color: var(--tx-line); color: var(--tx-text-dim); font-weight: normal; }
.hx-marker-game-completed,
.hx-marker-match-completed {
  border-left-color: #ffd76b;
  color: #ffd76b;
  background: rgba(255, 215, 107, 0.08);
}
.hx-marker-match-started        { border-left-color: var(--tx-gold-bright); color: var(--tx-gold-bright); }
.hx-marker-tournament-clock-paused,
.hx-marker-tournament-clock-resumed {
  border-left-color: #4aa3ff;
  color: #4aa3ff;
  font-weight: normal;
}
.hx-marker-tournament-clock-expired {
  border-left-color: #ff6b6b;
  color: #ff6b6b;
}
.hx-marker-game-saved-manual,
.hx-marker-game-resumed-from-save {
  border-left-color: var(--tx-line);
  color: var(--tx-text-faint);
  font-weight: normal;
  font-style: italic;
}

@media (max-width: 480px) {
  .hx-panel { max-height: 220px; }
}

.under-board-actions button {
  min-width: 130px;
  padding: 9px 14px;
  text-align: center;
  border-radius: var(--tx-radius-sm);
}

.under-board-actions .wide-action {
  min-width: 150px;
}

@media (max-width: 900px) {
  .under-board-actions button {
    width: 100%;
  }
}

.under-board-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--tx-line-soft);
}
.under-board-row:last-child {
  padding-bottom: 0;
  border-bottom: none;
}

.under-board-row-title {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--tx-gold);
  min-width: 82px;
  font-family: 'Segoe UI', sans-serif;
}

.under-board-controls button {
  min-width: 130px;
  padding: 9px 14px;
  text-align: center;
  border-radius: var(--tx-radius-sm);
}

.under-board-controls .wide-action {
  min-width: 150px;
}

.under-board-controls .small-status {
  font-size: 10px;
  color: var(--tx-text-dim);
  font-family: 'Courier New', monospace;
  line-height: 1.35;
  /* v4.1.1 (Pip Race recording stability): keep these status lines to a
     single, fixed-height row. The autosave-status text ("Autosaved
     H:MM:SS AM (move)" / "…(checker)" / "Autosave cleared" …) changes
     every autosave and, in the narrow control cell, was wrapping between
     one and three lines — changing the height of #under-board-controls,
     which sits above Game Trends, and shifting the Pip Race view down the
     page mid-recording. One line + ellipsis fixes the height regardless
     of the message or the column width; the text is a non-critical hint. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-height: 1.35em;
}

.under-board-inline-label {
  font-size: 11px;
  color: var(--tx-text-dim);
  display: flex;
  gap: 6px;
  align-items: center;
}

@media (max-width: 900px) {
  .under-board-row {
    flex-direction: column;
    align-items: stretch;
  }

  .under-board-row-title {
    min-width: 0;
  }

  .under-board-controls button {
    width: 100%;
  }
}

/* ── Bottom admin shell (v175) — collapsible, moved off the left column ── */
/* ── Bottom admin shell (v185: deliberately secondary) ──
   No gold accent, no glow, flatter background, smaller type. This area
   should read as "developer/testing tools," not part of the polished
   tabletop presentation above it. */
.bottom-admin-shell {
  width: 100%;
  max-width: 1400px;
  margin: 18px auto 0 auto;
  opacity: 0.94;
}

.admin-toggle-btn {
  width: 100%;
  padding: 8px 14px;
  border: 1px solid var(--tx-line);
  border-radius: var(--tx-radius-sm);
  background: transparent;
  color: var(--tx-text-faint);
  text-align: center;
  font-size: 10px;
  letter-spacing: 1px;
  text-transform: uppercase;
  font-weight: normal;
  /* v186: explicitly stays flat — the chunky/raised button treatment
     below is for the main control console; Admin should keep reading
     as quiet and secondary rather than picking up the same depth. */
  box-shadow: none;
}
.admin-toggle-btn:hover {
  background: rgba(255,255,255,0.03);
  color: var(--tx-text-dim);
  box-shadow: none;
}
.admin-toggle-btn:active {
  transform: none;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.4);
}

.bottom-admin-panel {
  margin-top: 8px;
  background: var(--tx-bg-panel-2);
  border: 1px dashed var(--tx-line);
  border-radius: var(--tx-radius);
  padding: 14px 12px 12px;
}

.bottom-admin-panel.collapsed {
  display: none;
}

.bottom-admin-panel .panel-title {
  color: var(--tx-text-faint);
  letter-spacing: 1.5px;
}

.bottom-admin-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
  align-items: start;
}

.admin-group {
  background: rgba(255,255,255,0.015);
  border: 1px solid var(--tx-line-soft);
  border-radius: var(--tx-radius-sm);
  padding: 9px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* v184: lets the Match / Balance Log group span the full grid width
   instead of being squeezed into a single narrow auto-fit column —
   the detailed per-game rows read much better with more room. */
.admin-group-wide {
  grid-column: 1 / -1;
}

.admin-group button {
  width: 100%;
  text-align: left;
  padding: 6px 10px;
  font-size: 11px;
}

@media (max-width: 650px) {
  .bottom-admin-grid {
    grid-template-columns: 1fr;
  }
}

/* ── Move log ── */
.move-log {
  width: 100%;
  background: #070710;
  border: 1px solid var(--tx-line);
  border-radius: 6px;
  padding: 8px 12px;
  max-height: 130px;
  overflow-y: auto;
  font-size: 11px;
  font-family: 'Courier New', monospace;
  color: #636e72;
  box-shadow: inset 0 2px 6px rgba(0,0,0,0.5);
}

/* ── Dice area inside player panel ── */
.dice-panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0;
  margin: 0;
}
.dice-col { display: flex; flex-direction: column; gap: 4px; }
.dice-row { display: flex; gap: 5px; flex-wrap: wrap; }

/* ── Side-by-side Rolled / Available Moves (v177) ── */
.dice-status-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  align-items: start;
}
.dice-status-row .dice-col {
  min-width: 0;
}
.dice-col-rolled,
.dice-col-available {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--tx-line);
  border-radius: 6px;
  padding: 6px;
}
.dice-col-available .roll-type {
  margin-top: 4px;
  line-height: 1.35;
}
@media (max-width: 980px) {
  .dice-status-row {
    grid-template-columns: 1fr;
  }
}

/* ── Under-board Dice / Status panel (v186b) ──
   Horizontal strip directly under the board: Current Turn on the left,
   Rolled + Available Moves in the middle, Bar + Pips on the right.
   Reuses .dice-label / .dice-row / .roll-type / .pip-display /
   .turn-badge / .dice-col-rolled / .dice-col-available styling as-is —
   only the outer flex arrangement is new, so the moved panel keeps the
   same look it always had, just laid out horizontally instead of in a
   narrow vertical column. */
.under-board-dice-panel {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;   /* was stretch — prevented vertical bloat */
  gap: 10px;
  background:
    linear-gradient(180deg, rgba(216,180,106,0.05), transparent 35%),
    var(--tx-bg-panel);
  border: 1px solid var(--tx-line);
  border-top-color: rgba(255,255,255,0.07);
  border-radius: var(--tx-radius);
  padding: 10px 12px;
  box-shadow: var(--tx-raised);
}

.udp-col {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.udp-col-turn {
  flex: 0 0 auto;
  justify-content: center;
}

.udp-col-rolled,
.udp-col-available {
  flex: 1 1 140px;
}

.udp-col-bar,
.udp-col-pips {
  flex: 0 1 auto;
}

.udp-col-pips .pip-display {
  min-width: 150px;
}

/* Thin separators between the three logical groups (Turn | Dice | Bar+Pips)
   on wide screens — removed automatically once columns wrap to stacked
   rows on narrower screens, where a vertical rule would look stray. */
@media (min-width: 761px) {
  .udp-col-rolled {
    border-left: 1px solid var(--tx-line-soft);
    padding-left: 10px;
  }
  .udp-col-bar {
    border-left: 1px solid var(--tx-line-soft);
    padding-left: 10px;
  }
}

@media (max-width: 760px) {
  .under-board-dice-panel {
    flex-direction: column;
  }
  .udp-col-turn {
    align-items: flex-start;
  }
}

/* v190c layout polish — keep Rolled and Available Moves beside each other,
   and move Bar/Pips below the main action buttons. */
/* v190d hotfix: was flex: 1 1 420px — the large flex-basis forced this
   wrapper to claim 420px of space, and align-items:stretch on the parent
   then stretched it vertically to match the tallest sibling (turn badge),
   creating empty air below the rolled dice. flex: 1 1 auto with min-width:0
   lets it grow to fill available space without any artificial height floor. */
.udp-col-dice-combo {
  flex: 1 1 auto;
  min-width: 0;
}

.udp-dice-grid {
  display: grid;
  /* v263: was minmax(0,1fr) minmax(0,1fr) — equal columns left dead space
     to the right of Rolled (which only ever needs room for 2 fixed-size
     dice). max-content sizes the Rolled column to its actual content;
     Available Moves takes the rest via minmax(0,1fr). Dice size/animation,
     move chips, and dice values are untouched — this is a column-width
     change only. The narrow-screen stacking override just below
     (@media max-width:760px, grid-template-columns:1fr) is unchanged, so
     genuinely narrow screens still stack exactly as before. */
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 10px;
  align-items: start;   /* was stretch — caused empty height within each cell */
}

.udp-dice-grid .udp-col-rolled,
.udp-dice-grid .udp-col-available {
  border-left: none;
  padding-left: 0;
  min-width: 0;
}

.under-board-stats-strip {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  gap: 10px;
  align-items: start;   /* was stretch */
}

/* v245: Pips moved to the right sidebar — Bar is now this strip's only
   column. Single-column so Bar doesn't leave an empty second column /
   wasted space where Pips used to sit. */
.under-board-stats-strip-bar-only {
  grid-template-columns: minmax(0, 1fr);
}
.under-board-stats-strip-bar-only .udp-col-bar {
  max-width: 320px;
}

.under-board-stats-strip .udp-col {
  background: var(--tx-bg-panel-2);
  border: 1px solid var(--tx-line-soft);
  border-radius: 12px;
  padding: 10px 12px;
}

.under-board-stats-strip .udp-col-pips .pip-display {
  min-width: 0;
}

@media (max-width: 760px) {
  .udp-col-dice-combo {
    min-width: 0;
  }
  /* v264: .udp-dice-grid (Rolled/Available Moves) removed from this
     forced-stack rule — see the dedicated, much narrower fallback below.
     .under-board-stats-strip (Bar/Pips) is untouched and still stacks
     here exactly as before; it was not part of this request. */
  .under-board-stats-strip {
    grid-template-columns: 1fr;
  }
}

/* v264: Rolled/Available Moves side-by-side on mobile portrait.
   Previously .udp-dice-grid stacked to a single column at the same
   760px breakpoint as everything else in this panel, even though Rolled
   only ever needs room for its (fixed-size, unchanged) dice tray and
   Available Moves' chips already wrap on their own
   (.dice-row{flex-wrap:wrap}, unchanged) — so there was no real reason
   for these two specifically to stack that early, and doing so left a
   lot of unused horizontal width on real phones. The v263 content-based
   column sizing (max-content minmax(0,1fr), 10px gap — both unchanged)
   now applies all the way down to 340px. Only below that — a width
   genuinely too narrow for the dice tray + at least one legible
   Available Moves chip to sit side by side without crowding/overlap —
   does this fall back to the single stacked column, matching the exact
   grid-template-columns:1fr treatment .udp-dice-grid already used above.
   Dice size, dice animation, dice values, move-chip markup/logic, and
   board/gameplay logic are all completely untouched — this is the same
   grid, same two child elements, just a narrower point at which it
   switches from 2 columns to 1. */
@media (max-width: 340px) {
  .udp-dice-grid {
    grid-template-columns: 1fr;
  }
}

/* ── Move list panel ── */
.move-list-panel {
  width: 100%;
  padding: 8px 14px;
  background: var(--tx-bg-panel-2);
  border: 1px solid #55efc4;
  border-radius: 6px;
  font-size: 12px;
  font-family: 'Courier New', monospace;
  display: none;
  line-height: 1.8;
}

/* ── Manual setup panel ── */
#manual-setup-panel {
  display: none;
  width: 100%;
  padding: 10px 14px;
  background: var(--tx-bg-panel-2);
  border: 2px solid #fdcb6e;
  border-radius: 8px;
  font-size: 12px;
  font-family: 'Courier New', monospace;
}

/* ── Export / hint panels — full width under board ── */
.anchor-export-panel,
.czl-export-panel {
  width: 100%;
  max-width: none;
}
#anchor-edit-hint,
#czl-edit-hint {
  width: 100%;
  max-width: none;
}

/* ── Responsive: stack on narrow screens ── */
@media (max-width: 980px) {
  .page-layout {
    grid-template-columns: 1fr;
  }

  .board-column,
  .board-panel,
  .clock-panel-wrap,
  .right-panel {
    width: 100%;
  }

  /* v2.2.1: Responsive Game-Setup Parity Fix — at this collapsed
     single-column breakpoint, visually reorder the two existing
     .page-layout grid children so Game Setup (.right-panel: Player
     Types, Player Names, Dice Mode, Reset Game, etc.) appears ABOVE
     the board/gameplay stack (.board-column). This uses CSS grid
     `order` only — no DOM changes, no duplicated markup/IDs, no new
     elements. Desktop/wide layout (>980px) is completely unaffected
     because `order` has no effect outside this media query. */
  .right-panel {
    order: 1;
  }

  .board-column {
    order: 2;
  }

  .clock-panel {
    grid-template-columns: 1fr;
    width: min(100%, 360px);
  }

  .player-panel .player-action-btns { flex-direction: row; flex-wrap: wrap; }
  .player-panel .player-action-btns button { width: auto; flex: 1 1 auto; }
}

/* ── v3.7.8 Goal 1 — tablet: board near the top.
   Real iPad finding: with the whole Game Setup card (.right-panel) placed
   ABOVE the board on this band (the v3.7.2 Task 3 ordering), the board
   began roughly a full page down and every game needed a scroll before
   play. v3.7.8 reverses the order on the touch/tablet band so the stack
   reads, top to bottom:
     header/branding  ->  BOARD (.board-column: board, then the compact
       Bar/Captured + Borne-Off strips, the opening/turn status panel and
       the Player Actions / Game Flow row that already live directly under
       the board in .board-panel)  ->  full Game Setup / Play Options
       (.right-panel)  ->  the collapsible secondary sections further down.
   The Game Setup card keeps every control (Play Mode buttons, Dice Mode,
   player types/names, single-Human colour choice, Tournament match length)
   — only its position moves, to just below the live play area — and
   playGame() still smooth-scrolls the board into view on Start
   (scrollBoardIntoViewForPlayStart(), 05-gameplay-interaction.js).
   Scoped to (pointer: coarse) only, so desktop/laptop windows — including
   a narrow non-touch window that hits the same width — are completely
   unaffected (the v2.2.1 Parity Fix ordering in the plain max-width:980px
   block above still governs those). Board geometry, checker coordinates,
   Bar/Borne-Off logic and the v3.7.7 scroll-pause thresholds are untouched
   — this is order + spacing only. ── */
@media (min-width: 700px) and (max-width: 1080px) and (pointer: coarse) {
  .board-column { order: 1; }
  .right-panel  { order: 2; }

  .page-layout { gap: 10px; }
  .board-column { padding-top: 0; }
  .landscape-tip-banner { margin-bottom: 4px; }

  /* The Game Setup card now sits below the board — keep it visually
     lighter and lead with the essential Play Mode / Dice Mode / player
     controls (already first in its markup). */
  .right-panel { margin-top: 4px; }
  .right-panel .panel-title { font-size: 13px; }
}

/* ── v5.1.4 Goal 1 — PHONE: board first.
   Owner real-device finding (Pixel 10 Pro): gameplay felt good but the
   board sat ~1500px down the page, below the entire Game Setup stack,
   because the plain `@media (max-width:980px)` block above deliberately
   places Game Setup (.right-panel) ABOVE the board (.board-column) via
   grid `order` — the "v2.2.1 Responsive Game-Setup Parity Fix". The
   v3.7.8 band above already reverses that for tablets (700–1080px,
   pointer:coarse). This new band extends the same, proven, board-first
   ordering DOWN to phone widths — everything below 700px on a touch
   device. It is CSS `order` on the two existing .page-layout grid
   children only: NO DOM change, NO duplicated markup/IDs, NO new
   elements, NO board-geometry / checker-anchor / canvas change. It must
   sit AFTER the max-width:980px block so its `order` values win on the
   cascade at ≤699px + coarse.

   Result stack, top → bottom, on a phone:
     compact TrigammonX header / Public Beta status
     -> BOARD (#board-stage)
     -> live play strip already inside .board-panel: Current Turn + dice,
        Player Actions (Roll / End Turn / Take Back), Game Flow
        (Start Game / Rotate / Sound), info bar
     -> Game Trends (Pip Race / Dice Fortune)  [#pro-card-trends]
     -> Table Talk / Music  [under-board-commentary / under-board-music]
     -> Save / Resume + Winner Archive  [under-board-controls]
     -> full Game Setup / Play Options  (.right-panel, now order:2)
     -> TrigammonX Pro cards + Advanced / Data Management (#tx-section-pro,
        #tx-section-admin — already last in source order, unaffected).

   Pre-game is NOT a dead end: the authoritative ▶ Start Game button
   (#btn-begin-play -> playGame()) and every live control already live
   directly under the board in .board-panel, and playGame() still
   scrollBoardIntoViewForPlayStart()s on start. The full Game Setup card
   (to change seats / dice / mode away from the sensible defaults) is a
   short scroll below the live area, with every control preserved — no
   mobile-only Game Setup collapse was added (not needed after
   board-first ordering; see the v5.1.4 change report §6).

   iPad is unaffected: portrait (~768–834px) and landscape (~1024–1194px)
   both fall in the v3.7.8 tablet band or the desktop layout, never this
   ≤699px band. Desktop and narrow non-touch windows are unaffected
   (pointer:coarse gate, matching the v3.7.8 precedent). ── */
@media (max-width: 699px) and (pointer: coarse) {
  .board-column { order: 1; }
  .right-panel  { order: 2; }

  .page-layout { gap: 10px; }
  .board-column { padding-top: 0; }
  .landscape-tip-banner { margin-bottom: 4px; }

  /* Game Setup now sits below the live play area — trim its top gap and
     title so it reads as a secondary section, not a competing header.
     Every control inside it is untouched. */
  .right-panel { margin-top: 6px; }
  .right-panel .panel-title { font-size: 13px; }

  /* v5.1.4 pixel polish — pre-existing phone overflow fix (present since
     the compact clocks landed, unrelated to the ordering change above):
     .clock-panel-compact's `repeat(auto-fit, minmax(120px, 1fr))` gave
     the Game-Insights clock cards a ~169px grid cell while each card's
     intrinsic width is ~220px, so the second card poked ~18px past a
     ~412px viewport. Stacking them one-per-row on phones removes the
     overflow with zero change to #clock-game/#clock-turn ids, the
     .clock-card/.clock-face markup, updateClocks() timing, or Tournament
     clock mathematics — grid track count only. The `.pro-card` prefix is
     only to out-specify the base rule in css/04-themes-responsive.css
     (loaded after this file). */
  .pro-card .clock-panel-compact { grid-template-columns: 1fr; }

  /* v5.1.4 pixel polish — second pre-existing phone overflow, also
     unrelated to the ordering change: `.under-board-controls .small-status`
     is `white-space: nowrap`, so once there is an autosaved game the
     "Saved game available from <ISO timestamp>. Click Load Saved Game."
     line (~444px) refuses to wrap and pushes the page ~63px wider than a
     ~412px phone. Allow these short status lines (#save-status /
     #undo-status / #allow-reset-last-move-status) to wrap on phones only.
     Text, ids and logMsg()/save-flow logic are untouched; desktop / iPad
     keep the single-line nowrap look. */
  .under-board-controls .small-status { white-space: normal; overflow-wrap: anywhere; }
}
/* v186: chunky physical button treatment — a solid bottom-edge "shelf"
   gives each button real thickness, and :active compresses it down
   into that shelf to simulate an actual press. transform/translateY
   only ever moves by 1-2px and never affects layout flow. */
button {
  padding: 7px 14px;
  border-radius: var(--tx-radius-sm);
  border: 1px solid var(--tx-blue-glow);
  background: linear-gradient(180deg, rgba(255,255,255,0.05), transparent 50%), var(--tx-bg-panel-2);
  color: var(--tx-blue-glow);
  cursor: pointer;
  font-size: 12px;
  font-weight: bold;
  font-family: inherit;
  transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.15s, color 0.15s;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.08),
    0 2px 0 rgba(0,0,0,0.7),
    0 4px 6px rgba(0,0,0,0.35);
}
button:hover {
  background: var(--tx-blue-glow);
  color: #0a121a;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.25),
    0 2px 0 rgba(0,0,0,0.7),
    0 4px 10px var(--tx-blue-dim),
    0 4px 6px rgba(0,0,0,0.35);
}
button:active {
  transform: translateY(2px);
  box-shadow:
    inset 0 1px 3px rgba(0,0,0,0.5),
    0 0 0 rgba(0,0,0,0.7);
}
button.active { background: var(--tx-blue-glow); color: #0a121a; }
button.btn-blue  { border-color: #74b9ff; color: #74b9ff; }
button.btn-blue:hover  { background: #74b9ff; color: #0a121a; }
button.btn-green { border-color: #55efc4; color: #55efc4; }
button.btn-green:hover { background: #55efc4; color: #0a121a; }
button.btn-red   { border-color: #ff7675; color: #ff7675; }
button.btn-red:hover   { background: #ff7675; color: #0a121a; }
button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.04),
    0 1px 0 rgba(0,0,0,0.5);
}
button:disabled:active { transform: none; }

/* ── Board container ── */
#board-img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 6px;
}
.die-chip {
  width: 34px; height: 34px;
  border-radius: 8px;
  border: 2px solid var(--tx-gold);
  background: linear-gradient(155deg, #232f52 0%, #16213e 55%, #0e1730 100%);
  color: var(--tx-gold-bright);
  font-size: 17px;
  font-weight: bold;
  display: flex; align-items: center; justify-content: center;
  position: relative;
  font-family: 'Courier New', monospace;
  /* v186: a raised, lacquered-tile look — top-edge highlight, base
     shadow pooling, and a soft ambient lift off the panel behind it. */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.18),
    inset 0 -3px 5px rgba(0,0,0,0.45),
    0 2px 0 rgba(0,0,0,0.6),
    0 4px 8px rgba(0,0,0,0.4);
}
.die-chip.used {
  background: linear-gradient(155deg, #1a1a1a 0%, #111 60%, #0a0a0a 100%);
  color: #444; border-color: #333;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.04),
    inset 0 -3px 5px rgba(0,0,0,0.5),
    0 1px 0 rgba(0,0,0,0.5);
}
/* v258: readability pass — Available Moves chips were 26px/12px, too
   small to read at a glance. Same element/class structure, same
   diceExpanded/diceUsed-driven renderDicePanel() innerHTML — presentation
   only (size, weight, contrast). */
.die-chip.expanded {
  width: 34px; height: 34px;
  border-color: #fd79a8;
  border-width: 2px;
  color: #ff9ec2;
  background: linear-gradient(155deg, #33001d 0%, #1e0012 60%, #13000b 100%);
  font-size: 17px;
  font-weight: bold;
  border-radius: 7px;
  text-shadow: 0 1px 2px rgba(0,0,0,0.7);
}
.die-chip.expanded.used {
  background: linear-gradient(155deg, #1a1a1a 0%, #111 60%, #0a0a0a 100%);
  color: #444; border-color: #333;
}
/* v2.1.2 Bug A fix: "dead" = not yet marked used, but no legal move
   remains for this die anywhere on the board right now (computed live
   from the existing getMaxDiceConstrainedMoves() engine — see
   renderDicePanel()). Visually distinct from both the bright "still
   playable" look and the fully-used look, so a player can tell at a
   glance that this die needs no action and isn't stuck/broken. */
.die-chip.expanded.dead {
  background: linear-gradient(155deg, #241a1a 0%, #171111 60%, #0e0a0a 100%);
  color: #7a5a5a;
  border-color: #5a3a3a;
  border-style: dashed;
}
/* v2.1.5: investigation into a tester report of a Bar-entry 6+5 turn where
   one die is consumed by entry and the other is genuinely unplayable (e.g.
   both possible entry points converge on the same closed point) confirmed
   the underlying diceUsed/legality logic is correct (see change report),
   but the existing v2.1.2 "dead" style above — a subtle color/dashed-border
   difference from "used" — can look too similar to "used" at a glance on a
   small screen. This adds ONE small, unmistakable "blocked" badge to dead
   chips only. Presentation only: no change to chip values, diceUsed,
   legality, tooltip text, or the renderDicePanel() JS that decides used
   vs. dead vs. neither. */
.die-chip.expanded.dead::after {
  content: '\00d7';
  position: absolute;
  top: -6px; right: -6px;
  width: 15px; height: 15px;
  border-radius: 50%;
  background: #5a1a1a;
  color: #ffb3b3;
  border: 1px solid #7a5a5a;
  font-size: 11px;
  font-weight: bold;
  line-height: 13px;
  text-align: center;
  font-family: Arial, sans-serif;
  text-shadow: none;
}

