/* 3DICE — Step 2: fit-to-viewport layout.
   Body fills 100dvh as a flex column; topbar + actions are fixed-height
   chrome; board flex-grows to fill the remaining vertical space.
   Cells share board height evenly via grid-template-rows: repeat(8, 1fr).
   No JS yet — chip + ROLL buttons are visual placeholders. */

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { height: 100%; }

:root {
  --bg:           #0a1530;
  --bg-elev-1:   #0f1d3f;
  --bg-elev-2:   #14264f;
  --bg-row:       #122142;
  --border:       #1d3260;
  --border-hot:   #f0c252;        /* gold-ish accent for risk corners */
  --border-warm:  #5b8db8;        /* cooler accent for mid-risk row */
  --text:         #e4ecff;
  --text-muted:   #8aa2c8;
  --gold-lt:      #ffd882;
  --gold:         #f0c252;
  --teal:         #5ed4d4;
  /* Step 5: 7X3 palette aliases for the chrome + chip ports.
     Source: _staging/games/7x3/assets/css/game.css :root (lines 14-56). */
  --cyan:         #5fb7ff;
  --cyan-soft:    #8fcdff;
  --gold-glow:    #ffe39a;
  --cream:        #fff5d8;
  --txt-dim:      rgba(234, 242, 255, 0.62);
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;             /* dynamic viewport — accounts for browser chrome */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;                /* the page itself never scrolls */
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

/* Stab 136: subtle decorative diamond pattern in the body background.
   Desktop only (>= 768px) — on phones the game's 500px column nearly
   fills the viewport and there's no visible margin to decorate.
   Inline SVG data URI so no extra HTTP request. fill-opacity 0.05 on
   the cyan platform-ambient color (#5fb7ff) renders as a hairline
   diamond every 32px on the dark navy --bg base. Pseudo-element sits
   at z-index -1 (above body background-color, below all content), so
   even non-positioned game elements paint cleanly above it. */
@media (min-width: 768px) {
  body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cpath d='M16 13L19 16L16 19L13 16Z' fill='%235fb7ff' fill-opacity='0.05'/%3E%3C/svg%3E");
    background-repeat: repeat;
  }
}

/* Stab 122: topbar rules (.bar, .bar::after, .brand, .brand .logo,
   .back-to-lobby) are no longer defined by this file. The full bar —
   chassis, left cluster (back/close button + title), right cluster
   (wallet pill + person icon) — is now rendered and styled by
   shared/header. See _staging/shared/header/header.css (#shared-header
   and its children). 3DICE's index.html mounts a single
   <div id="shared-header"> div and chrome.js calls header.init() with
   title='3DICE'. */
/* Stab 88: legacy #userTag is gone; the apex-mirror .lobby-person
   icon sits in the right header zone now and the profile overlay
   below carries name/email/balance. No styling needed for #userTag
   (it's been removed from the markup).
   Stab 110: .lobby-person CSS moved to shared/header/header.css —
   3DICE's header right cluster is now rendered by the shared module
   and its styling is centralized there. */

.lobby-ov {
  position: fixed; inset: 0;
  display: flex; align-items: flex-start; justify-content: center;
  z-index: 100;
  padding: 24px 14px;
  padding-top: calc(24px + env(safe-area-inset-top, 0px));
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.30s ease-out;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.lobby-ov.open { pointer-events: auto; opacity: 1; }
@media (min-width: 540px) {
  .lobby-ov { align-items: center; padding: 24px; }
}
.lobby-ov-bd {
  position: fixed; inset: 0;
  background: rgba(2, 7, 15, 0.70);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.lobby-ov-card {
  position: relative;
  width: 100%; max-width: 440px;
  padding: 22px 18px calc(20px + env(safe-area-inset-bottom, 0px));
  border-radius: 22px;
  background:
    radial-gradient(ellipse 100% 30% at 50% 0%, rgba(95, 183, 255, 0.22) 0%, transparent 60%),
    linear-gradient(180deg, rgba(20, 42, 100, 0.96) 0%, rgba(5, 13, 36, 0.96) 100%);
  border: 1px solid rgba(240, 194, 82, 0.65);
  box-shadow:
    inset 0 1px 0 rgba(255, 245, 216, 0.22),
    inset 0 0 0 1px rgba(255, 227, 154, 0.10),
    0 16px 36px rgba(0, 0, 0, 0.70);
  transform: translateY(-20px);
  transition: transform 0.30s ease-out;
}
.lobby-ov.open .lobby-ov-card { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .lobby-ov, .lobby-ov-card { transition: opacity 0.20s linear; transform: none !important; }
}
.lobby-ov-close {
  position: absolute; top: 12px; right: 12px;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(240, 194, 82, 0.55);
  color: var(--cream);
  font-size: 20px; line-height: 1;
  cursor: pointer; outline: none;
  font-family: inherit;
  display: inline-flex; align-items: center; justify-content: center;
  -webkit-tap-highlight-color: transparent;
  z-index: 2;
}
.lobby-ov-close:hover { border-color: var(--gold-lt); color: var(--gold-glow); }
.lobby-ov-title {
  font-size: 18px; font-weight: 900; letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--cream);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
  padding-right: 36px;
  margin-bottom: 4px;
}
.lobby-ov-sub {
  font-size: 13px; color: var(--text-muted); line-height: 1.5;
  margin-bottom: 14px;
}
.lobby-ov-section {
  display: flex; flex-direction: column; gap: 4px;
  padding: 12px;
  border-radius: 12px;
  background: rgba(2, 7, 15, 0.45);
  border: 1px solid rgba(240, 194, 82, 0.25);
  margin-bottom: 10px;
}
.lobby-ov-section-label {
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--cyan-soft);
}
.lobby-ov-section-value {
  font-size: 15px; font-weight: 800;
  color: var(--cream);
  letter-spacing: 0.02em;
  word-break: break-word;
}
.lobby-ov-section-value.is-balance {
  font-size: 24px;
  background: linear-gradient(180deg, var(--cream) 0%, var(--gold-lt) 60%, var(--gold) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 6px rgba(240, 194, 82, 0.30));
}
.lobby-ov-actions {
  display: grid; gap: 10px;
  grid-template-columns: 1fr 1fr;
  margin-top: 14px;
}
.lobby-ov-actions.single-col { grid-template-columns: 1fr; }
.lobby-ov-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 12px 14px; border-radius: 100px;
  font-family: inherit;
  font-size: 12.5px; font-weight: 900; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--cream);
  border: 1px solid rgba(255, 227, 154, 0.85);
  background:
    radial-gradient(ellipse 80% 100% at 50% 100%, rgba(95, 183, 255, 0.45) 0%, transparent 70%),
    radial-gradient(ellipse 100% 50% at 50% 0%, rgba(255, 227, 154, 0.30) 0%, transparent 70%),
    linear-gradient(180deg, #1f4ea8 0%, #0e1f4a 60%, #050d24 100%);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55), 0 0 8px rgba(255, 227, 154, 0.45);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 245, 216, 0.40),
    0 0 12px rgba(255, 227, 154, 0.30);
  cursor: pointer; outline: none;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.lobby-ov-btn:active { transform: translateY(1px); }
.lobby-ov-btn:disabled { opacity: 0.55; cursor: default; transform: none; }
.lobby-ov-btn.danger {
  border-color: rgba(255, 184, 159, 0.55);
  color: #ffe2d2;
  background: linear-gradient(180deg, rgba(70, 24, 24, 0.55) 0%, rgba(34, 12, 12, 0.85) 100%);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
}
.lobby-ov-btn.danger:hover { border-color: rgba(255, 184, 159, 0.85); }

/* Stab 110: .wallet-btn + .walletIcon CSS moved to shared/header/header.css.
   The wallet pill is now part of the shared header micro-region. */

/* Stab 6c: .bar-row-2 removed — .real-money moved into .bar topbar.
   .help-btn now lives in the action row (Stab 60 placement preserved). */
.help-btn {
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background:
    radial-gradient(circle at 35% 30%, rgba(95, 183, 255, 0.45) 0%, transparent 60%),
    linear-gradient(180deg, rgba(20, 42, 100, 0.85) 0%, rgba(5, 13, 36, 0.95) 100%);
  color: var(--gold-glow);
  border: 1px solid rgba(240, 194, 82, 0.65);
  font-family: inherit;
  font-size: 14px; font-weight: 900;
  text-decoration: none;
  cursor: pointer;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55), 0 0 6px rgba(255, 227, 154, 0.45);
  -webkit-tap-highlight-color: transparent;
}
.help-btn:active { transform: translateY(1px); }

/* Stab 110: .real-money pill + .rm-dot + the body.is-demo / body.is-real
   visibility toggles all removed — the REAL MONEY element is gone from
   the chrome. The wallet pill is now always shown by the shared header
   (its data-state attribute controls whether the balance number is
   visible inside it). The demo↔real signup CTA path is folded into the
   wallet pill's state-aware click handler in chrome.js bindHeaderClicks. */

/* --- Board (flex-grows to fill remaining viewport) --- */
.board-wrap {
  flex: 1 1 auto;
  min-height: 0;                   /* critical for nested flex children */
  padding: 8px 8px;
  display: flex;
  justify-content: center;
  overflow: hidden;
  position: relative;              /* Stab 73: anchor for .dice-overlay */
}
.board {
  width: 100%;
  max-width: 460px;
  display: grid;
  grid-template-columns: 1fr 1.15fr 1fr;
  grid-template-rows: repeat(8, 1fr);   /* even vertical distribution */
  gap: 5px;
  position: relative;                   /* anchor for the zone-divider line below */
}

/* 2026-05-22: thin gold zone divider. Splits the upper zone (rows 1-4,
   exact-sum multipliers ×16 and up) from the lower zone (rows 5-8, ×10
   and below). Pure pseudo-element overlay sits in the grid gap between
   row 4 and row 5 — no layout shift, no cell-border changes. Math:
   .board has 8 equal rows + 7 equal gaps; mid-board (50%) lands exactly
   on the centerline of the gap between row 4 and row 5 (proof:
   total = 8r + 7g, midpoint = 4r + 3.5g = midpoint of the row-4/row-5
   gap). Soft gradient ends so the line fades into the rail rather than
   hitting the board corners abruptly. Subtle by design — visual hint
   only, not a "no-go" red bar. */
.board::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(240, 194, 82, 0.45) 18%,
    rgba(255, 227, 154, 0.85) 50%,
    rgba(240, 194, 82, 0.45) 82%,
    transparent 100%
  );
  transform: translateY(-0.5px);
  pointer-events: none;
  z-index: 1;
}

/* --- Cell base --- */
.cell {
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--border);
  background: var(--bg-elev-1);
  color: var(--text);
  font-family: inherit;
  border-radius: 9px;
  padding: 4px 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 80ms ease;
  touch-action: manipulation;
  outline: none;
  position: relative;             /* anchor for .bet-on-cell overlay */
  min-width: 0;
  min-height: 0;
}
.cell:active { transform: translateY(1px); }
/* Cell content wrapper — enables side-by-side flex layout when .has-bet
   is set on the parent .cell. Default: stacked column (label over mult). */
.cell-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 0;
}
.cell-label {
  font-size: 20px;                 /* Stab 60: -6 from Stab 54's 26 */
  font-weight: 800;
  line-height: 1;
  letter-spacing: 0.02em;
}
.cell-mult {
  margin-top: 2px;
  font-size: 12px;                 /* Stab 60: -3 from Stab 54's 15 */
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  line-height: 1;
}

/* --- Middle column (row pairs) --- */
.cell-row {
  background: var(--bg-row);
  border-color: #294272;
}
.cell-row .cell-label { font-size: 14px; }   /* Stab 60: -4 from Stab 54's 18; "7 or 14" */

/* --- Risk/reward gradient — outer columns top three rows --- */
.cell-corner {
  border-color: var(--border-hot);
  background: linear-gradient(180deg, #1a2b5a 0%, var(--bg-elev-1) 100%);
}
.cell-corner .cell-label { color: var(--gold-lt); }
.cell-corner .cell-mult  { color: var(--gold-lt); }

.cell-hot {
  border-color: rgba(240, 194, 82, 0.55);
}
.cell-hot .cell-mult { color: var(--gold-lt); }

.cell-warm {
  border-color: var(--border-warm);
}
.cell-warm .cell-mult { color: var(--teal); }

.cell-corner-row { border-color: rgba(240, 194, 82, 0.45); }
.cell-corner-row .cell-mult { color: var(--gold-lt); }
.cell-hot-row    { border-color: rgba(240, 194, 82, 0.30); }

/* Stab 6c: .has-bet no longer flips cell to flex-row (that was stretching
   chips into ellipses inside the flex). Cell stays default flex-column
   centered; chip stack is absolute overlay in bottom-right corner.
   The .has-bet class is now an opt-in styling hook only — no layout flip. */
.cell.has-bet {
  /* reserved for future visual distinction; no-op for now */
}

/* --- Rejected-bet flash (per-cell, 400ms) --- */
.cell.rejected { animation: rejected-flash 400ms ease-out; }
@keyframes rejected-flash {
  0%   { box-shadow: 0 0 0 0  rgba(239, 68, 68, 0.65); }
  100% { box-shadow: 0 0 0 12px rgba(239, 68, 68, 0); }
}

/* --- Action footer — TWO rows, secondary chrome on top + primary on bottom.
   Two rows keep ROLL on its own line so it stays the headline action,
   and avoid horizontal cramping at iPhone-SE width. --- */
.actions {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px 12px 10px;
  padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border);
}
.actions-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.actions-top {
  /* Stab PF-5c: drop justify-content:space-between in favor of
     flex-start + margin-left:auto on .actions-top-right. This lets us
     group [help · PF badge · dice result] tightly on the left and
     push [bet-total · CLEAR] further right with explicit breathing
     room — the previous space-between distributed extra space between
     EVERY pair of items, including badge↔result, which read as
     unrelated when they should read as a pair (the badge verifies
     that result). */
  justify-content: flex-start;
  gap: 4px;
}
.actions-top-right {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
  margin-left: auto;        /* push the cluster to the right edge */
  padding-left: 16px;       /* breathing room before bet-total */
}
.actions-bottom {
  justify-content: center;
  /* Stab 75: needed so .bet-error-msg's `bottom: calc(100% + 6px)`
     anchors to this row rather than walking up to the viewport and
     ending up off-screen above the topbar. */
  position: relative;
}

.bet-total {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.bet-total-amount {
  color: var(--gold-lt);
  font-weight: 800;
  font-variant-numeric: tabular-nums;   /* prevents jitter as digits grow */
}

.clear-btn {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  font-family: inherit;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.10em;
  color: var(--text-muted);
  background: transparent;
  border: 1px solid var(--border);
  padding: 6px 12px;
  border-radius: 999px;
  cursor: pointer;
  outline: none;
  -webkit-tap-highlight-color: transparent;
  text-transform: uppercase;
  white-space: nowrap;
  transition: transform 80ms ease, color 120ms ease, border-color 120ms ease;
}
.clear-btn:hover { color: var(--text); border-color: rgba(255, 255, 255, 0.25); }
.clear-btn:active { transform: translateY(1px); }

.sound-toggle {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  line-height: 1;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 999px;
  cursor: pointer;
  outline: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform 80ms ease, border-color 120ms ease;
}
.sound-toggle[aria-pressed="true"] { color: var(--text-muted); border-color: rgba(255, 255, 255, 0.10); }
.sound-toggle:active { transform: translateY(1px); }

/* --- Chip cycler (◀ disc ▶). Sizing/shape mirrors 7X3 .denom/.darr/.chip-sel.
   DENOMS = [1, 5, 10, 25, 50, 100] mirror _staging/games/7x3/assets/js/game.js.
   chrome.js cycles through these and updates .chip-disc[data-value]. --- */
.chip-selector {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0;
}
.chip-arrow {
  appearance: none;
  -webkit-appearance: none;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: inherit;
  font-size: 15px;
  color: var(--text);
  background: linear-gradient(180deg, rgba(20, 42, 100, 0.65) 0%, rgba(10, 24, 56, 0.85) 100%);
  border: 1px solid rgba(240, 194, 82, 0.40);
  border-radius: 11px;
  cursor: pointer;
  outline: none;
  -webkit-tap-highlight-color: transparent;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
  transition: transform 80ms ease;
}
.chip-arrow:active { transform: translateY(1px); }

.chip-display {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0 4px;
}

/* --- CASINO CHIPS (Stab 59 — ported from 7X3 game.css lines 533-607).
   .chip is the base. ::before paints the 6-segment cream-gold edge inserts.
   ::after is the inner disc carrying the body color (set inline by JS via
   chipColor() — `background: inherit` flows through). .chip-val sits on top.
   .chip-sel is the selector variant (full size + extra outer glow).
   .chip-on-board is the on-cell variant (smaller, absolute-positioned). --- */
.chip {
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-weight: 900; color: #fff;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.7), 0 0 4px rgba(0, 0, 0, 0.5);
  position: relative;
  background-clip: padding-box;
  -webkit-tap-highlight-color: transparent;
  font-family: inherit;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  outline: none;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.55),
    0 0 0 2px rgba(240, 194, 82, 0.65),
    0 0 8px rgba(255, 227, 154, 0.20);
}
.chip::before {
  content: '';
  position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(
    var(--gold-glow) 0deg     22deg,
    transparent      22deg     60deg,
    var(--gold-glow) 60deg     82deg,
    transparent      82deg    120deg,
    var(--gold-glow) 120deg   142deg,
    transparent     142deg    180deg,
    var(--gold-glow) 180deg   202deg,
    transparent     202deg    240deg,
    var(--gold-glow) 240deg   262deg,
    transparent     262deg    300deg,
    var(--gold-glow) 300deg   322deg,
    transparent     322deg    360deg
  );
  -webkit-mask: radial-gradient(circle, transparent 60%, #000 60%, #000 90%, transparent 91%);
          mask: radial-gradient(circle, transparent 60%, #000 60%, #000 90%, transparent 91%);
  pointer-events: none;
}
.chip::after {
  content: '';
  position: absolute; inset: 12%;
  border-radius: 50%;
  background: inherit;
  border: 1px dashed rgba(255, 245, 216, 0.40);
  box-shadow:
    inset 0 2px 3px rgba(255, 245, 216, 0.30),
    inset 0 -3px 4px rgba(0, 0, 0, 0.35),
    inset 0 0 8px rgba(95, 183, 255, 0.15);
  pointer-events: none;
}
.chip > .chip-val { position: relative; z-index: 2; pointer-events: none; }

/* Selector variant — full size + extra outer glow. */
.chip-sel {
  width: 56px; height: 56px;
  font-size: 18px;
  border: 1.5px solid rgba(0, 0, 0, 0.45);
  margin: 0 4px;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.55),
    0 0 0 2px rgba(240, 194, 82, 0.85),
    0 0 14px rgba(255, 227, 154, 0.35),
    0 6px 14px rgba(0, 0, 0, 0.65);
}
.chip-sel:active { transform: scale(0.93); }

/* Stab 6c — on-cell stack variant. Mirrors 7X3's .chip-on-board pattern
   (game.css:586-593) scaled smaller for 3DICE's tighter cells.
   bet.js renders one element per chip with bottom + zIndex set inline;
   stack grows upward, newest at bottom-front.
   ELONGATION GUARDS (per Stab 6c spec): explicit width + height +
   flex:0 0 auto + aspect-ratio:1 — three independent rules so even if
   a parent flips to flex-row, the chip stays a perfect circle. */
.chip-on-board {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  aspect-ratio: 1 / 1;
  font-size: 10px;
  position: absolute;
  right: 4px;
  /* `bottom` and `z-index` set inline by bet.js per chip in the stack. */
  border: 1.5px solid rgba(0, 0, 0, 0.40);
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}

/* --- ROLL — the headline action. Visually echoes 7X3's gold-rim crystal
   pill: cobalt body, warm gold rim + glow. Simplified vs 7X3's full
   layered gradient stack to keep the static skeleton readable. --- */
.roll-btn {
  appearance: none;
  -webkit-appearance: none;
  flex: 1 1 auto;
  min-width: 110px;
  max-width: 200px;
  height: 52px;
  padding: 0 20px;
  font-family: inherit;
  font-size: 16px;
  font-weight: 900;
  letter-spacing: 0.18em;
  color: var(--text);
  background:
    radial-gradient(ellipse 80% 100% at 50% 100%, rgba(95, 183, 255, 0.45) 0%, transparent 70%),
    radial-gradient(ellipse 100% 50% at 50% 0%, rgba(255, 227, 154, 0.25) 0%, transparent 70%),
    linear-gradient(180deg, #1f4ea8 0%, #0e1f4a 60%, #050d24 100%);
  border: 1px solid rgba(255, 227, 154, 0.85);
  border-radius: 999px;
  cursor: pointer;
  outline: none;
  -webkit-tap-highlight-color: transparent;
  text-transform: uppercase;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.55),
    0 0 12px rgba(255, 227, 154, 0.35),
    inset 0 1px 0 rgba(255, 245, 216, 0.40),
    inset 0 -2px 4px rgba(0, 0, 0, 0.55),
    0 4px 10px rgba(0, 0, 0, 0.55);
  transition: transform 80ms ease;
}
.roll-btn:active { transform: translateY(1px); }

/* --- Start overlay (Stab 60 — mirrors 7X3's .start-ov pattern).
   Tap-to-dismiss gates Web Audio. Always shows on load (matches 7X3 —
   no sessionStorage skip). chrome.js wires the click handler. --- */
.start-ov {
  position: absolute; inset: 0; z-index: 50;
  background:
    radial-gradient(ellipse 60% 50% at 50% 45%, rgba(31, 78, 168, 0.30) 0%, transparent 70%),
    radial-gradient(ellipse 80% 40% at 50% 80%, rgba(95, 183, 255, 0.15) 0%, transparent 70%),
    rgba(2, 6, 15, 0.96);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px;
  padding: 0 20px;
  cursor: pointer;
  /* Stab 6d: visible "lift" dismiss — fade out AND slide upward over
     350ms when .hidden is added by chrome.js. Replaces Stab 60's
     opacity-only 400ms transition. */
  opacity: 1;
  transform: translateY(0);
  transition: opacity 2500ms ease-out, transform 2500ms ease-out;
}
.start-ov.hidden {
  opacity: 0;
  transform: translateY(-100%);
  pointer-events: none;
}

/* Site logo — copied byte-for-byte from 7X3 (game.css:756-774).
   Stab 6c: replaces Stab 61's text-only .site-name. The link uses
   the apex logo PNG (28×28) + 13px gold-gradient "1gambling.online".
   Anchored at the top-center of the overlay, NOT in the flex-column
   stack — keeps the wordmark out of the title's vertical line.
   Critical: NO stopPropagation in the markup — taps bubble to
   .start-ov's dismiss handler first, then navigation proceeds. */
.start-ov .site-logo {
  position: absolute;
  top: calc(14px + env(safe-area-inset-top, 0px));
  left: 50%; transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 8px;
  text-decoration: none; opacity: 0.9;
  -webkit-tap-highlight-color: transparent;
}
.start-ov .site-logo img {
  height: 28px; width: auto; display: block;
  filter: drop-shadow(0 0 4px rgba(255, 227, 154, 0.30));
}
.start-ov .site-logo span {
  font-size: 13px; font-weight: 800; letter-spacing: 0.04em; line-height: 1;
  background: linear-gradient(180deg, var(--cream) 0%, var(--gold-lt) 60%, var(--gold) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.start-ov .site-logo:hover { opacity: 1; }

/* "3DICE" game-name title — UNCHANGED size/styling per Stab 60b spec. */
.start-ov .title {
  font-size: 36px; font-weight: 900; letter-spacing: 0.18em;
  background: linear-gradient(180deg, var(--cream) 0%, var(--gold-lt) 50%, var(--gold) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 14px rgba(240, 194, 82, 0.45));
}

.start-ov .icon { font-size: 48px; filter: drop-shadow(0 0 18px rgba(255, 227, 154, 0.55)); }

/* Payout hook (Stab 60b — promotional pull below the dice emoji).
   Gold accent matching the chip palette, attention-pulling weight. */
.start-ov .payout-hook {
  font-size: 18px; font-weight: 800;
  color: var(--gold-glow);
  letter-spacing: 0.06em;
  text-align: center;
  text-shadow: 0 0 10px rgba(255, 227, 154, 0.45);
}

.start-ov .tip {
  font-size: 15px; color: #ffb24a;
  text-align: center; max-width: 280px; padding: 0 16px;
  line-height: 1.45;
  text-shadow: 0 0 8px rgba(255, 178, 74, 0.30);
}

/* Stab 6d: brand footer inside the start overlay (Legal / Support / Share).
   Markup + CSS copied byte-for-byte from 7X3 (game.css:778-797).
   .start-ov is the positioned ancestor so position:absolute anchors here.
   No media-query overrides — same at all breakpoints (matches 7X3). */
.start-ov-footer {
  position: absolute;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  left: 50%; transform: translateX(-50%);
  display: flex; gap: 14px; align-items: center;
}
.start-ov-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  color: var(--gold-lt);
  background: none; border: none; padding: 0; margin: 0;
  font: inherit; cursor: pointer; text-decoration: none;
  filter: drop-shadow(0 0 4px rgba(255, 227, 154, 0.30));
  -webkit-tap-highlight-color: transparent;
}
.start-ov-icon:hover {
  color: var(--cream);
  filter: drop-shadow(0 0 6px rgba(255, 227, 154, 0.55));
}
.start-ov-icon svg { display: block; }

@media (min-width: 480px) {
  .start-ov .payout-hook { font-size: 20px; }
}

/* --- Slightly larger screens — give cells / chip / ROLL a bit more weight --- */
@media (min-width: 480px) {
  .board { gap: 7px; }
  .cell-label { font-size: 22px; }            /* Stab 60: -6 from 28 */
  .cell-row .cell-label { font-size: 15px; }  /* Stab 60: -4 from 19 */
  .cell-mult  { font-size: 13px; }            /* Stab 60: -3 from 16 */
  .chip-arrow { width: 40px; height: 40px; font-size: 16px; }
  .chip-sel    { width: 64px; height: 64px; font-size: 20px; }
  /* Stab 6c: on-cell stack chip stays compact even on larger screens
     so the stack of up to 5 fits in a 64-72px tall cell without
     overflowing past the cell boundary. */
  .chip-on-board { width: 28px; height: 28px; font-size: 11px; }
  .roll-btn   { height: 56px; font-size: 18px; }
}

/* ─── Stab 73: dice render overlay + win highlight + result banner ──────
   The .dice-overlay sits absolute inside .board-wrap (which got
   position: relative above). Hidden + non-blocking when idle; reveals
   over the board cells during the dice replay animation. The replay
   player creates a Three.js canvas that fills this container. */
.dice-overlay {
  position: absolute;
  inset: 0;
  z-index: 50;                     /* above cells; below .gam-modal-ov (200+) */
  pointer-events: none;            /* dice are non-interactive */
  display: none;
}
.dice-overlay.active {
  display: block;
}

/* Stab 85: winning-cell highlight. Solid gold backplate on every cell
   matching the dice sum (the single-number cell + the pair-row cell).
   Persists until the player's next action (chip placement clears it
   in bet.js; ROLL clears it at playRound entry in round.js). The
   pre-Stab-85 pulsing .cell-win class is replaced by this — pulsing
   was tied to a 2.5s timer that no longer applies. !important on the
   background overrides .cell-corner / .cell-hot / .cell-warm gradient
   backplates which would otherwise win on specificity. */
/* Stab 91 redesign: from solid gold fill (Stab 85) to subtle rim+tint.
   The cell's original dark backplate remains; a soft gold radial tint
   layers on top via background-image (which stacks above background-
   color). The cell text keeps its original light color — DO NOT
   force-darken it; with the new design the background is dark enough
   that the original text reads cleanly. */
.cell.cell-win-gold {
  background-image: radial-gradient(
    circle at center,
    rgba(255, 193, 7, 0.22) 0%,
    rgba(255, 193, 7, 0.10) 40%,
    rgba(255, 193, 7, 0)    70%
  );
  /* Stab 93: rim thickness now inherits the default `.cell` border
     (1px solid var(--border) — see :349-352). Only the color changes
     to gold; thickness is identical to every non-winning cell. */
  border-color: #d4a017 !important;
  box-shadow:
    0 0 0 1px rgba(255, 193, 7, 0.15) inset,
    0 0 10px rgba(255, 193, 7, 0.25);
}

/* Stab 91: celebration pulse — applied alongside .cell-win-gold for
   ~1.6 s on settle on win. Rim + outer glow brighten and settle back
   to the static state; static state persists via .cell-win-gold until
   the next chip placement clears it. */
@keyframes cellWinPulse {
  0% {
    border-color: #d4a017;
    box-shadow:
      0 0 0 1px rgba(255, 193, 7, 0.15) inset,
      0 0 10px rgba(255, 193, 7, 0.25);
  }
  50% {
    border-color: #ffd54f;
    box-shadow:
      0 0 0 1px rgba(255, 215, 100, 0.40) inset,
      0 0 22px rgba(255, 215, 100, 0.55);
  }
  100% {
    border-color: #d4a017;
    box-shadow:
      0 0 0 1px rgba(255, 193, 7, 0.15) inset,
      0 0 10px rgba(255, 193, 7, 0.25);
  }
}
.cell.cell-win-pulse {
  animation: cellWinPulse 0.8s ease-in-out 2;
}

/* Stab 91: 360° spin on the winning player chip stack at settle.
   Rotates over 1.0 s about the vertical axis. Self-cleans via the
   class removal in round.js after animationend (or you can rely on
   the animation simply ending — the class stays but rotation returns
   to 0deg at 100%). Multiple cells animate simultaneously. */
@keyframes chipWinningSpin {
  0%   { transform: rotateY(0deg); }
  100% { transform: rotateY(360deg); }
}
.chip-winning-spin {
  animation: chipWinningSpin 1.0s ease-in-out 1;
  transform-style: preserve-3d;
}

/* Stab 91 → Stab 95 → Stab 96 → Stab 98: larger Unicode pips in the
   result line. Sum digit stays default size — only the dice glyphs are
   wrapped in .result-pip. Stab 95 bumped 200% → 300%. Stab 96 set
   line-height: 1.4 to contain the glyph; that made the pill ~58px tall
   and crowded the game board. Stab 98 compresses the line-box to 0.7
   so the pill hugs the surrounding digits tightly — the glyph itself
   stays ~36 px and visually extends ~5-6 px above and below the pill
   as an intentional accent (paired with .round-result overflow:
   visible below). transform: translateY(-1px) lifts the glyph one
   pixel so its optical centre lines up with the "+", "=", and sum
   digits' baseline. U+FE0E in round.js DICE_PIPS still forces iOS
   text-style rendering (outlined dice with inner pips). */
.result-pip {
  display: inline-block;
  /* Stab PF-5c: 300% → 240%. Still legible at small viewports, no
     longer crowds the actions-top row alongside the PF badge. */
  font-size: 240%;
  /* Stab 110: explicit font-weight: 400 (normal) overrides the parent
     .round-result's font-weight: 700. The cascaded bold was making
     the Unicode dice glyphs render as filled black squares on some
     platforms (notably Chromium on Android), obscuring the pip dots
     inside the dice outlines. Normal weight gives the outlined-dice-
     with-visible-pips treatment we want. Only this rule changes;
     the surrounding result-row text ("Last:", "=", sum digit,
     "+X chips") stays bold via the unchanged .round-result rule. */
  font-weight: 400;
  line-height: 0.7;
  vertical-align: middle;
  transform: translateY(-1px);
}

/* Stab 91: floating "+X chips" win amount display. Positioned over
   the dice landing zone, fades in 0.5 s with subtle scale-up, stays
   visible until the next chip placement triggers the clear. Layered
   above the dice mesh (z-index 60 vs diceOverlay's 50). */
.win-amount-display {
  position: absolute;
  top: 22%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.85);
  font-size: 2.5rem;
  font-weight: 900;
  letter-spacing: 0.04em;
  color: #ffd54f;
  text-shadow: 0 0 12px rgba(255, 193, 7, 0.6), 0 1px 2px rgba(0, 0, 0, 0.7);
  z-index: 60;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease-out, transform 0.5s ease-out;
  white-space: nowrap;
}
.win-amount-display.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1.0);
}
@media (min-width: 720px) {
  .win-amount-display { font-size: 3.2rem; }
}

/* ─── Stab 94: win-celebration FX overlay ────────────────────────────
   Three effect families render inside #winFxLayer at settle on win:
   rising money particles, firework bursts, zigzag money tokens that
   travel to the wallet pill. All animations use only
   transform + opacity for 60fps performance — no layout-triggering
   property animations. pointer-events: none on the layer is mandatory
   so the FX never block chip placement. Children self-clean via
   animationend listeners; the whole layer is wiped atomically by
   clearPostRoundUI on the next-chip tap. */
.win-fx-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 70;
  overflow: visible;            /* tokens fly out to the header buttons */
}

/* Rising money particles — small gold discs, ~14 px. JS sets inline
   --tx (horizontal drift target) and --rot (rotation amount) plus
   left/top spawn position. The keyframe holds opacity through most of
   the rise then fades over the last ~30%. animation-delay on the
   element staggers spawn times over the first 0.5 s of celebration. */
.fx-money-particle {
  position: absolute;
  width: 14px; height: 14px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 30% 30%, #fff3c4 0%, #ffd54f 50%, #d4a017 100%);
  border: 1px solid #c79100;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 0 6px rgba(255, 213, 79, 0.55);
  opacity: 0;
  --tx: 0px;
  --rot: 0deg;
  animation: fxRise 1.8s ease-out forwards;
  will-change: transform, opacity;
}
@keyframes fxRise {
  0%   { opacity: 0; transform: translate(0, 0) rotate(0deg) scale(0.6); }
  10%  { opacity: 1; transform: translate(calc(var(--tx) * 0.1), -20px) rotate(calc(var(--rot) * 0.1)) scale(1); }
  70%  { opacity: 1; transform: translate(calc(var(--tx) * 0.7), -210px) rotate(calc(var(--rot) * 0.7)) scale(1); }
  100% { opacity: 0; transform: translate(var(--tx), -300px) rotate(var(--rot)) scale(0.85); }
}

/* Firework burst — a centered container with 12 radial spokes. Each
   spoke is a 70 px gold line that scaleY-grows from the center then
   fades. transform-origin: 50% 100% so scale animates outward from
   the burst center. */
.fx-firework-burst {
  position: absolute;
  width: 0; height: 0;
  pointer-events: none;
}
.fx-firework-spoke {
  position: absolute;
  left: -1px;
  top: -70px;
  width: 2px;
  height: 70px;
  background: linear-gradient(to top, rgba(255, 213, 79, 1) 0%, rgba(255, 215, 100, 0.9) 50%, rgba(255, 213, 79, 0) 100%);
  border-radius: 1px;
  transform-origin: 50% 100%;
  transform: rotate(var(--angle, 0deg)) scaleY(0);
  opacity: 0;
  animation: fxFireworkSpoke 0.6s ease-out forwards;
  will-change: transform, opacity;
}
@keyframes fxFireworkSpoke {
  0%   { transform: rotate(var(--angle, 0deg)) scaleY(0);    opacity: 1; }
  40%  { transform: rotate(var(--angle, 0deg)) scaleY(1);    opacity: 1; }
  100% { transform: rotate(var(--angle, 0deg)) scaleY(1);    opacity: 0; }
}

/* Zigzag money token — slightly larger chip-like disc that travels
   from the winning area to the wallet pill via four
   keyframe waypoints. JS sets --dx1..--dx3 + --dxF (and dy*) so the
   path resolves to the target's position relative to #winFxLayer.
   Final keyframe shrinks + fades = "absorbed by the button". */
.fx-zigzag-token {
  position: absolute;
  width: 18px; height: 18px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 30% 30%, #fff3c4 0%, #ffd54f 50%, #d4a017 100%);
  border: 1px solid #c79100;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5), 0 0 10px rgba(255, 213, 79, 0.85);
  opacity: 0;
  --dx1: 0px; --dy1: 0px;
  --dx2: 0px; --dy2: 0px;
  --dx3: 0px; --dy3: 0px;
  --dxF: 0px; --dyF: 0px;
  animation: fxZigzag 1.15s ease-in-out forwards;
  will-change: transform, opacity;
}
@keyframes fxZigzag {
  0%   { opacity: 0; transform: translate(0, 0) scale(1); }
  10%  { opacity: 1; transform: translate(0, 0) scale(1.05); }
  35%  { opacity: 1; transform: translate(var(--dx1), var(--dy1)) scale(1.05); }
  60%  { opacity: 1; transform: translate(var(--dx2), var(--dy2)) scale(1.05); }
  82%  { opacity: 1; transform: translate(var(--dx3), var(--dy3)) scale(1.05); }
  95%  { opacity: 1; transform: translate(var(--dxF), var(--dyF)) scale(0.55); }
  100% { opacity: 0; transform: translate(var(--dxF), var(--dyF)) scale(0.2); }
}

/* Brief pulse on the target button when a zigzag token lands. Class
   added by spawnMoneyZigzag at the moment of token arrival; removed
   by a 250 ms setTimeout (or by clearPostRoundUI on next chip). Uses
   filter/box-shadow on a button — both are GPU-accelerated.
   Stab 110: .real-money compound selector dropped (element removed);
   the bare .wallet-received-money still attaches to the wallet pill
   rendered by shared/header so the pulse animation keeps working. */
.wallet-received-money {
  animation: walletReceivedMoney 0.25s ease-out 1;
}
@keyframes walletReceivedMoney {
  0%   { filter: brightness(1)    drop-shadow(0 0 0   rgba(255, 215, 100, 0)); }
  50%  { filter: brightness(1.25) drop-shadow(0 0 10px rgba(255, 215, 100, 0.85)); }
  100% { filter: brightness(1)    drop-shadow(0 0 0   rgba(255, 215, 100, 0)); }
}

/* Stab 77: persistent result pill in the action row. Replaces the
   transient floating banner. Renders "Last: D1 + D2 + D3 = SUM" or
   "Last: D1 + D2 + D3 = SUM · +X chips" — populated when dice settle,
   cleared when the next ROLL fires. Sits between the help button on
   the left and the Bet total / CLEAR on the right (flex space-between
   distributes them). Hidden when empty so the row doesn't reserve a
   gap before the first round. */
.round-result {
  display: none;                   /* hidden when no result (between rounds) */
  font-size: 12px;
  font-weight: 700;
  color: var(--cyan-soft);
  letter-spacing: 0.03em;
  white-space: nowrap;
  /* Stab 98: was overflow: hidden + text-overflow: ellipsis. The result
     content is a fixed shape (⚀ + ⚁ + ⚂ = SUM, ~200 px wide) and never
     wraps or grows long enough to ellipsis-truncate, so the hidden
     overflow was effectively unused. Switching to visible lets the
     compressed .result-pip line-box (line-height: 0.7) overflow the
     pill vertically — the dice glyphs extend ~5-6 px above and below
     the pill outline as an intentional visual accent. */
  overflow: visible;
  max-width: 60%;
  min-width: 0;
  flex: 0 1 auto;
  /* Stab 98: was padding: 4px 10px. Vertical padding reduced to 2 px
     each side so the pill clamps tightly to the inline content. With
     .result-pip line-height: 0.7 the pill ends up ~31 px tall vs the
     pre-Stab ~58 px — the .actions-top row gets shorter and the
     game board reclaims the difference. */
  padding: 2px 10px;
  border-radius: 100px;
  background: rgba(2, 6, 15, 0.45);
  border: 1px solid rgba(95, 183, 255, 0.30);
}
.round-result.show {
  display: inline-block;           /* revealed once dice settle */
}
.round-result.win {
  color: var(--gold-glow);
  border-color: rgba(240, 194, 82, 0.55);
  background:
    radial-gradient(ellipse 100% 100% at 50% 0%, rgba(255, 227, 154, 0.18) 0%, transparent 70%),
    rgba(2, 6, 15, 0.55);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
}

/* Legacy transient banner (Stab 73, retired Stab 77). Kept hidden so
   any stale DOM node left over from before the cleanup doesn't render
   in the wrong place. Stab 77 stopped creating the element entirely
   and routes results to .round-result above instead. */
.round-result-banner { display: none !important; }

/* Inline message near ROLL button — used for "Place bets first" + API
   errors. Hidden by default; .show fades it in for 2-3s then back out. */
.bet-error-msg {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 12px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.04em;
  color: var(--cyan-soft);
  background: rgba(20, 42, 100, 0.85);
  border: 1px solid rgba(95, 183, 255, 0.45);
  border-radius: 8px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
  z-index: 30;
}
.bet-error-msg.show { opacity: 1; }

/* Stab 95: .config-error-blocker REMOVED. Stab 87's blocker fired
   when /game-config/3dice's bounded retry sequence exhausted; Stab 95
   replaced that with unbounded patient retry, so the blocker can
   never fire — no need for its CSS. Owner sees only the calm
   .config-loading-hint pill below until config arrives. */

/* Stab 87: non-blocking "Loading game data…" pill shown by bet.js when
   a chip placement (or by round.js when a ROLL tap) lands BEFORE the
   /game-config/3dice fetch has resolved.
   Mirrors .bet-error-msg position (above the bottom action row) but
   uses a calm gold accent instead of cyan info / red error tones so
   the player reads it as "wait a moment" not "something is wrong". */
.config-loading-hint {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 12px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.04em;
  color: var(--gold-lt);
  background: rgba(40, 30, 6, 0.88);
  border: 1px solid rgba(240, 194, 82, 0.45);
  border-radius: 8px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease-out;
  z-index: 5;
}
.config-loading-hint.show { opacity: 1; }

.bet-error-msg.err {
  color: #ffb89f;
  border-color: rgba(255, 184, 159, 0.55);
}

/* ROLL button while a round is in flight — slightly dimmed, no transform
   on tap (since clicks are ignored anyway). */
.roll-btn:disabled,
.roll-btn.rolling {
  opacity: 0.55;
  cursor: default;
}
.roll-btn.rolling:active { transform: none; }

/* ─── Stab 83: legal menu overlay ────────────────────────────────────
   Three-item menu opened from the start overlay's LEGAL icon. Layout
   + selectors mirror apex's #legalOv (main-domain/index.html
   :367-440 + :291-324). Sits above the start overlay (z-index 100
   vs start-ov 50), backdrop dims everything behind. */
.legal-menu-ov {
  position: fixed; inset: 0;
  display: flex; align-items: flex-start; justify-content: center;
  z-index: 100;
  padding: 24px 14px;
  padding-top: calc(24px + env(safe-area-inset-top, 0px));
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.30s ease-out;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.legal-menu-ov.open { pointer-events: auto; opacity: 1; }
@media (min-width: 540px) {
  .legal-menu-ov { align-items: center; padding: 24px; }
}
.legal-menu-bd {
  position: fixed; inset: 0;
  background: rgba(2, 7, 15, 0.70);
  -webkit-backdrop-filter: blur(4px);
          backdrop-filter: blur(4px);
}
.legal-menu-card {
  position: relative;
  width: 100%; max-width: 440px;
  padding: 22px 18px calc(20px + env(safe-area-inset-bottom, 0px));
  border-radius: 22px;
  background:
    radial-gradient(ellipse 100% 30% at 50% 0%, rgba(95, 183, 255, 0.22) 0%, transparent 60%),
    linear-gradient(180deg, rgba(20, 42, 100, 0.96) 0%, rgba(5, 13, 36, 0.96) 100%);
  border: 1px solid rgba(240, 194, 82, 0.65);
  box-shadow:
    inset 0 1px 0 rgba(255, 245, 216, 0.22),
    inset 0 0 0 1px rgba(255, 227, 154, 0.10),
    0 16px 36px rgba(0, 0, 0, 0.70);
  transform: translateY(-20px);
  transition: transform 0.30s ease-out;
}
.legal-menu-ov.open .legal-menu-card { transform: translateY(0); }
.legal-menu-close {
  position: absolute; top: 12px; right: 12px;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(240, 194, 82, 0.55);
  color: var(--cream);
  font-size: 20px; line-height: 1;
  cursor: pointer; outline: none;
  font-family: inherit;
  display: inline-flex; align-items: center; justify-content: center;
  -webkit-tap-highlight-color: transparent;
  z-index: 2;
}
.legal-menu-close:hover { border-color: var(--gold-lt); color: var(--gold-glow); }
.legal-menu-title {
  font-size: 18px; font-weight: 900; letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--cream);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
  padding-right: 36px;
  margin-bottom: 4px;
}
.legal-menu-sub {
  font-size: 13px; color: var(--text-muted); line-height: 1.5;
  margin-bottom: 14px;
}
.legal-menu-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px;
  border-radius: 12px;
  background: rgba(2, 7, 15, 0.45);
  border: 1px solid rgba(240, 194, 82, 0.30);
  color: var(--cream);
  text-decoration: none;
  font-family: inherit;
  font-size: 14px; font-weight: 700; letter-spacing: 0.02em;
  cursor: pointer; outline: none;
  transition: border-color 0.18s, background 0.18s, transform 0.06s;
  -webkit-tap-highlight-color: transparent;
}
.legal-menu-row + .legal-menu-row { margin-top: 8px; }
.legal-menu-row:hover  { border-color: var(--gold-lt); background: rgba(20, 42, 100, 0.55); }
.legal-menu-row:active { transform: translateY(1px); }
.legal-menu-row-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex-shrink: 0;
  border-radius: 50%;
  background: linear-gradient(180deg, var(--gold-glow) 0%, var(--gold-lt) 60%, var(--gold) 100%);
  color: #1a1408;
  box-shadow: inset 0 1px 0 rgba(255, 245, 216, 0.40);
}
.legal-menu-row-icon svg { width: 20px; height: 20px; display: block; }
.legal-menu-row-label { flex: 1 1 auto; }
.legal-menu-row-meta {
  display: inline-flex; align-items: center;
  font-size: 11px; font-weight: 800; letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--cyan-soft);
  min-width: 60px; justify-content: flex-end;
}

/* ─── Stab 84: help / instructions overlay ──────────────────────────
   Same apex-derived card chassis as .legal-menu-*. Per Stab 84 spec:
   open animation is 300ms ease-in with translateY(8px → 0) + backdrop
   opacity 0 → 1; close is 200ms ease-out reverse. Stays under
   z-index 100 so the existing modals (200+) can layer above. */
.help-ov {
  position: fixed; inset: 0;
  display: flex; align-items: flex-start; justify-content: center;
  z-index: 100;
  padding: 24px 14px;
  padding-top: calc(24px + env(safe-area-inset-top, 0px));
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.20s ease-out;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.help-ov.open {
  pointer-events: auto;
  opacity: 1;
  transition: opacity 0.30s ease-in;
}
@media (min-width: 540px) {
  .help-ov { align-items: center; padding: 24px; }
}
.help-ov-bd {
  position: fixed; inset: 0;
  background: rgba(2, 7, 15, 0.70);
  -webkit-backdrop-filter: blur(4px);
          backdrop-filter: blur(4px);
}
.help-ov-card {
  position: relative;
  width: 100%; max-width: 460px;
  padding: 22px 18px calc(20px + env(safe-area-inset-bottom, 0px));
  border-radius: 22px;
  background:
    radial-gradient(ellipse 100% 30% at 50% 0%, rgba(95, 183, 255, 0.22) 0%, transparent 60%),
    linear-gradient(180deg, rgba(20, 42, 100, 0.96) 0%, rgba(5, 13, 36, 0.96) 100%);
  border: 1px solid rgba(240, 194, 82, 0.65);
  box-shadow:
    inset 0 1px 0 rgba(255, 245, 216, 0.22),
    inset 0 0 0 1px rgba(255, 227, 154, 0.10),
    0 16px 36px rgba(0, 0, 0, 0.70);
  transform: translateY(8px);
  transition: transform 0.20s ease-out;
}
.help-ov.open .help-ov-card {
  transform: translateY(0);
  transition: transform 0.30s ease-in;
}
.help-ov-close {
  position: absolute; top: 12px; right: 12px;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(240, 194, 82, 0.55);
  color: var(--cream);
  font-size: 20px; line-height: 1;
  cursor: pointer; outline: none;
  font-family: inherit;
  display: inline-flex; align-items: center; justify-content: center;
  -webkit-tap-highlight-color: transparent;
  z-index: 2;
}
.help-ov-close:hover { border-color: var(--gold-lt); color: var(--gold-glow); }
.help-ov-title {
  font-size: 18px; font-weight: 900; letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--cream);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
  padding-right: 36px;
  margin-bottom: 12px;
}
.help-ov-section { margin-top: 12px; }
.help-ov-section:first-of-type { margin-top: 0; }
.help-ov-section-title {
  margin: 0 0 4px 0;
  font-size: 13px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--gold-lt);
}
/* When the heading is itself a link (e.g. the external "Ultimate Guide"
   blurb), the bare <a> would otherwise inherit the browser's blue link
   default. Pin all four link states to the same --gold-lt the sibling
   non-linked headings use, and keep the underline so it still reads as
   a link. Color + underline only — font-size / weight / spacing stay
   on the parent .help-ov-section-title rule above. */
.help-ov-section-title a,
.help-ov-section-title a:link,
.help-ov-section-title a:visited,
.help-ov-section-title a:hover,
.help-ov-section-title a:active {
  color: var(--gold-lt);
  text-decoration: underline;
}
.help-ov-section-body {
  margin: 0;
  font-size: 13px; line-height: 1.55;
  color: var(--text);
}
.help-ov-section-body b { color: var(--gold-glow); font-weight: 800; }

/* Stab 134: .auth-iframe-overlay + .auth-iframe-overlay iframe +
   html.auth-overlay-open / body.auth-overlay-open rules removed
   from this file. The chassis lives in shared/iframe-overlay/
   iframe-overlay.css now, loaded via <link> in index.html. Class
   names renamed to .iframe-overlay / .iframe-overlay-open in the
   shared copy (forward-compatible with non-auth iframes). The
   shared module adds an animated fade-in + slide-in transition
   that the local copies lacked. */

/* ─── Stab PF-5b: provably-fair badge inside the actions-top row ───
   The badge sits next to the help (?) button — icon-only, enlarged,
   clickable. Opens /auth/?mode=fairness via openAuthIframe(). The
   separate .pf-row strip from PF-5 is gone; per-round info lives on
   the badge's title attribute (full triple: nonce + client_seed +
   server_seed_hash) and inline in #roundResult (" · nonce N"). Size
   matches .help-btn so the two read as siblings in the row.
*/
.pf-badge {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  /* Stab PF-5c: tighten gap to #roundResult so the badge + dice read
     as one unit (the badge VERIFIES that result). The actions-top
     gap is 4px now; the negative right-margin pulls the result span
     even closer. The badge stays separated from #helpBtn by the
     row's gap. */
  margin-left: 0;
  margin-right: -2px;
  padding: 0;
  border-radius: 50%;
  background: rgba(2, 7, 15, 0.40);
  border: 1px solid rgba(255, 227, 154, 0.55);
  cursor: pointer;
  outline: none;
  -webkit-tap-highlight-color: transparent;
}
.pf-badge:hover  { border-color: #ffd882; }
.pf-badge:active { transform: translateY(1px); }
.pf-badge-icon {
  width: 22px;
  height: 22px;
  display: block;
  filter: drop-shadow(0 0 3px rgba(255, 227, 154, 0.45));
}

/* Stab FIX-NONCE-ROW: .round-result .result-nonce (PF-5c) deleted —
   the inline " · nonce N" span removed from round.js. PF nonce stays
   reachable via the PF badge's title attribute. */

