*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { overflow-x: hidden; }
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: #0f0f0f;
  color: #e8e8e8;
  min-height: 100vh;
  overflow-x: hidden;
  /* App-wide: text isn't selectable/highlightable (this is a game UI, not a document);
     also kills the iOS long-press selection callout. Editable fields opt back in below. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
/* Editable fields must stay selectable so typing/editing works normally. */
input, textarea, [contenteditable="true"] {
  -webkit-user-select: text;
  user-select: text;
}

/* Auth screen */
#auth-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

.auth-card {
  background: #1a1a1a;
  border: 0.5px solid #2e2e2e;
  border-radius: 16px;
  padding: 2rem;
  width: 100%;
  max-width: 380px;
}

.auth-card h1 { font-size: 28px; margin-bottom: 4px; text-align: center; }
.auth-sub { text-align: center; color: #888; font-size: 14px; margin-bottom: 1.5rem; }

.auth-tabs { display: flex; gap: 8px; margin-bottom: 1.25rem; }
.auth-tab {
  flex: 1; padding: 8px; border-radius: 8px;
  border: 0.5px solid #2e2e2e; background: transparent;
  color: #888; cursor: pointer; font-size: 14px; transition: all .15s, transform .05s, filter .05s;
}
.auth-tab.active { background: #2e2e2e; color: #e8e8e8; }
.auth-tab:active { transform: scale(.97); filter: brightness(.85); }

.auth-card input, .card input[type=text], .card input[type=number] {
  width: 100%; padding: 10px 12px; margin-bottom: 10px;
  background: #111; border: 0.5px solid #2e2e2e;
  border-radius: 8px; color: #e8e8e8; font-size: 14px;
}
.auth-card input:focus, .card input:focus { outline: none; border-color: #555; }

.password-field { position: relative; margin-bottom: 10px; }
.password-field input { margin-bottom: 0; padding-right: 40px; }
.password-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  background: none; border: none; color: #888; cursor: pointer;
  font-size: 14px; padding: 4px 6px; line-height: 1; transition: color .15s, filter .05s;
}
.password-toggle:hover { color: #e8e8e8; }
.password-toggle:active { filter: brightness(.7); }

.primary-btn {
  width: 100%; padding: 11px; background: #e8e8e8; color: #0f0f0f;
  border: none; border-radius: 8px; font-size: 15px;
  font-weight: 600; cursor: pointer; transition: opacity .15s, transform .05s, filter .05s;
}
.primary-btn:hover { opacity: .85; }
.primary-btn:active { transform: scale(.98); filter: brightness(.85); }

.auth-error { color: #E24B4A; font-size: 13px; margin-top: 10px; text-align: center; min-height: 20px; }
@keyframes auth-error-flash { 0% { opacity: 0; transform: translateY(-2px); } 100% { opacity: 1; transform: translateY(0); } }
.auth-error.flash { animation: auth-error-flash .25s ease-out; }

/* Topbar */
.topbar {
  display: flex; align-items: center; gap: 12px;
  /* env(safe-area-inset-top) is 0 unless the page opts into viewport-fit=cover (only
     predicely.html does, for the iOS standalone-shortcut status-bar fix) — harmless everywhere
     else. The topbar is the actual top-of-page element on a notched/Dynamic-Island phone once
     viewport-fit=cover draws edge-to-edge, so the safe-area clearance belongs here, not on
     whatever content sits below it (2026-07-04 playtest fix, see predicely.html's .pq-wrap). */
  padding: calc(.75rem + env(safe-area-inset-top)) 1.5rem .75rem;
  border-bottom: 0.5px solid #1e1e1e;
  background: #111;
}
.topbar-logo { font-size: 16px; font-weight: 600; flex: 1; }
.topbar-logo-text { white-space: nowrap; }
#topbar-user { font-size: 13px; color: #888; }
.signout-btn {
  font-size: 12px; padding: 5px 12px;
  background: transparent; border: 0.5px solid #2e2e2e;
  border-radius: 6px; color: #888; cursor: pointer;
  transition: border-color .15s, color .15s, transform .05s, filter .05s;
}
.signout-btn:hover { border-color: #555; color: #e8e8e8; }
.signout-btn:active { transform: scale(.96); filter: brightness(.85); }

/* Global Profile + notifications cluster — moved into the active screen's topbar on navigation */
#global-topbar-extras { display: none; gap: 8px; align-items: center; }

/* Notifications */
.notif-wrap { position: relative; }
.notif-bell { position: relative; font-size: 14px; padding: 5px 10px; }
.notif-badge {
  position: absolute; top: -5px; right: -5px;
  background: #E24B4A; color: #fff; font-size: 10px; font-weight: 600;
  border-radius: 999px; min-width: 16px; height: 16px;
  display: flex; align-items: center; justify-content: center; padding: 0 4px;
}
.notif-panel {
  position: absolute; top: calc(100% + 8px); right: 0;
  width: 280px; max-height: 360px;
  background: #181818; border: 0.5px solid #2e2e2e; border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,.4); z-index: 50;
  display: flex; flex-direction: column;
}
.notif-panel-title {
  font-size: 12px; font-weight: 600; color: #888;
  padding: 10px 12px; border-bottom: 0.5px solid #2e2e2e;
}
.notif-tabs {
  display: flex; gap: 6px; padding: 8px 12px;
  border-bottom: 0.5px solid #2e2e2e;
}
.notif-tab {
  flex: 1; padding: 5px; border-radius: 6px;
  border: 0.5px solid #2e2e2e; background: transparent;
  color: #888; cursor: pointer; font-size: 12px; transition: all .15s, transform .05s, filter .05s;
}
.notif-tab.active { background: #2e2e2e; color: #e8e8e8; }
.notif-tab:active { transform: scale(.94); filter: brightness(.85); }
#notif-list { overflow-y: auto; }
.notif-row {
  display: flex; gap: 8px; align-items: flex-start;
  padding: 10px 12px; font-size: 13px; border-bottom: 0.5px solid #1e1e1e;
}
.notif-row:last-child { border-bottom: none; }
.notif-row.unread { background: #1D9E7511; }
.notif-row[onclick]:hover { background: #ffffff0a; }
.notif-emoji { font-size: 16px; line-height: 1.4; }
.notif-body { flex: 1; line-height: 1.4; }
.notif-time { font-size: 11px; color: #666; margin-top: 2px; }
.notif-empty { padding: 16px 12px; font-size: 13px; color: #888; text-align: center; }

/* Story line */
.story-line {
  margin-top: 10px; padding-top: 10px; border-top: 0.5px solid #1e1e1e;
  font-size: 13px; color: #aaa; font-style: italic;
}
.story-line::before { content: "📜 "; }

/* Game layout */
.wrap { padding: 1.5rem; max-width: 640px; margin: 0 auto; position: relative; overflow: hidden; }

.balance-bar { display: grid; grid-template-columns: repeat(3,1fr); gap: 12px; margin-bottom: 1rem; }
.metric { background: #1a1a1a; border-radius: 10px; padding: .75rem 1rem; }
.metric-label { font-size: 12px; color: #888; margin-bottom: 4px; }
.metric-value { font-size: 20px; font-weight: 500; }
.metric-value.green { color: #1D9E75; }
.metric-value.red { color: #E24B4A; }

.card {
  background: #1a1a1a; border: 0.5px solid #2e2e2e;
  border-radius: 14px; padding: 1.25rem; margin-bottom: 1rem;
  position: relative; transition: border-color .4s, box-shadow .4s;
}

.card.streak-5  { border-color: #f97316; box-shadow: 0 0 0 1px #f9731640, 0 0 18px 2px #f9731628; }
.card.streak-10 { border-color: #3b82f6; box-shadow: 0 0 0 1px #3b82f640, 0 0 22px 4px #3b82f630; }
.card.streak-20 { border-color: #a855f7; box-shadow: 0 0 0 1px #a855f740, 0 0 28px 6px #a855f730; }

@keyframes pulse-streak { 0%,100%{opacity:1} 50%{opacity:.65} }
.card.streak-5, .card.streak-10, .card.streak-20 { animation: pulse-streak 2s ease-in-out infinite; }

/* Draws the eye to the roll/turn counter when the card is glowing */
.stat-val.glow-5  { color: #f97316; }
.stat-val.glow-10 { color: #3b82f6; }
.stat-val.glow-20 { color: #a855f7; }
.stat-val.glow-5, .stat-val.glow-10, .stat-val.glow-20 { animation: pulse-streak 2s ease-in-out infinite; }

@keyframes shake {
  0%,100%{transform:translateX(0)} 15%{transform:translateX(-6px)}
  30%{transform:translateX(5px)} 45%{transform:translateX(-4px)}
  60%{transform:translateX(3px)} 75%{transform:translateX(-2px)} 90%{transform:translateX(1px)}
}
.card.shaking { animation: shake .45s ease-out; }

@keyframes win-flash {
  0%{background:#1a1a1a} 30%{background:#1D9E7514} 100%{background:#1a1a1a}
}
.card.win-flash { animation: win-flash .6s ease-out; }

.section-label { font-size: 12px; color: #888; margin-bottom: .5rem; letter-spacing: .04em; text-transform: uppercase; }

.roll-display { text-align: center; margin: 1.25rem 0; }
.roll-number { font-size: 64px; font-weight: 500; line-height: 1; transition: color .15s; }
.roll-number.win  { color: #1D9E75; }
.roll-number.lose { color: #E24B4A; }
.roll-number.idle { color: #555; }
.roll-number.rolling { color: #555; animation: roll-pulse .3s ease-in-out infinite; }

@keyframes roll-pulse {
  0%, 100% { opacity: .5; }
  50%      { opacity: 1; }
}
.roll-result-text { font-size: 14px; margin-top: 6px; min-height: 20px; color: #888; }

.slider-row { margin-bottom: 1rem; }
.slider-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 6px; }
.slider-header span { font-size: 13px; color: #888; }
.slider-header strong { font-size: 15px; font-weight: 500; }
input[type=range] { width: 100%; accent-color: #1D9E75; }

.chance-bar { height: 6px; border-radius: 3px; background: #2e2e2e; margin-top: 6px; overflow: hidden; }
.chance-fill { height: 100%; background: #1D9E75; border-radius: 3px; transition: width .2s; }

.bet-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 1rem; align-items: end; }
/* Pig/Farkle bet rows only contain the bet input (Roll/Bank live in .pig-actions below),
   so force a single column — otherwise the 2-column grid leaves it stuck on the left half. */
#pig-bet-row, #farkle-bet-row, #bj-bet-row { grid-template-columns: 1fr; }
.bet-input-wrap input[type=number] {
  width: 100%; padding: 8px 10px; font-size: 15px;
  border: 0.5px solid #2e2e2e; border-radius: 8px;
  background: #111; color: #e8e8e8;
}

/* Keeps the bet row's height reserved during play instead of collapsing it,
   so the card doesn't resize (and shove the history below) on every deal. */
.bj-bet-row-locked { opacity: .45; pointer-events: none; }

.bet-shortcuts { display: flex; gap: 6px; margin-top: 6px; }
.bet-shortcuts button {
  flex: 1; font-size: 11px; padding: 4px 0;
  background: #111; border: 0.5px solid #2e2e2e;
  border-radius: 6px; color: #888; cursor: pointer;
  transition: border-color .15s, color .15s, transform .05s, filter .05s;
}
.bet-shortcuts button:hover { border-color: #555; color: #e8e8e8; }
.bet-shortcuts button:active { transform: scale(.94); filter: brightness(.85); }

/* Linked with .roll-btn.bet-max — same purple glow so it's clear the two are connected */
.bet-shortcuts button.bet-max-active {
  border-color: #a855f7; color: #c9a4f7;
  box-shadow: 0 0 0 1px #a855f740, 0 0 10px 2px #a855f728;
  animation: max-pulse-sm 1.2s ease-in-out infinite;
}
@keyframes max-pulse-sm {
  0%,100% { box-shadow: 0 0 0 1px #a855f740, 0 0 10px 2px #a855f728; }
  50%      { box-shadow: 0 0 0 2px #a855f780, 0 0 16px 4px #a855f750; }
}

.over-under { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 1rem; }
.ou-btn {
  padding: 10px; border-radius: 8px; border: 0.5px solid #2e2e2e;
  background: #111; cursor: pointer; text-align: center;
  font-size: 14px; color: #888; transition: all .15s, transform .05s, filter .05s;
}
.ou-btn.active { background: #1a3a5c; border-color: #2a5a8c; color: #60a5fa; font-weight: 500; }
.ou-btn:active { transform: scale(.97); filter: brightness(.85); }

.roll-btn {
  width: 100%; padding: 12px; font-size: 16px; font-weight: 500;
  background: #1a1a1a; border: 0.5px solid #2e2e2e;
  border-radius: 8px; color: #e8e8e8; cursor: pointer;
  letter-spacing: .02em; transition: background .1s, transform .05s, box-shadow .3s, font-size .25s, padding .25s, border-color .3s;
}
.roll-btn:active  { transform: scale(.98); filter: brightness(.85); }
.roll-btn:hover   { background: #222; }
.roll-btn:disabled { opacity: .4; cursor: default; }
.roll-btn.bet-med  { font-size: 17px; padding: 14px 12px; border-color: #f97316aa; box-shadow: 0 0 10px 0 #f9731622; }
.roll-btn.bet-high { font-size: 18px; padding: 16px 12px; border-color: #ef4444aa; box-shadow: 0 0 16px 0 #ef444430; }
.roll-btn.bet-max  { font-size: 19px; padding: 17px 12px; border-color: #a855f7; box-shadow: 0 0 0 2px #a855f740, 0 0 22px 4px #a855f728; }

@keyframes max-pulse {
  0%,100% { box-shadow: 0 0 0 2px #a855f740, 0 0 22px 4px #a855f728; }
  50%      { box-shadow: 0 0 0 3px #a855f780, 0 0 32px 8px #a855f750; }
}
.roll-btn.bet-max { animation: max-pulse 1.2s ease-in-out infinite; }

.stats-row { display: grid; grid-template-columns: repeat(4,1fr); gap: 8px; margin-bottom: 1rem; }
.stat-cell { text-align: center; }
.stat-val { font-size: 15px; font-weight: 500; }
.stat-lbl { font-size: 11px; color: #888; margin-top: 2px; }

/* Profile Stats-tab record tiles get the bordered "card" treatment (same shape as
   the game-over .recap-stats). Scoped to .gpane so the compact in-game HUD panels
   that also use .stats-row stay small. */
.gpane .stats-row { gap: 10px; margin-bottom: 0; }
.gpane .stat-cell { background: #242424; border: 0.5px solid #333; border-radius: 10px; padding: 12px 8px; }
.gpane .stat-val  { font-size: 22px; font-weight: 600; }
.gpane .stat-lbl  { font-size: 12px; margin-top: 3px; }

/* "The dark side" divider + the record-of-shame (negative) tiles under it. */
.stat-divider { display: flex; align-items: center; gap: 8px; margin: 1.1rem 0 .75rem; }
.stat-divider span { font-size: 11px; color: #f0997b; letter-spacing: .06em; text-transform: uppercase; white-space: nowrap; }
.stat-divider::after { content: ''; flex: 1; height: 0.5px; background: #5a2a2a; }
/* Neutral divider — for non-negative sub-sections (by table size, variant, multiplayer). */
.stat-divider.neutral span { color: #9a9a9a; }
.stat-divider.neutral::after { background: #333; }
.gpane .stat-cell.neg { background: #2a1a1a; border-color: #5a2a2a; }
.gpane .stat-cell.neg .stat-val { color: #f0997b; }
.gpane .stat-cell.neg .stat-lbl { color: #c98a7a; }
/* Positive-highlight tile — used by the "skill vs. luck" figure when you're above chance. */
.gpane .stat-cell.pos { background: #17271c; border-color: #2c5a3a; }
.gpane .stat-cell.pos .stat-val { color: #7bd69b; }
.gpane .stat-cell.pos .stat-lbl { color: #7aab8a; }

/* Tap-to-explain: a stat card with a description is tappable; the chosen card's
   explainer text drops into a fixed strip at the bottom of the card. The strip
   is always present with a reserved height, so revealing text never reflows the
   pane or pushes anything below it. */
.gpane .stat-cell[data-desc] { cursor: pointer; transition: background .12s, border-color .12s; }
.gpane .stat-cell[data-desc]:hover { border-color: #3f4a57; }
.gpane .stat-cell.desc-on { background: #26333f; border-color: #4a6a8a; }
.gpane .stat-cell.neg.desc-on { background: #3a2020; border-color: #7a3a3a; }
.gpane .stat-cell.pos.desc-on { background: #1f3a28; border-color: #3a7a50; }

/* "Recent form" strip — up to 5 W/L chips per game, newest on the left. */
.form-strip { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.form-chip {
  width: 28px; height: 28px; border-radius: 8px; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; color: #fff;
}
.form-chip.win  { background: #1D9E75; }
.form-chip.loss { background: #E24B4A; }
.form-empty { font-size: 12px; color: #6a6a6a; font-style: italic; }
.stat-explainer {
  height: 54px;                 /* FIXED (reserves ~2 lines) so revealing text can
                                   never grow the card or push siblings down */
  overflow: hidden;             /* hard guard: an over-long line clips, never reflows */
  box-sizing: border-box;
  margin-top: 12px;
  padding: 9px 11px;
  border-top: 0.5px solid #333;
  font-size: 12px; line-height: 1.4; color: #b0b0b0;
  display: flex; align-items: center;
}
.stat-explainer.is-hint { color: #6a6a6a; font-style: italic; }

/* Solo / Multiplayer segmented toggle above the "by table size" tiles. */
.seatmode-toggle { display: inline-flex; gap: 3px; margin: 0 0 10px; background: #1a1a1a; border: 0.5px solid #333; border-radius: 999px; padding: 3px; }
.seatmode-toggle button { border: 0; background: transparent; color: #9a9a9a; font-size: 12px; font-weight: 600; padding: 5px 15px; border-radius: 999px; cursor: pointer; transition: background .12s, color .12s; }
.seatmode-toggle button.on { background: #333; color: #fff; }

.history { max-height: 140px; overflow-y: auto; }
.history-row { display: flex; justify-content: space-between; align-items: center; padding: 5px 0; border-bottom: 0.5px solid #2e2e2e; font-size: 13px; }
.history-row:last-child { border-bottom: none; }
.h-roll { font-weight: 500; min-width: 36px; }
.h-win  { color: #1D9E75; font-weight: 500; }
.h-lose { color: #E24B4A; font-weight: 500; }
.h-bal  { color: #888; font-size: 12px; }
.h-verify { color: #4caf50; font-size: 11px; text-decoration: none; margin-left: 6px; }

.pf-note { font-size: 11px; color: #555; display: flex; align-items: center; gap: 6px; margin-top: .5rem; flex-wrap: wrap; }
.seed-hash { font-family: monospace; font-size: 10px; color: #555; word-break: break-all; }

.daily-bar-wrap { margin-bottom: 1rem; }
.daily-label-row { display: flex; justify-content: space-between; margin-bottom: 4px; font-size: 12px; color: #888; }
.daily-bar { height: 4px; background: #2e2e2e; border-radius: 2px; overflow: hidden; }
.daily-fill { height: 100%; background: #378ADD; border-radius: 2px; transition: width .3s; }

.effects-toggle { display: flex; align-items: center; gap: 6px; font-size: 12px; color: #888; margin-bottom: 1rem; user-select: none; cursor: pointer; }
.effects-toggle input { cursor: pointer; }

.face-wrap { position: relative; display: inline-block; font-size: 18px; line-height: 1; vertical-align: middle; margin-left: 4px; }
.face-temp { position: absolute; top: 0; left: 0; opacity: 0; transition: opacity .2s; pointer-events: none; }
.face-temp.show { opacity: 1; }
/* Hide the base emoji while a temp reaction is displaying */
.face-wrap:has(.face-temp.show) > :not(.face-temp) { opacity: 0; transition: opacity .2s; }

/* Reactive emoji "pal" for Pig/Farkle — always-present base face with a
   temporary overlay reaction for bust/bank/etc, same overlay trick as Dice's
   face-wrap/face-temp but sized by an always-non-empty base emoji. */
.reaction-face { text-align: center; margin: .25rem 0 .5rem; }
.reaction-face .face-wrap { font-size: 28px; }

.bust-msg { text-align: center; padding: .75rem; font-size: 14px; color: #E24B4A; }
.coin-particle { position: fixed; font-size: 20px; pointer-events: none; z-index: 9999; will-change: transform, opacity; }

.streak-badge { font-size: 11px; font-weight: 500; padding: 2px 7px; border-radius: 10px; margin-left: 6px; vertical-align: middle; }
.streak-badge.s5  { background: #f9731622; color: #f97316; }
.streak-badge.s10 { background: #3b82f622; color: #3b82f6; }
.streak-badge.s20 { background: #a855f722; color: #a855f7; }

.title-badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; font-weight: 500; padding: 4px 10px; border-radius: 12px;
  background: #1D9E7522; color: #1D9E75; margin: 3px;
  border: 1px solid transparent;
}
.title-badge.selectable { cursor: pointer; opacity: .55; }
.title-badge.selectable.selected { opacity: 1; border-color: #1D9E75; }

/* Leaderboard */
.lb-row { display: flex; align-items: center; gap: 10px; padding: 7px 0; border-bottom: 0.5px solid #2e2e2e; font-size: 13px; }
.lb-row:last-child { border-bottom: none; }
.lb-rank { font-size: 15px; min-width: 28px; }
.lb-name { flex: 1; font-weight: 500; }
.lb-stat { color: #888; font-size: 12px; }
.lb-bal  { font-weight: 500; color: #1D9E75; }

/* Friends */
.friend-row { display: flex; align-items: center; gap: 8px; padding: 7px 0; border-bottom: 0.5px solid #2e2e2e; font-size: 13px; }
.friend-row:last-child { border-bottom: none; }
.friend-name { flex: 1; font-weight: 500; }
.friend-empty { font-size: 13px; color: #888; }
.friend-btn {
  font-size: 12px; padding: 5px 10px;
  background: transparent; border: 0.5px solid #2e2e2e;
  border-radius: 6px; color: #888; cursor: pointer;
  transition: border-color .15s, color .15s, transform .05s, filter .05s;
}
.friend-btn:hover { border-color: #555; color: #e8e8e8; }
.friend-btn:active { transform: scale(.94); filter: brightness(.85); }
.friend-btn.accept { color: #1D9E75; border-color: #1D9E7555; }
.friend-btn.accept:hover { border-color: #1D9E75; }
.friend-btn.decline:hover { color: #E24B4A; border-color: #E24B4A; }

.pref-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 13px; cursor: pointer; user-select: none; }
.pref-row input { cursor: pointer; }

.guest-btn {
  width: 100%; padding: 11px; background: transparent;
  border: 0.5px solid #2e2e2e; border-radius: 8px;
  color: #888; font-size: 14px; cursor: pointer;
  margin-top: 8px; transition: border-color .15s, color .15s, transform .05s, filter .05s;
}
.guest-btn:hover { border-color: #555; color: #e8e8e8; }
.guest-btn:active { transform: scale(.98); filter: brightness(.85); }

.guest-banner {
  background: #1a1a0a; border-bottom: 0.5px solid #3a3a1a;
  padding: .6rem 1.5rem; font-size: 13px; color: #aaa; text-align: center;
}
.guest-banner a { color: #f97316; cursor: pointer; text-decoration: underline; }
/* ─── Session commentary ─────────────────────────────────────────── */
.commentary {
  font-size: 12px;
  color: #888;
  text-align: center;
  min-height: 18px;
  margin-top: 6px;
  letter-spacing: 0.01em;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.commentary.commentary-in {
  opacity: 1;
  animation: commentary-fade 0.35s ease forwards;
}

@keyframes commentary-fade {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ─── Insurance toast ────────────────────────────────────────────── */
.insurance-toast {
  position: fixed; top: 16px; left: 50%; transform: translateX(-50%) translateY(-20px);
  background: #1D9E75; color: #fff; font-size: 14px; font-weight: 600;
  padding: .85rem 1.5rem; border-radius: 12px; box-shadow: 0 6px 24px rgba(0,0,0,.35);
  z-index: 10001; opacity: 0; transition: opacity .25s ease, transform .25s ease;
  text-align: center; max-width: 90vw;
}
.insurance-toast.insurance-toast-in {
  opacity: 1; transform: translateX(-50%) translateY(0);
}
/* Daily recap modal */
.recap-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,.65);
  display: flex; align-items: center; justify-content: center;
  z-index: 10000; padding: 1rem;
}
.recap-card {
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 16px;
  padding: 1.75rem; width: 100%; max-width: 380px; text-align: center;
  animation: recap-in .25s ease;
  max-height: calc(100vh - 2rem); overflow-y: auto;
}
.recap-emoji { font-size: 48px; margin-bottom: .25rem; }
.recap-card h2 { font-size: 18px; margin-bottom: 1rem; }
.recap-stats {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  margin-bottom: 1rem; font-size: 14px;
}
.recap-stats .stat-cell { background: #222; border-radius: 10px; padding: 10px; }
.recap-stats .stat-val { font-size: 18px; font-weight: 600; }
.recap-stats .stat-lbl { font-size: 11px; color: #888; margin-top: 2px; }

/* Multiplayer reaction button — minimal icon, centered under Roll/Bank */
.mp-reaction-btn {
  width: 46px; height: 46px; padding: 0; font-size: 20px;
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 8px;
  color: #888; cursor: pointer;
  transition: border-color .15s, color .15s, transform .05s, filter .05s;
}
.mp-reaction-btn:hover { background: #222; border-color: #555; color: #e8e8e8; }
.mp-reaction-btn:active { transform: scale(.96); filter: brightness(.85); }
.mp-reaction-btn.reaction-armed { border-color: #1D9E75; color: #1D9E75; }
.reaction-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: .5rem; }
.reaction-grid button {
  padding: 10px 6px; background: #111; border: 0.5px solid #2e2e2e; border-radius: 10px;
  color: #e8e8e8; font-size: 13px; cursor: pointer;
  transition: border-color .15s, transform .05s, filter .05s;
}
.reaction-grid button:hover { border-color: #555; }
.reaction-grid button:active { transform: scale(.96); filter: brightness(.85); }
.mp-reaction-row { border-bottom: none; padding-bottom: 0; justify-content: flex-start; }
.mp-reaction-row .h-bal { color: #aaa; font-style: italic; }

.mp-forfeit-link { cursor: pointer; font-size: 12px; color: #666; transition: color .15s; }
.mp-forfeit-link:hover { color: #E24B4A; }
.mp-forfeit-corner { text-align: right; margin-top: 6px; }

/* Timeout bar — shown during lobby matches when a player is idle */
.mp-timeout-bar { text-align: center; padding: 8px 12px; border-radius: 8px; margin: 8px 0 0; font-size: 13px; }
.mp-timeout-warn { background: rgba(255,100,0,.12); color: #ff8c42; border: 1px solid rgba(255,100,0,.25); }
.mp-timeout-opp  { color: #777; }
/* FLOATING variant (backgammon): the bar appears and disappears on a timer, and on a
   board that is sized from whatever the page leaves over, that made the whole board
   resize mid-game (playtest, 2026-07-26). Out of flow it costs nothing and nothing
   refits — it just lies over the top of the opponent's card for the few seconds it is
   up. Opaque, because it is now sitting ON something. */
.mp-timeout-float { position: absolute; top: 0; left: 0; right: 0; z-index: 40; margin: 0; }
.mp-timeout-float.mp-timeout-warn { background: #2a1508; border-color: rgba(255,140,66,.5); }
.mp-timeout-float.mp-timeout-opp  { background: rgba(18,18,22,.92); border: 1px solid rgba(255,255,255,.09); color: #9a9aa2; }
#backgammon-mp-game-screen .wrap { position: relative; }
/* Liar's Dice floats its idle bar too — anchored to the shell, which is the positioned
   ancestor here (the bar lives inside .ldt-shell, not beside it). */
.ldt-shell { position: relative; }

/* Pig MP dice trail — accumulates die faces inside each player's seat */
.pig-die-trail { display: flex; gap: 4px; flex-wrap: wrap; justify-content: center; padding-top: 8px; min-height: 36px; }
.pig-die-trail .trail-die { font-size: 20px; display: inline-block; transition: color .12s ease, transform .12s ease; }

.recap-narrative { font-size: 13px; color: #aaa; margin-bottom: 1.25rem; line-height: 1.4; }

@keyframes recap-in {
  from { opacity: 0; transform: scale(.96); }
  to   { opacity: 1; transform: scale(1); }
}

/* Multiplayer table shell — positional seats. In 2-player games "you" are
   always .mp-seat-bottom and the opponent is .mp-seat-top; .mp-seat-left/
   .mp-seat-right are reserved for future 3-4 player games. */
.mp-table { display: flex; flex-direction: column; gap: 10px; margin: 1.25rem 0; }
.mp-seat {
  position: relative; display: flex; flex-direction: column; align-items: stretch;
  padding: .85rem; border-radius: 12px;
  background: #1a1a1a; border: 0.5px solid #2e2e2e;
}
.mp-seat-main { display: flex; align-items: center; justify-content: center; gap: 14px; }
.mp-seat-name { font-size: 12px; color: #888; }
.mp-seat .reaction-face { margin: 0; }
.mp-seat .reaction-face .face-wrap { font-size: 32px; }
.mp-seat .farkle-die { font-size: 36px; }
.mp-seat-score { font-size: 22px; font-weight: 700; min-width: 3ch; text-align: center; }
.mp-table-center { text-align: center; font-size: 13px; color: #888; padding: 2px 0; }
.mp-seat-left, .mp-seat-right { display: none; } /* reserved for future 3-4p games */

@keyframes mp-bust-flash {
  0%, 100% { background: #1a1a1a; border-color: #2e2e2e; }
  30% { background: #E24B4A33; border-color: #E24B4A; }
}
.mp-seat.bust-flash { animation: mp-bust-flash .6s ease-out; }

@keyframes mp-bank-flash {
  0%, 100% { background: #1a1a1a; border-color: #2e2e2e; }
  30% { background: #1D9E7533; border-color: #1D9E75; }
}
.mp-seat.bank-flash { animation: mp-bank-flash .6s ease-out; }

@keyframes yz-glow-good {
  0%, 100% { box-shadow: none; border-color: #2e2e2e; }
  15%, 65%  { box-shadow: 0 0 18px 5px #1D9E75; border-color: #1D9E75; }
}
@keyframes yz-glow-great {
  0%, 100% { box-shadow: none; border-color: #2e2e2e; }
  15%, 65%  { box-shadow: 0 0 26px 7px #f0a500; border-color: #f0a500; }
}
@keyframes yz-glow-bad {
  0%, 100% { box-shadow: none; border-color: #2e2e2e; }
  15%, 65%  { box-shadow: 0 0 18px 5px #E24B4A; border-color: #E24B4A; }
}
.mp-seat.yz-glow-good  { animation: yz-glow-good  1.8s ease-out; }
.mp-seat.yz-glow-great { animation: yz-glow-great 2.2s ease-out; }
.mp-seat.yz-glow-bad   { animation: yz-glow-bad   1.8s ease-out; }

/* Speech bubbles — float near a seat, with a small triangle pointing toward
   that player's position. */
.speech-bubble {
  position: absolute; left: 50%; transform: translateX(-50%);
  /* Bigger on purpose (owner, 2026-07-25): a bubble is on screen for a few seconds and it's
     fine if it covers something — being readable at a glance matters more. */
  background: #2e2e2e; color: #e8e8e8; font-size: 18px; padding: 10px 16px;
  border-radius: 10px; white-space: nowrap; z-index: 5; pointer-events: none;
  animation: speech-pop .2s ease;
}
/* Opt-in tap-to-dismiss. NOT the default: every other game's bubbles are pointer-transparent
   on purpose, and turning that off globally would let a bubble eat a tap meant for the seat
   card under it. Yahtzee MP opts in; the rest can follow once each is checked. */
.speech-bubble.dismissable { pointer-events: auto; cursor: pointer; }
.speech-bubble::after {
  content: ''; position: absolute; left: 50%; margin-left: -6px;
  width: 0; height: 0; border: 6px solid transparent;
}
/* Opponent's reaction — bubble sits below their (top) seat, arrow points up at them */
.speech-bubble.from-top { top: calc(100% + 10px); }
.speech-bubble.from-top::after { top: -12px; border-bottom-color: #2e2e2e; }
/* Your reaction — bubble sits above your (bottom) seat, arrow points down at you */
.speech-bubble.from-bottom { bottom: calc(100% + 10px); }
.speech-bubble.from-bottom::after { bottom: -12px; border-top-color: #2e2e2e; }

@keyframes speech-pop {
  from { opacity: 0; transform: translateX(-50%) scale(.85); }
  to   { opacity: 1; transform: translateX(-50%) scale(1); }
}

/* Games Hub */
.games-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.game-card {
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 14px;
  padding: 1.25rem; text-align: center; cursor: pointer;
  transition: border-color .15s, transform .05s;
}
.game-card:hover  { border-color: #555; }
.game-card:active { transform: scale(.98); }
.game-card.disabled { cursor: default; opacity: .5; }
.game-card-emoji  { font-size: 36px; margin-bottom: 8px; }
.game-card-title  { font-size: 16px; font-weight: 600; margin-bottom: 8px; }
.game-card-badges { display: flex; flex-direction: column; gap: 4px; align-items: center; }
.game-badge {
  display: inline-block; font-size: 11px; padding: 3px 10px; border-radius: 10px;
  background: #1D9E7522; color: #1D9E75;
}
.game-badge.soon { background: #2e2e2e; color: #888; }
.game-badge-mp {
  background: #1a3a5c; color: #60a5fa; cursor: pointer;
  transition: background .15s;
}
.game-badge-mp:hover { background: #1e4a70; }

/* ── Games Hub V5 (mock GameHub_Variation5) — featured carousel + card grid ────
   Rendered by renderHub() in hub.js from window.GAMES. Fully token-driven via the
   shared --pgr-* board-scheme contract, so all 12 schemes recolour it for free;
   applyHubTheme() paints the equipped scheme's vars inline on #games-hub-screen.
   Charcoal defaults below = today's look for anyone who never picks a scheme. */
/* Token defaults live on the hub SCREEN (the element applyHubTheme() paints inline),
   so the balance bar + cards + featured all inherit the equipped scheme from one place.
   Charcoal defaults below = today's look before any scheme loads. .hub-root must NOT
   redeclare these or it would shadow the scheme for the cards inside it. */
#games-hub-screen {
  --pgr-bg: radial-gradient(125% 120% at 50% -10%,#1c1e24 0%,#141519 58%,#0d0e11 100%);
  --pgr-seat: rgba(255,255,255,.025); --pgr-panel: rgba(255,255,255,.04);
  --pgr-line: rgba(255,255,255,.075); --pgr-txt: #e9e8e4; --pgr-dim: #83858b;
  --pgr-sel: oklch(.74 .14 152); --pgr-btn: linear-gradient(180deg,#333841,#22252b);
  --pgr-btnTxt: #e9e8e4; --pgr-selBg: oklch(.74 .14 152 / .16);
}
.hub-root { display: flex; flex-direction: column; gap: 16px; color: var(--pgr-txt); }
/* Hub balance bar follows the equipped board scheme (was hardcoded #1a1a1a, which
   stayed near-black on light schemes). Scoped to the hub so other screens are unaffected;
   tokens are set inline on #games-hub-screen by applyHubTheme(). */
#games-hub-screen .metric {
  background: var(--pgr-panel, #1a1a1a);
  border: 1px solid var(--pgr-line, transparent);
}
#games-hub-screen .metric-label { color: var(--pgr-dim, #888); }
#games-hub-screen .metric-value { color: var(--pgr-txt, inherit); }

.hub-feat-wrap { position: relative; }
.hub-featured {
  display: flex; overflow: hidden; border-radius: 16px; height: 210px;
  background: var(--pgr-panel); border: 1px solid var(--pgr-line);
  user-select: none; touch-action: pan-y;
}
.hub-feat-art {
  width: 128px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
  font-size: 56px; border-right: 1px solid var(--pgr-line);
  background: linear-gradient(160deg, color-mix(in oklch, var(--pgr-sel) 28%, var(--pgr-panel)), var(--pgr-panel));
}
.hub-feat-col { flex: 1; padding: 16px 18px; display: flex; flex-direction: column; justify-content: center; }
.hub-feat-eyebrow { font-size: 11px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--pgr-sel); }
.hub-feat-name { font-size: 23px; font-weight: 800; margin: 5px 0 6px; line-height: 1.1; }
.hub-feat-desc {
  font-size: 13px; color: var(--pgr-dim); line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.hub-tags { display: flex; flex-wrap: nowrap; gap: 6px; margin: 10px 0 14px; overflow: hidden; }
.hub-tag {
  font-size: 11px; font-weight: 600; color: var(--pgr-dim); white-space: nowrap;
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 999px; padding: 4px 10px;
}
.hub-feat-btns { display: flex; gap: 8px; flex-wrap: nowrap; }
.hub-btn-icon { padding: 8px 11px; flex-shrink: 0; }
.hub-btn {
  border-radius: 12px; padding: 9px 14px; font-family: inherit; font-weight: 700; font-size: 13px;
  cursor: pointer; transition: filter .15s, transform .05s, border-color .15s, background .15s;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; border: none;
}
.hub-btn:active { transform: translateY(1px) scale(.98); }
.hub-btn-primary { background: var(--pgr-btn); color: var(--pgr-btnTxt); }
.hub-btn-primary:hover { filter: brightness(1.1); }
.hub-btn-sec { background: var(--pgr-seat); color: var(--pgr-txt); border: 1px solid var(--pgr-line); font-weight: 600; }
.hub-btn-sec:hover { border-color: var(--pgr-sel); }
.hub-btn-ghost { background: transparent; color: var(--pgr-dim); border: 1px solid var(--pgr-line); font-weight: 600; font-size: 12px; }
.hub-btn-ghost:hover { color: var(--pgr-txt); border-color: var(--pgr-sel); }
.hub-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--pgr-line);
  background: var(--pgr-panel); color: var(--pgr-txt); font-size: 21px; line-height: 1;
  font-family: inherit; cursor: pointer; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 14px rgba(0,0,0,.3); transition: border-color .15s, color .15s, transform .15s;
}
.hub-arrow:hover { border-color: var(--pgr-sel); color: var(--pgr-sel); }
.hub-arrow-l { left: -10px; } .hub-arrow-r { right: -10px; }
.hub-dots { display: flex; justify-content: center; gap: 7px; }
.hub-dot { width: 8px; height: 8px; border-radius: 999px; cursor: pointer; transition: width .2s, background .2s; background: var(--pgr-line); }
.hub-dot-active { width: 22px; background: var(--pgr-sel); }
.hub-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.hub-card {
  background: var(--pgr-panel); border: 1px solid var(--pgr-line); border-radius: 14px;
  padding: 13px 12px; display: flex; flex-direction: column; align-items: center; gap: 6px;
  cursor: pointer; text-align: center; transition: border-color .15s, transform .1s, box-shadow .15s;
}
.hub-card:hover { border-color: var(--pgr-sel); transform: translateY(-2px); box-shadow: 0 8px 22px rgba(0,0,0,.3); }
.hub-card:active { transform: scale(.98); }
.hub-card-icon {
  font-size: 27px; width: 50px; height: 50px; display: flex; align-items: center; justify-content: center;
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 14px;
}
.hub-card-name { font-weight: 700; font-size: 14px; }
.hub-card-meta { font-size: 11px; color: var(--pgr-dim); }

/* Quickplay popup */
.qp-overlay {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  padding: 20px; background: rgba(0,0,0,.55); backdrop-filter: blur(2px);
}
.qp-panel {
  position: relative; overflow: hidden;
  width: 100%; max-width: 360px; display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 26px 22px 20px; text-align: center;
  border-radius: 20px; border: 1px solid var(--pgr-line, #2e2e2e);
  /* Accent glow on top, the equipped scheme's own background beneath. NOTE: --pgr-bg
     is itself a gradient, so it must be its OWN background layer — nesting it as a
     color stop inside another gradient invalidates the whole declaration. */
  background:
    radial-gradient(120% 80% at 50% -20%, color-mix(in oklch, var(--pgr-sel) 20%, transparent) 0%, transparent 60%),
    var(--pgr-bg, #16171b);
  color: var(--pgr-txt, #e9e8e4); box-shadow: 0 24px 60px rgba(0,0,0,.55);
  animation: qpPop .22s cubic-bezier(.2,.9,.3,1.3);
}
/* accent hairline across the top edge */
.qp-panel::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, transparent, var(--pgr-sel), transparent); opacity: .9;
}
@keyframes qpPop { from { opacity: 0; transform: translateY(10px) scale(.96); } to { opacity: 1; transform: none; } }
.qp-art {
  width: 76px; height: 76px; border-radius: 20px; display: flex; align-items: center; justify-content: center;
  font-size: 42px; margin-bottom: 2px; border: 1px solid var(--pgr-line);
  background: linear-gradient(160deg, color-mix(in oklch, var(--pgr-sel) 38%, var(--pgr-panel)), var(--pgr-panel));
  box-shadow: 0 8px 22px color-mix(in oklch, var(--pgr-sel) 28%, transparent);
}
.qp-title { font-weight: 800; font-size: 20px; letter-spacing: .2px; }
.qp-close {
  position: absolute; top: 12px; right: 12px; z-index: 1;
  width: 32px; height: 32px; border-radius: 10px; border: 1px solid var(--pgr-line); background: var(--pgr-seat);
  color: var(--pgr-dim); font-size: 14px; cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.qp-close:hover { border-color: var(--pgr-sel); color: var(--pgr-sel); }
.qp-meta { font-size: 12px; color: var(--pgr-dim); }
.qp-tags { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin: 4px 0 6px; }
.qp-tag {
  font-size: 10.5px; font-weight: 600; color: var(--pgr-dim);
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 999px; padding: 3px 9px;
}
.qp-btns { display: flex; gap: 10px; width: 100%; margin-top: 4px; }
.qp-play { flex: 1; padding: 14px; font-size: 15px; }
.qp-details {
  background: transparent; border: none; color: var(--pgr-dim); font-family: inherit; font-weight: 600;
  font-size: 13px; cursor: pointer; padding: 4px; align-self: center;
}
.qp-details:hover { color: var(--pgr-txt); }

@media (max-width: 420px) {
  .hub-grid { grid-template-columns: repeat(2, 1fr); }
  .hub-feat-art { width: 96px; font-size: 44px; }
  .hub-feat-name { font-size: 20px; }
}

/* ── Per-game Landing (mock "Game Landing Page" V1→V2) ─────────────────────────
   Modular floating-widget cards. All colours come from the shared --pgr-* board-
   scheme contract (charcoal defaults for pre-scheme paint), so every scheme
   recolours the landing for free — same idiom as the hub above. */
#game-landing-screen {
  --pgr-bg: radial-gradient(125% 120% at 50% -10%,#1c1e24 0%,#141519 58%,#0d0e11 100%);
  --pgr-seat: rgba(255,255,255,.025); --pgr-panel: rgba(255,255,255,.04);
  --pgr-line: rgba(255,255,255,.075); --pgr-txt: #e9e8e4; --pgr-dim: #83858b;
  --pgr-sel: oklch(.74 .14 152); --pgr-btn: linear-gradient(180deg,#333841,#22252b);
  --pgr-btnTxt: #e9e8e4; --pgr-selBg: oklch(.74 .14 152 / .16);
}
.lp-topline { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
.lp-back { padding: 8px 16px; }
.lp-balance {
  display: inline-flex; align-items: center; gap: 8px; font-weight: 700; font-size: 14px;
  color: var(--pgr-txt); background: var(--pgr-panel); border: 1px solid var(--pgr-line);
  border-radius: 999px; padding: 8px 15px;
}
.lp-root { color: var(--pgr-txt); }

/* Grid: 1-col stacked (mock V1) → 2-col at ≥760px (mock V2). Full-width widgets
   span both columns; MP + Solo sit side-by-side. */
.lp-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 760px) {
  .lp-grid { grid-template-columns: 1fr 1fr; align-items: start; }
  .lp-span { grid-column: 1 / -1; }
}

.lp-card {
  background: var(--pgr-panel); border: 1px solid var(--pgr-line); border-radius: 18px;
  padding: 16px 18px; display: flex; flex-direction: column; gap: 12px;
}

/* Hero */
.lp-hero { flex-direction: row; padding: 0; overflow: hidden; gap: 0; }
.lp-hero-art {
  width: 140px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
  font-size: 64px; border-right: 1px solid var(--pgr-line);
  background: linear-gradient(160deg, color-mix(in oklch, var(--pgr-sel) 32%, var(--pgr-panel)), var(--pgr-panel));
}
.lp-hero-body { flex: 1; padding: 20px 22px; display: flex; flex-direction: column; justify-content: center; gap: 4px; }
.lp-eyebrow { font-size: 11px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--pgr-sel); }
.lp-name { font-size: 30px; font-weight: 800; margin: 2px 0 4px; line-height: 1; }
.lp-hero-desc { font-size: 14px; color: var(--pgr-dim); line-height: 1.45; margin: 0 0 6px; max-width: 46ch; }
.lp-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.lp-tag {
  font-size: 11px; font-weight: 600; color: var(--pgr-dim);
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 999px; padding: 4px 10px;
}
.lp-howto { align-self: flex-start; }
.lp-hero-actions { display: flex; flex-wrap: wrap; gap: 8px; align-self: flex-start; }
@media (max-width: 520px) {
  .lp-hero { flex-direction: column; }
  .lp-hero-art { width: 100%; height: 110px; border-right: none; border-bottom: 1px solid var(--pgr-line); }
}

/* Card head + shared bits */
.lp-card-head { display: flex; align-items: center; justify-content: space-between; font-weight: 800; font-size: 15px; color: var(--pgr-txt); }
.lp-subhead { margin-top: 2px; }
.lp-head-muted { font-size: 12px; font-weight: 600; color: var(--pgr-dim); }
.lp-muted-row { font-size: 13px; color: var(--pgr-dim); line-height: 1.4; }
.lp-wide { width: 100%; padding: 13px; font-size: 15px; }
.lp-btn-row { display: flex; gap: 9px; }
.lp-flex { flex: 1; }

/* Solo options (registry-driven chip rows — Backgammon difficulty is the first user) */
.lp-opt { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.lp-opt-label { font-size: 12px; font-weight: 700; color: var(--pgr-dim); }
.lp-opt-chips { display: flex; gap: 6px; }
.lp-opt-chip {
  padding: 6px 12px; border-radius: 9px; border: 1px solid var(--pgr-line);
  background: var(--pgr-panel); color: var(--pgr-dim); font-family: inherit;
  font-size: 12px; font-weight: 700; cursor: pointer; transition: filter .12s;
}
.lp-opt-chip:hover { filter: brightness(1.25); }
/* --pgr-sel, not --pgr-accent: accent is invisible as a text colour in Classic. */
.lp-opt-on { color: var(--pgr-sel); border-color: var(--pgr-sel); }

/* Daily */
.lp-daily-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.lp-daily-label { font-size: 12px; font-weight: 600; color: var(--pgr-dim); }
.lp-daily-cat { font-size: 20px; font-weight: 800; margin: 2px 0; }
.lp-daily-sub { font-size: 12px; color: var(--pgr-dim); }
.lp-daily-actions { display: flex; flex-direction: column; gap: 8px; }

/* Stats */
.lp-stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.lp-stat {
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 12px;
  padding: 13px 8px; display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.lp-stat-val { font-size: 22px; font-weight: 800; color: var(--pgr-txt); line-height: 1; }
.lp-stat-lbl { font-size: 10px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--pgr-dim); text-align: center; }

/* Multiplayer live tag + inline tables */
.lp-live { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--pgr-dim); }
.lp-live-dot { width: 8px; height: 8px; border-radius: 50%; background: #3fd07a; box-shadow: 0 0 6px #3fd07a; }
.lp-tables { display: flex; flex-direction: column; gap: 8px; }
.lp-table-row {
  display: flex; align-items: center; gap: 10px;
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 12px; padding: 9px 11px;
}
.lp-table-info { flex: 1; }
.lp-table-host { font-weight: 700; font-size: 13px; }
.lp-table-sub { font-size: 12px; color: var(--pgr-dim); margin-top: 1px; }
.lp-join { padding: 7px 14px; font-size: 12px; }
.lp-table-row--locked { opacity: .62; }
.lp-lock { flex: 0 0 auto; font-size: 12px; font-weight: 600; color: var(--pgr-dim); white-space: nowrap; padding: 7px 10px; }

/* Leaderboard (stub) */
.lp-lb-list { display: flex; flex-direction: column; gap: 6px; }
.lp-lb-row {
  display: flex; align-items: center; gap: 10px;
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 10px; padding: 8px 11px;
}
.lp-lb-rank { font-weight: 800; font-size: 13px; color: var(--pgr-sel); width: 18px; }
.lp-lb-name { flex: 1; font-size: 13px; font-weight: 600; }
.lp-lb-score { font-size: 13px; color: var(--pgr-dim); font-weight: 700; }
.lp-lb-note { text-align: center; }
/* Current user's own row + inline "you" chip */
.lp-lb-row.lp-lb-me { border-color: var(--pgr-sel); background: var(--pgr-selBg); }
.lp-lb-row.lp-lb-me .lp-lb-score { color: var(--pgr-txt); }
.lp-lb-you { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--pgr-sel); margin-left: 6px; }
/* "History →" is a real link to the profile Stats tab */
.lp-head-link { cursor: pointer; }
.lp-head-link:hover { color: var(--pgr-txt); }

/* ── Lobby (public matchmaking) — board-scheme token-driven, same idiom as the hub +
   landing. applyLobbyTheme() (lobby.js) paints the equipped scheme's --pgr-* inline on
   #lobby-screen, so all 12 schemes recolour it for free. Charcoal defaults below = today's
   look before any scheme loads; they must live on the SCREEN (the painted element), not an
   inner wrapper, or the inner block would shadow the scheme (see board-schemes memory). */
#lobby-screen {
  --pgr-bg: radial-gradient(125% 120% at 50% -10%,#1c1e24 0%,#141519 58%,#0d0e11 100%);
  --pgr-seat: rgba(255,255,255,.025); --pgr-panel: rgba(255,255,255,.04);
  --pgr-line: rgba(255,255,255,.075); --pgr-txt: #e9e8e4; --pgr-dim: #83858b;
  --pgr-sel: oklch(.74 .14 152); --pgr-btn: linear-gradient(180deg,#333841,#22252b);
  --pgr-btnTxt: #e9e8e4; --pgr-selBg: oklch(.74 .14 152 / .16);
}
#lobby-screen .section-label { color: var(--pgr-dim); }
/* Native <select> popups can't render a translucent background — a scheme's --pgr-seat is
   translucent (e.g. Candy rgba(255,255,255,.06)), so the browser falls back to WHITE and the
   light option text vanishes (white-on-white, on every scheme). Fix: --pgr-solid, an OPAQUE
   surface derived per-scheme from the bg gradient's darkest stop (BoardSchemes.vars), so the
   dropdown matches the scheme (blue on Candy, purple on Royal…) and --pgr-txt stays readable. */
#lobby-screen select {
  background-color: var(--pgr-solid, #20242c); color: var(--pgr-txt);
  border: 1px solid var(--pgr-line); border-radius: 8px; padding: 3px 6px; font-family: inherit;
}
#lobby-screen select option { background-color: var(--pgr-solid, #20242c); color: var(--pgr-txt); }
/* Per-game option rows: tokenized colour (layout stays inline in index.html) */
.lobby-opt-label { font-size: 13px; color: var(--pgr-dim); }
.lobby-opt-note  { font-size: 12px; color: var(--pgr-dim); opacity: .8; align-self: center; }

/* ── Create panel — collapsible; all create clutter is hidden until the user opts in.
   Collapsed by default so the lobby opens focused on the join list. */
.lobby-create-panel {
  background: var(--pgr-panel); border: 1px solid var(--pgr-line); border-radius: 16px;
  margin-bottom: 4px; overflow: hidden;
}
.lobby-create-head {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: transparent; border: 0; cursor: pointer; padding: 14px 16px; font-family: inherit;
  text-align: left; color: var(--pgr-txt);
}
.lobby-create-head-txt { display: flex; flex-direction: column; gap: 2px; }
.lobby-create-title { font-size: 16px; font-weight: 800; }
.lobby-create-sub { font-size: 12px; color: var(--pgr-dim); }
.lobby-create-chevron {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%;
  border: 1px solid var(--pgr-line); display: flex; align-items: center; justify-content: center;
  font-size: 15px; color: var(--pgr-dim); transition: transform .2s, color .15s, border-color .15s;
}
.lobby-create-panel:not(.collapsed) .lobby-create-chevron { transform: rotate(180deg); color: var(--pgr-txt); border-color: var(--pgr-sel); }
.lobby-create-body { display: flex; flex-direction: column; gap: 12px; padding: 0 16px 16px; }
.lobby-create-panel.collapsed { cursor: pointer; }  /* whole tab expands on click */
.lobby-create-panel.collapsed .lobby-create-body { display: none; }
/* "•••" affordance — only when collapsed — implies the panel can grow. */
.lobby-create-ellipsis { display: none; text-align: center; color: var(--pgr-dim); font-size: 18px; letter-spacing: 3px; padding-bottom: 10px; margin-top: -4px; }
.lobby-create-panel.collapsed .lobby-create-ellipsis { display: block; }

.lobby-field-label { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--pgr-dim); }

/* Every create-panel group is a labelled section: uppercase label above a bordered box,
   so each choice (Game / Visibility / Players / Variant) reads as its own bounded block. */
.lobby-section { display: flex; flex-direction: column; gap: 8px; }
.lobby-box {
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 12px; padding: 10px 12px;
}

.lobby-game-select {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 12px; padding: 10px 12px;
}
.lobby-game-current { display: flex; align-items: center; gap: 10px; min-width: 0; }
.lobby-game-icon { font-size: 26px; flex: 0 0 auto; }
.lobby-game-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.lobby-game-name { font-size: 15px; font-weight: 700; color: var(--pgr-txt); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Base-game format line under the name (e.g. "Bank 100+ to open the final round …"). */
.lobby-game-rule { font-size: 12px; font-weight: 400; color: var(--pgr-dim); line-height: 1.3; }
.lobby-game-rule:empty { display: none; }

/* Registry-driven create options (rendered by lobbyRenderCreateOptions) */
#lobby-create-options:empty { display: none; }
#lobby-create-options { display: flex; flex-direction: column; gap: 14px; }
.lobby-opt-fields { display: flex; gap: 18px; flex-wrap: wrap; align-items: center; }
.lobby-variants { display: flex; flex-direction: column; gap: 12px; }

/* Roll Bonus (and future per-game) toggle switch — themed, replaces the bare checkbox */
.lobby-bonus-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.lobby-bonus-title { font-weight: 600; font-size: 14px; color: var(--pgr-txt); }
.lobby-toggle { position: relative; display: inline-block; flex: 0 0 auto; cursor: pointer; }
.lobby-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.lobby-toggle-track {
  display: block; width: 46px; height: 26px; border-radius: 999px;
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); transition: background .18s, border-color .18s;
}
.lobby-toggle-knob {
  position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--pgr-dim); transition: transform .18s, background .18s;
}
.lobby-toggle input:checked + .lobby-toggle-track { background: var(--pgr-selBg); border-color: var(--pgr-sel); }
.lobby-toggle input:checked + .lobby-toggle-track .lobby-toggle-knob { transform: translateX(20px); background: var(--pgr-sel); }

/* Visibility segmented control (Public / Private) — universal create option */
.lobby-vis { display: flex; flex-direction: column; gap: 8px; }
.lobby-vis-seg {
  display: flex; gap: 4px; padding: 4px;
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 12px;
}
.lobby-vis-opt {
  flex: 1; text-align: center; cursor: pointer; font-family: inherit;
  background: transparent; border: 1px solid transparent; border-radius: 9px;
  color: var(--pgr-dim); font-size: 14px; font-weight: 700; padding: 9px 8px;
  transition: background .15s, color .15s, border-color .15s;
}
.lobby-vis-opt:hover { color: var(--pgr-txt); }
.lobby-vis-opt.active { background: var(--pgr-selBg); border-color: var(--pgr-sel); color: var(--pgr-sel); }

/* OR JOIN divider */
.lobby-divider { display: flex; align-items: center; gap: 12px; color: var(--pgr-dim); font-size: 11px; font-weight: 800; letter-spacing: .14em; margin: 16px 0 12px; }
.lobby-divider::before, .lobby-divider::after { content: ''; flex: 1; height: 1px; background: var(--pgr-line); }

/* Change-game picker popup — reuses the .qp-* overlay/panel shell (themed, appended
   inside #lobby-screen). Grid of game tiles. */
.lobby-gp-list { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 6px; width: 100%; }
.lobby-gp-item {
  display: flex; align-items: center; gap: 9px; text-align: left; cursor: pointer; font-family: inherit;
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 12px; padding: 10px 12px;
  color: var(--pgr-txt); transition: border-color .15s, background .15s;
}
.lobby-gp-item:hover { border-color: var(--pgr-sel); }
.lobby-gp-item.active { border-color: var(--pgr-sel); background: var(--pgr-selBg); }
.lobby-gp-icon { font-size: 22px; flex: 0 0 auto; }
.lobby-gp-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Full-width primary CTAs (use the themed hub-btn-primary base — no black, no neon) */
.lobby-quick-join-btn { width: 100%; font-size: 17px; padding: 14px; }
/* order:2 keeps Create last even when a section claims order:1 to sit below the
   registry-rendered options (craps' buy-in follows the Table setting it belongs to).
   Every other child defaults to order:0, so this changes nothing on its own. */
.lobby-create-btn { width: 100%; font-size: 15px; padding: 12px; order: 2; }
.lobby-table-card {
  display: flex; align-items: center; justify-content: space-between;
  background: var(--pgr-panel); border: 1px solid var(--pgr-line); border-radius: 14px;
  padding: .9rem 1rem; margin-bottom: 10px;
  transition: border-color .15s;
}
.lobby-table-card:hover { border-color: var(--pgr-sel); }
.lobby-card-left { display: flex; align-items: center; gap: 12px; }
.lobby-card-emoji { font-size: 28px; }
.lobby-card-title { font-size: 15px; font-weight: 600; margin-bottom: 2px; color: var(--pgr-txt); }
.lobby-card-sub   { font-size: 12px; color: var(--pgr-dim); }
.lobby-join-btn {
  background: var(--pgr-selBg); border: 1px solid var(--pgr-sel); border-radius: 8px;
  color: var(--pgr-sel); font-size: 14px; font-weight: 600; padding: 7px 18px; cursor: pointer;
  transition: filter .15s;
}
.lobby-join-btn:hover { filter: brightness(1.25); }
.lobby-view-btn { background: var(--pgr-seat); border-color: var(--pgr-line); color: var(--pgr-dim); }
.lobby-view-btn:hover { border-color: var(--pgr-sel); color: var(--pgr-txt); }
.lobby-card-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.lobby-invite-btn { padding: 7px 12px; }

/* "Your table" banner — the seated state (Slice 1, lobby start-flow overhaul,
   docs/design/lobby-start-flow-design.md). A pinned, accented card above the browse list
   that carries all seated controls; while it shows, the Create panel + Quick Join lock.
   Ready/Start + a live countdown land here in later slices. */
.lobby-seated { margin-bottom: 14px; }
.lobby-seated-inner {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: var(--pgr-selBg); border: 1.5px solid var(--pgr-sel); border-radius: 14px;
  padding: .9rem 1rem;
}
.lobby-seated-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.lobby-seated-leave { background: var(--pgr-seat); border-color: var(--pgr-line); color: var(--pgr-dim); }
.lobby-seated-leave:hover { filter: none; border-color: var(--pgr-sel); color: var(--pgr-txt); }
/* Joiner's ✓ Ready toggle (3b): accent-outline when off, filled-solid when on. All
   non-host humans ready → the server skips the 10s countdown and starts instantly.
   Host ▶ Start (Slice 4): the same prominent primary treatment.

   BOTH used to be `background: var(--pgr-sel); color: var(--pgr-selBg)`, which paints
   INVISIBLE TEXT: --pgr-selBg is not a background colour to sit on, it is a ~16%-alpha
   TINT OF THE ACCENT ITSELF (e.g. `oklch(.74 .14 152 / .16)`), so it rendered as
   near-transparent green lettering on solid green. Caught in the 2026-07-27 playtest —
   the Start button read as a blank green slab. There is no "ink on accent" token in
   BoardSchemes and none of the 12 schemes guarantees one (--pgr-bg is often a gradient,
   which is invalid as a `color` and would silently inherit), so the primary action is the
   button pair every scheme already guarantees is legible, lifted by an accent ring+glow. */
.lobby-ready-on, .lobby-start-btn {
  background: var(--pgr-btn); color: var(--pgr-btnTxt); border-color: var(--pgr-sel);
  box-shadow: 0 0 0 1px var(--pgr-sel) inset, 0 0 12px -4px var(--pgr-sel);
}
/* 📣 Announced (Slice D): once advertised, a muted non-interactive confirmation state. */
.lobby-announced { opacity: .6; cursor: default; }
.lobby-announced:hover { filter: none; }
/* Create panel is locked (collapsed, non-expandable) while seated at a table. */
.lobby-create-panel.locked { opacity: .55; }
/* Countdown state — the table is full and about to start (Slice 3a). */
.lobby-seated-starting { animation: lobby-seated-pulse 1.1s ease-in-out infinite; }
@keyframes lobby-seated-pulse {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50%      { box-shadow: 0 0 0 3px var(--pgr-selBg); }
}

/* Private tables: still listed (so the lobby feels alive) but greyed + locked —
   no id was sent to non-holders, so there's no Join button, only a lock chip.
   The invite/link holder enters via the notification or /join/<code>. */
.lobby-table-card--locked { opacity: .62; }
.lobby-table-card--locked:hover { border-color: var(--pgr-line); }
.lobby-lock-chip {
  display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto;
  background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 8px;
  color: var(--pgr-dim); font-size: 13px; font-weight: 600; padding: 7px 14px; white-space: nowrap;
}
.lobby-private-badge { color: var(--pgr-dim); font-weight: 700; }

/* Invite-players modal (reuses the .qp-* popup shell) */
.lobby-inv-linkrow { display: flex; gap: 8px; margin: 4px 0 2px; }
.lobby-inv-link {
  flex: 1; min-width: 0; font-size: 13px; padding: 9px 11px;
  background: var(--pgr-solid); border: 1px solid var(--pgr-line); border-radius: 8px;
  color: var(--pgr-txt);
}
.lobby-inv-copy { flex: 0 0 auto; padding: 9px 14px; font-size: 13px; }
.lobby-inv-sub { margin-top: 14px !important; margin-bottom: 6px !important; }
.lobby-inv-friends { display: flex; flex-direction: column; gap: 2px; max-height: 210px; overflow-y: auto; }
.lobby-inv-friend {
  display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--pgr-txt);
  padding: 8px 10px; border-radius: 8px; cursor: pointer;
}
.lobby-inv-friend:hover { background: var(--pgr-seat); }
.lobby-inv-friend input { width: 16px; height: 16px; accent-color: var(--pgr-sel); cursor: pointer; }
.lobby-inv-send { width: 100%; margin-top: 12px; padding: 11px; font-size: 15px; }
.lobby-inv-status { text-align: center; font-size: 13px; color: var(--pgr-dim); min-height: 18px; margin-top: 8px; }
.lobby-empty   { text-align: center; color: var(--pgr-dim); padding: 2rem 0; font-size: 14px; }
.lobby-loading { text-align: center; color: var(--pgr-dim); padding: 1.5rem 0; font-size: 14px; }

/* Store */
.store-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; }
.store-item {
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 12px;
  padding: .85rem; text-align: center;
}
.store-swatch {
  width: 32px; height: 32px; border-radius: 50%; margin: 0 auto 8px;
  border: 2px solid #444;
}
.store-swatch.rainbow {
  background: linear-gradient(90deg, #ff5252, #ffeb3b, #4caf50, #2196f3, #ba68c8, #ff5252);
  background-size: 300% 100%;
  animation: name-rainbow-shift 4s linear infinite;
}
.store-item-label { font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.store-item-price { font-size: 12px; color: #888; margin-bottom: 8px; }
.store-item-btn {
  width: 100%; font-size: 12px; padding: 6px; border-radius: 8px;
  border: 0.5px solid #2e2e2e; background: #2e2e2e; color: #eee; cursor: pointer;
}
.store-item-btn:hover { border-color: #555; }
.store-item-btn.equipped { background: #1D9E7522; color: #1D9E75; border-color: #1D9E7555; cursor: default; }
.store-item-btn:disabled { opacity: .5; cursor: default; }

/* Store v2 — two-level nav into one grid (overhaul 2026-07-20) */
.store2-screen { -webkit-user-select: none; user-select: none; }  /* no drag-highlight — feels cheap in a game UI */
.store2-card { padding: 0; overflow: hidden; }
.store2-tabs { display: flex; gap: 2px; padding: 6px 6px 0; border-bottom: 0.5px solid #2e2e2e; }
.store2-tabs button {
  flex: 1; white-space: nowrap; border: 0; cursor: pointer; background: transparent; color: #888;
  font-weight: 600; font-size: 12.5px; padding: 9px 4px 11px; border-bottom: 2px solid transparent;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}
.store2-tabs button .s2-tab-ic { font-size: 18px; }
.store2-tabs button.on { color: #eee; border-bottom-color: #4caf50; }
.store2-subs { display: flex; gap: 7px; flex-wrap: wrap; padding: 12px 14px 2px; }
.store2-subs button {
  border: 0.5px solid #2e2e2e; cursor: pointer; background: #222; color: #999;
  font-weight: 600; font-size: 11.5px; padding: 6px 13px; border-radius: 999px;
}
.store2-subs button.on { color: #fff; background: #16241d; border-color: #1D9E7566; }
.store2-body { padding: 12px 14px 16px; }
/* Centered title (line 1) + count parked at the right; description on a fixed-height line 2,
   so switching sub-categories never shifts the grid down. */
.store2-ghead { text-align: center; margin: 6px 0 12px; }
.store2-ghead .s2-line1 { position: relative; }
.store2-ghead .s2-title { font-size: 14px; font-weight: 700; }
.store2-ghead .s2-count { position: absolute; right: 2px; top: 50%; transform: translateY(-50%); font-size: 11px; color: #888; font-weight: 600; }
.store2-ghead .s2-sub { font-size: 11.5px; color: #888; line-height: 1.4; min-height: 32px; margin-top: 4px; display: flex; align-items: flex-start; justify-content: center; }

.store2-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; }
.store2-grid.two { grid-template-columns: repeat(2, 1fr); }
.store2-item {
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 13px; padding: 11px 9px 10px;
  display: flex; flex-direction: column; align-items: center; gap: 7px; text-align: center;
}
.store2-item .s2-lab { font-size: 12px; font-weight: 600; color: #eee; }
.store2-item .s2-pr { font-size: 11px; font-weight: 600; color: #ffd54f; }
.store2-item .s2-pr.owned { color: #888; }
.store2-item .s2-free { color: #4ade80; }
.store2-item .s2-btn {
  border: 0; cursor: pointer; border-radius: 9px; font-weight: 700; font-size: 11.5px;
  padding: 7px 0; width: 100%; color: #fff; background: #2e2e2e;
}
.store2-item .s2-btn.buy { background: #1D9E75; }
.store2-item .s2-btn.buy:disabled { background: #2b2b2b; color: #666; cursor: not-allowed; }
.store2-item .s2-btn.equip { background: #33384a; color: #eee; }
.store2-item .s2-btn.equipped { background: transparent; color: #4ade80; border: 0.5px solid #4ade8066; cursor: default; }
.store2-item .s2-btn:disabled { cursor: default; }
.s2-swatch { width: 44px; height: 44px; border-radius: 11px; border: 0.5px solid #2e2e2e; }
.s2-rainbow {
  background: linear-gradient(90deg, #ff5252, #ffeb3b, #4caf50, #2196f3, #ba68c8, #ff5252);
  background-size: 300% 100%; animation: name-rainbow-shift 4s linear infinite;
}
.s2-emoji {
  width: 44px; height: 44px; border-radius: 11px; background: #222; display: flex;
  align-items: center; justify-content: center; font-size: 22px; border: 0.5px solid #2e2e2e;
}
.s2-die { min-height: 44px; display: flex; align-items: center; justify-content: center; }
/* Store board-scheme preview — a tiny WYSIWYG board painted from the scheme's tokens. */
.s2-board {
  width: 100%; height: 54px; border-radius: 10px; padding: 8px; overflow: hidden;
  display: flex; flex-direction: column; justify-content: space-between; border: 0.5px solid #2e2e2e;
}
.s2-board-seat { display: flex; align-items: center; border: 1px solid; border-radius: 7px; padding: 4px 6px; }
.s2-board-dot { width: 11px; height: 11px; border-radius: 4px; }
.s2-board-btn { align-self: flex-start; border-radius: 6px; padding: 1px 8px; font-size: 10px; font-weight: 800; line-height: 1.5; }
.s2-placeholder {
  border: 1px dashed #2e2e2e; border-radius: 13px; padding: 22px 14px; text-align: center;
  color: #888; font-size: 12.5px; line-height: 1.55;
}
.s2-placeholder b { color: #ccc; }
/* Store taunt card — the phrase IS the visual, styled as a comic speech-bubble pill. */
.s2-taunt {
  min-height: 44px; width: 100%; display: flex; align-items: center; justify-content: center;
  background: #fff; color: #111; border-radius: 13px; border-bottom-left-radius: 3px;
  padding: 8px 10px; font-size: 12.5px; font-weight: 800; line-height: 1.3;
}
/* Slotted taunt — equipped look, but clickable (tap to unslot). */
.store2-item .s2-btn.slotted { background: transparent; color: #4ade80; border: 0.5px solid #4ade8066; }
.store2-cz { color: #4caf50 !important; }

@keyframes name-rainbow-shift { 0% { background-position: 0% 0; } 100% { background-position: 100% 0; } }

.name-rainbow {
  background: linear-gradient(90deg, #ff5252, #ffeb3b, #4caf50, #2196f3, #ba68c8, #ff5252);
  background-size: 300% 100%;
  animation: name-rainbow-shift 4s linear infinite;
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* Pig */
.pig-die-display  { text-align: center; margin: 1.25rem 0; }
.pig-die          { font-size: 64px; line-height: 1; }
.pig-result-text  { font-size: 14px; margin-top: 10px; min-height: 20px; color: #888; text-align: center; }
.pig-round-info   { margin-bottom: 1rem; }
.pig-actions      { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 1rem; }

/* Farkle */
.farkle-dice-display {
  display: flex; justify-content: center; align-items: center;
  flex-wrap: wrap; gap: 8px; margin: 1.25rem 0;
}
/* On narrow screens, flex-wrap splits 6 dice unevenly (5 + 1) — force a clean 3x2 grid. */
@media (max-width: 600px) {
  .farkle-dice-display { display: grid; grid-template-columns: repeat(3, 1fr); justify-items: center; }
}
.farkle-die {
  font-size: 40px; line-height: 1; border-radius: 8px; padding: 4px 6px;
  transition: background .1s, transform .05s;
}
.farkle-die.selectable { cursor: pointer; }
.farkle-die.selectable:hover { background: #2e2e2e; }
.farkle-die.selected { background: #1D9E7544; transform: translateY(-4px); }
.farkle-die.pending { opacity: .25; }
.farkle-die.busted { background: #E24B4A33; animation: shake .45s ease-out; }
.farkle-die.rolling { animation: die-tumble .28s ease-in-out infinite; }

@keyframes die-tumble {
  0%, 100% { transform: rotate(0deg) scale(1); }
  50% { transform: rotate(15deg) scale(1.12); }
}

.farkle-scoring-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.farkle-scoring-table td { padding: 4px 6px; border-bottom: 1px solid #2e2e2e; }
.farkle-scoring-table td:last-child { text-align: right; color: #1D9E75; font-weight: 600; white-space: nowrap; }

/* Blackjack/Yahtzee rules tables have long descriptive text in the right
   column — let it wrap instead of forcing the card wider than the screen. */
#blackjack-rules-overlay .farkle-scoring-table td:last-child,
#yahtzee-scoring-overlay .farkle-scoring-table td:last-child {
  text-align: left; white-space: normal;
}

/* Blackjack */
.bj-hand     { margin-bottom: .6rem; }
.bj-hand-box {
  border: 0.5px solid #2e2e2e; border-radius: 10px; padding: 8px 10px;
}
.bj-hand-label {
  display: flex; justify-content: space-between; align-items: flex-start;
  font-size: 12px; color: #888; margin-bottom: 6px;
}
.bj-hand-total { color: #fff; font-weight: 700; font-size: 15px; }

/* Reaction face doubles as the player's avatar, stacked under "You" */
.bj-player-name { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.bj-hand-label .reaction-face { display: block; margin: 0; }
.bj-hand-label .face-wrap     { font-size: 18px; margin: 0; }
.bj-dice-row {
  display: flex; justify-content: center; align-items: center;
  flex-wrap: wrap; gap: 8px; min-height: 54px;
}
.bj-die {
  font-size: 40px; line-height: 1; border-radius: 8px; padding: 4px 6px;
}
.bj-die.hidden-die { opacity: .35; }
.bj-die.new-die    { animation: bj-deal .25s ease-out; }
.bj-die.bust       { background: #E24B4A33; animation: shake .45s ease-out; }
.bj-die.win-die    { background: #1D9E7544; }

@keyframes bj-deal {
  from { transform: translateY(-16px) scale(.6); opacity: 0; }
  to   { transform: translateY(0) scale(1); opacity: 1; }
}

/* Yahtzee scorecard */
.yz-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 0; border-bottom: 0.5px solid #2e2e2e; font-size: 13px; gap: 8px;
}
.yz-row:last-child { border-bottom: none; }
.yz-label { flex: 1; color: #ccc; }
.yz-row-filled .yz-label { color: #888; }
.yz-score { font-weight: 600; min-width: 48px; text-align: right; white-space: nowrap; }
.yz-row-filled .yz-score { color: #888; }
.yz-preview { color: #1D9E75; font-weight: 600; min-width: 70px; text-align: right; white-space: nowrap; }
.yz-pending { color: #555; }
.yz-bonus-tag { color: #f97316; font-size: 11px; margin-left: 4px; }
.yz-score-btn {
  font-size: 12px; padding: 4px 10px; border-radius: 8px;
  border: 0.5px solid #1D9E7555; background: #1D9E7522; color: #1D9E75; cursor: pointer;
  white-space: nowrap;
}
.yz-score-btn:hover { background: #1D9E7544; }
.yz-score-btn:disabled { opacity: .5; cursor: default; }
.yz-row-subtotal { font-size: 12px; color: #888; }
.yz-row-total { font-weight: 700; font-size: 15px; border-top: 1px solid #444; padding-top: 8px; margin-top: 4px; }
.yz-opp-col { min-width: 48px; text-align: right; border-left: 1px solid #2e2e2e; padding-left: 8px; margin-left: 4px; flex-shrink: 0; }
.yz-bonus-tag { font-size: 10px; color: #f0a500; }

/* Shut the Box tile grid */
.sb-tiles {
  display: grid; grid-template-columns: repeat(9, 1fr); gap: 4px; margin: 1.25rem 0;
}
.sb-tile {
  font-size: 16px; font-weight: 700; line-height: 1; border-radius: 8px;
  padding: 10px 0; text-align: center; border: 0.5px solid #2e2e2e;
  background: #1e1e1e; color: #ddd; cursor: pointer; transition: background .1s, transform .05s;
}
.sb-tile:hover:not(:disabled) { background: #2e2e2e; }
.sb-tile.selected { background: #1D9E7544; border-color: #1D9E7555; color: #1D9E75; transform: translateY(-2px); }
.sb-tile.shut { opacity: .25; text-decoration: line-through; background: #151515; }
.sb-tile:disabled { cursor: default; }
@media (max-width: 480px) {
  .sb-tile { font-size: 13px; padding: 8px 0; }
}

/* ── Shut the Box SOLO reskin (.stbr-*) — token-driven, layout-scheme aware ─────
   Consumes the shared --pgr-* colour tokens (BoardSchemes) + --pgr-shell*/tile
   tokens (LayoutSchemes). Classic = full-bleed; Arcade = the cabinet shell. All
   colours come from vars so a scheme/layout switch is pure data (zero markup). */
.stbr-root { min-height: 100vh; font-family: var(--pgr-font, 'Outfit', system-ui, sans-serif); color: var(--pgr-txt, #e9e8e4); display: flex; flex-direction: column; }
/* The blur itself is the shared .bp-blurred (BetPopup owns it) — this just carries the
   transition, so the board eases into/out of the blur instead of snapping. */
.stbr-content { flex: 1; display: flex; flex-direction: column; transition: filter .35s ease, opacity .35s ease; }
/* The shared layout-scheme SHELL. Classic = the game's own board width (or full-bleed),
   transparent, no chrome; Arcade = the 480px "cabinet" (gradient bg, 28px radius, drop
   shadow, Poppins). Purely token-driven, so a game opts in by wrapping its board in
   .gm-shell and calling GameTheme.apply() — zero per-game layout code. */
.gm-shell, .stbr-shell {
  width: 100%; max-width: var(--pgr-shellMaxW, none); margin: 0 auto;
  background: var(--pgr-shellBg, transparent); border-radius: var(--pgr-shellRadius, 0);
  box-shadow: var(--pgr-shellShadow, none); padding: var(--pgr-shellPad, clamp(10px,1.7vw,20px));
  display: flex; flex-direction: column; gap: var(--pgr-shellGap, clamp(7px,1.2vh,13px)); box-sizing: border-box;
}
.stbr-board { display: flex; flex-direction: column; align-items: center; gap: clamp(10px,2vh,20px); }
.stbr-arena { width: 100%; display: flex; justify-content: center; }
.stbr-panel {
  display: inline-flex; flex-direction: column; background: var(--pgr-panel, rgba(255,255,255,.04));
  border: var(--pgr-panelBd, 1px solid var(--pgr-line,rgba(255,255,255,.075))); border-radius: 22px;
  padding: clamp(10px,1.8vw,18px);
}
.stbr-panel-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: clamp(8px,1.4vw,13px); }
.stbr-panel-title { color: var(--pgr-dim,#83858b); font-size: clamp(12px,2.4vw,14px); font-weight: 600; letter-spacing: .04em; }
.stbr-panel-sub { color: var(--pgr-dim,#83858b); font-size: clamp(12px,2.4vw,14px); white-space: nowrap; }
.stbr-grid {
  display: grid; grid-template-columns: repeat(3,1fr); grid-template-rows: repeat(3,1fr);
  gap: clamp(8px,2.2vw,15px); width: min(300px, 74vw); height: min(300px, 74vw);
}
.stbr-tile {
  display: flex; align-items: center; justify-content: center; border-radius: 14px; padding: 0;
  font-weight: 700; font-family: var(--pgr-font,inherit); font-variant-numeric: tabular-nums; line-height: 1;
  font-size: clamp(26px, 12vw, 50px); user-select: none; cursor: pointer;
  color: var(--pgr-tileNum,#ededed); background: var(--pgr-tileBg,linear-gradient(180deg,#272a30,#1f2126));
  border: 1px solid var(--pgr-tileBorder,rgba(255,255,255,.08)); box-shadow: var(--pgr-tileShadow,0 5px 14px -7px rgba(0,0,0,.75));
  transition: transform .15s, box-shadow .15s, background .15s, opacity .2s;
}
.stbr-tile.selected {
  color: var(--pgr-selNum,#fff); background: var(--pgr-selBg,rgba(29,158,117,.18));
  border: 1.5px solid var(--pgr-sel,#1D9E75); box-shadow: 0 9px 22px -9px var(--pgr-sel,#1D9E75); transform: translateY(-3px);
}
.stbr-tile.shut, .stbr-tile:disabled.shut {
  color: var(--pgr-shutNum,#4c4e54); background: var(--pgr-shutBg,#15161a);
  border: 1px solid var(--pgr-shutBorder,rgba(255,255,255,.04)); box-shadow: inset 0 2px 7px rgba(0,0,0,.45);
  transform: translateY(2px) scaleY(.93); opacity: .6; cursor: default; text-decoration: line-through;
}
.stbr-tile:disabled { cursor: default; }
.stbr-controls { display: flex; flex-direction: column; align-items: center; gap: clamp(5px,1vh,10px); width: 100%; }
.stbr-dice { display: flex; gap: clamp(10px,2.4vw,18px); align-items: center; justify-content: center; min-height: 60px; }
.stbr-dice .dr-die, .stbr-dice .dr-wrap { flex: none; }
.stbr-msg { color: var(--pgr-dim,#83858b); font-size: clamp(13px,2.7vw,16px); min-height: 1.4em; text-align: center; font-weight: 500; }
.stbr-secondary { display: flex; align-items: center; justify-content: center; gap: clamp(8px,1.8vw,14px); margin-top: 2px; }
/* One fixed slot holds both action cards stacked — its height is reserved so the swap
   never pushes the board / round-info / history. Sized to the taller (Roll) card. */
.stbr-action-slot { position: relative; width: 100%; min-height: clamp(116px,24vw,136px); display: flex; align-items: center; justify-content: center; perspective: 2400px; }
.stbr-card {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: clamp(8px,1.6vh,12px); transform-origin: 50% 50%;
  border: var(--pgr-panelBd,1px solid var(--pgr-line,rgba(255,255,255,.12)));
  border-radius: 16px; background: var(--pgr-panel,rgba(255,255,255,.03)); padding: clamp(10px,2vw,14px);
}
.stbr-card.stbr-card-in  { animation: stbr-unfold .4s ease; }
.stbr-card.stbr-card-out { animation: stbr-fold .3s ease forwards; }
.stbr-dc-row { display: flex; align-items: center; justify-content: center; gap: clamp(8px,1.8vw,12px); }
/* Roll button "call attention" — a gentle glow that ramps IN (from no glow) and pulses,
   armed a few seconds after the Roll card appears if the player hasn't rolled yet. */
@keyframes stbr-roll-glow {
  0%   { box-shadow: 0 0 0 0 rgba(29,158,117,0); }
  100% { box-shadow: 0 0 20px 3px var(--pgr-sel,#1D9E75); border-color: var(--pgr-sel,#1D9E75); }
}
.stbr-rolldice-attn { animation: stbr-roll-glow 1.5s ease-in-out infinite alternate; }
.stbr-btn {
  min-width: clamp(140px,30vw,180px); height: clamp(48px,9vw,58px); border-radius: 14px;
  background: var(--pgr-btn,linear-gradient(180deg,#333841,#22252b)); color: var(--pgr-btnTxt,#e9e8e4);
  border: 1px solid var(--pgr-line,rgba(255,255,255,.12)); font-size: clamp(16px,3.4vw,19px); font-weight: 700;
  font-family: var(--pgr-font,inherit); cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; padding: 0 18px;
  transition: opacity .2s, transform .12s;
}
.stbr-btn:disabled { opacity: .45; cursor: default; }
/* Forfeit — secondary/muted while active; a two-tap arm turns it into a danger button
   (inline confirm, so no native confirm() dialog — the run + its stake is being surrendered). */
.stbr-forfeit-btn {
  min-width: auto; height: clamp(40px,7.5vw,48px); padding: 0 16px; font-size: clamp(13px,2.8vw,15px);
  font-weight: 600; background: transparent; color: var(--pgr-dim,#9a9ca2);
  border-color: var(--pgr-line,rgba(255,255,255,.18));
}
.stbr-forfeit-btn.stbr-forfeit-armed {
  background: #E24B4A; color: #fff; border-color: #E24B4A; font-weight: 700;
}
/* One-die choice — a segmented 1/2-die toggle inside the Roll card. The 1-die option
   stays greyed/disabled until 7·8·9 are all shut (server-authoritative eligibility). */
.stbr-dc-label { color: var(--pgr-dim,#83858b); font-size: clamp(12px,2.6vw,14px); font-weight: 600; }
.stbr-dc-toggle { display: inline-flex; border-radius: 12px; overflow: hidden; border: 1px solid var(--pgr-line,rgba(255,255,255,.18)); }
.stbr-dc-opt {
  padding: 8px clamp(12px,3vw,18px); height: clamp(40px,7.5vw,48px); background: transparent;
  color: var(--pgr-dim,#9a9ca2); border: none; font-family: var(--pgr-font,inherit);
  font-size: clamp(13px,2.8vw,15px); font-weight: 700; cursor: pointer; transition: background .15s, color .15s;
}
.stbr-dc-opt + .stbr-dc-opt { border-left: 1px solid var(--pgr-line,rgba(255,255,255,.18)); }
.stbr-dc-opt.sel { background: var(--pgr-selBg,var(--pgr-btn,#2f9e75)); color: var(--pgr-selNum,var(--pgr-btnTxt,#fff)); }
.stbr-dc-opt:disabled { opacity: .32; cursor: default; }
/* Unlock flourish — the 1-die chip glows/pulses once 7·8·9 are all shut, so the newly
   available option gets noticed. A one-shot "pop" leads into a steady pulse, until the roll. */
@keyframes stbr-dc-unlock-glow {
  0%,100% { box-shadow: 0 0 0 0 rgba(29,158,117,0); }
  50%     { box-shadow: 0 0 15px 2px var(--pgr-sel,#1D9E75); }
}
@keyframes stbr-dc-unlock-pop {
  0%   { transform: scale(1); box-shadow: 0 0 0 0 rgba(29,158,117,0); }
  35%  { transform: scale(1.14); box-shadow: 0 0 22px 5px var(--pgr-sel,#1D9E75); }
  100% { transform: scale(1); box-shadow: 0 0 12px 1px var(--pgr-sel,#1D9E75); }
}
.stbr-dc-opt.stbr-dc-unlocked {
  color: var(--pgr-txt,#e9e8e4); position: relative; z-index: 1;
  animation: stbr-dc-unlock-pop .5s ease-out, stbr-dc-unlock-glow 1.3s ease-in-out .5s infinite;
}
/* MP opponent seats (2–4 players) — one compact card per rival above your own box.
   Purely token-driven, so Classic and Arcade both get the right colours for free. A card
   carries the seat's name, its live 9-tile strip, its last roll and its score/status; the
   seat on turn lights up with the selection accent. 3 rivals fit side-by-side on a phone
   because the cards flex-shrink; the tile strip is the only fixed-ish part. */
.stbr-seats { display: flex; flex-wrap: wrap; gap: clamp(6px,1.6vw,12px); justify-content: center; }
.stbr-seats:empty { display: none; }
.stbr-seat {
  position: relative; flex: 1 1 0; min-width: 0; max-width: 320px;
  display: flex; flex-direction: column; gap: 6px;
  background: var(--pgr-panel,rgba(255,255,255,.04));
  border: var(--pgr-panelBd,1px solid var(--pgr-line,rgba(255,255,255,.075)));
  border-radius: 14px; padding: clamp(7px,1.5vw,11px);
  transition: border-color .2s, box-shadow .2s, opacity .2s;
}
.stbr-seat.stbr-seat-turn { border-color: var(--pgr-sel,#1D9E75); box-shadow: 0 0 16px -6px var(--pgr-sel,#1D9E75); }
.stbr-seat.stbr-seat-done { opacity: .68; }
.stbr-seat-top { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px; min-width: 0; }
.stbr-seat-name {
  flex: 1 1 auto; min-width: 3.5em;
  font-weight: 700; font-size: clamp(12px,2.5vw,14px); color: var(--pgr-txt,#e9e8e4);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.stbr-seat-stat { margin-left: auto; font-size: clamp(11px,2.2vw,13px); color: var(--pgr-dim,#83858b); white-space: nowrap; }
.stbr-seat-strip { display: flex; gap: 3px; }
.stbr-seat-tile {
  flex: 1 1 0; min-width: 0; aspect-ratio: 1 / 1.15; border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  font-size: clamp(9px,2vw,12px); font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--pgr-tileNum,#ededed); background: var(--pgr-tileBg,linear-gradient(180deg,#272a30,#1f2126));
  border: 1px solid var(--pgr-tileBorder,rgba(255,255,255,.08));
}
.stbr-seat-tile.shut {
  color: var(--pgr-shutNum,#4c4e54); background: var(--pgr-shutBg,#15161a);
  border-color: var(--pgr-shutBorder,rgba(255,255,255,.04)); opacity: .6; text-decoration: line-through;
}
.stbr-seat-dice { display: flex; gap: 5px; align-items: center; justify-content: center; min-height: 26px; }
.stbr-seat .speech-bubble { top: auto; bottom: calc(100% + 6px); }
/* Your own box anchors your bubbles + the bust/bank flash, so it needs a positioning context. */
#stb-mp-my-panel { position: relative; }
.stbr-round-info { display: flex; justify-content: center; gap: clamp(14px,4vw,26px); color: var(--pgr-dim,#83858b); font-size: clamp(12px,2.5vw,14px); }
.stbr-round-info b { color: var(--pgr-txt,#e9e8e4); }
.stbr-round-info .stbr-ri-dim { opacity: .55; font-size: .92em; }

/* ── Yahtzee reskin (.yzr-*) — token-driven, layout-scheme aware ────────────────────
   Verbatim geometry from docs/design/Other Games/Yahtzee V3: one compact screen holding
   a narrow left rail (player cards → dice → roll button) beside the scorecard, whose grid
   carries one score column per player. Every colour is a --pgr-* token, so a board-scheme
   or Classic/Arcade switch is pure data. The scorecard can be maximised (rail hidden). */
.yzr-root { min-height: 100vh; font-family: var(--pgr-font, 'Outfit', system-ui, sans-serif); color: var(--pgr-txt, #e9e8e4); display: flex; flex-direction: column; }
.yzr-content {
  flex: 1; display: flex; flex-direction: column; gap: clamp(9px,1.8vh,14px);
  padding: clamp(8px,2vh,16px) clamp(10px,2.5vw,20px) 40px;
  transition: filter .35s ease, opacity .35s ease;   /* .bp-blurred eases in behind the bet popup */
}
.yzr-split { width: 100%; max-width: 660px; margin: 0 auto; display: flex; gap: clamp(9px,2.2vw,16px); align-items: flex-start; }
.yzr-split.yzr-maxed { gap: 0; }
.yzr-split.yzr-maxed .yzr-rail { display: none; }
.yzr-rail { flex: 0 0 auto; width: clamp(114px,30vw,168px); display: flex; flex-direction: column; gap: clamp(9px,1.7vh,13px); }
.yzr-cards { display: flex; flex-direction: column; gap: clamp(6px,1.2vh,9px); }

/* ── Narrow + crowded: the thin-rail arrangement (MP, 3+ seats, phone widths) ──
   Side by side, a 114px rail plus four content-sized seat columns leaves the category
   labels ~37px in Classic and ~13px in Arcade at 360px — unreadable. Owner's fix: keep
   the dice BESIDE the card (so you can see your hand while picking a category) but line
   them up ONE PER ROW, which shrinks the rail to ~60px, and drop the seat cards, turn
   label and Roll button into the dead space below the scorecard.

   `display: contents` on the rail (and the dice wrap) is what makes this a pure
   rearrangement: their children become grid items of the split, so no DOM moves and the
   wide layout is untouched. The .yzr-maxed override below is deliberately more specific
   than the base `.yzr-maxed .yzr-rail { display: none }` — same specificity would lose on
   load order and ⛶ would silently stop hiding the rail. */
@media (max-width: 430px) {
  .yzr-split.yzr-thin {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    /* Roll sits directly under the card, ABOVE the seat banners: the button and the dice
       are what you touch every turn, the banners are reference. Owner's call. */
    grid-template-areas: "dice card" "turn turn" "roll roll" "seats seats";
    gap: clamp(7px,2vw,12px);
    align-items: start;
  }
  .yzr-split.yzr-thin .yzr-rail,
  .yzr-split.yzr-thin .yzr-dicewrap { display: contents; }
  .yzr-split.yzr-thin .yzr-scorecard { grid-area: card; }
  /* Bottom-aligned with the scorecard, so the last die sits right above the Roll button —
     the two things you touch every turn end up next to each other (owner's call). */
  .yzr-split.yzr-thin .yzr-dicebox   { grid-area: dice; align-self: end; padding: clamp(7px,1.6vh,10px) 8px; }
  /* One die per row: the row wraps at a single die's width (the renderer draws them at 36
     here — see YAHTZEE_MP_DIE_SIZE_THIN — so 42px still admits exactly one). */
  .yzr-split.yzr-thin .yzr-dice { max-width: 42px; gap: clamp(5px,1.2vh,8px); }
  /* Arcade's cabinet costs ~24px of width and as much height on a phone — enough on its
     own to clip three category labels that Classic renders in full. Slim the frame down
     here; the cabinet look survives, it just stops taxing the board. */
  /* Arcade only: a slightly tighter grid gap. Classic already fits the whole board in a
     780px viewport with room to spare; Arcade was landing its last seat banner 1px past
     the fold, and 3 row gaps at 7.2px is the cheapest place to find those pixels. */
  .gm-arcade .yzr-split.yzr-thin { --pgr-shellPad: 6px; gap: 5px; }
  .gm-arcade .yzr-content:has(.yzr-split.yzr-thin) .gm-shell { padding: 2px 4px 6px; }
  /* Pull the board up under the topbar. The default 15.6px of content padding (plus
     Arcade's shell padding on top of it) left a visible dead band above the scorecard —
     wasted twice over here, since the layout is already fighting for vertical room. */
  .yzr-content:has(.yzr-split.yzr-thin) { padding-top: 6px; }
  .yzr-split.yzr-thin .yzr-turnlab { grid-area: turn; }
  .yzr-split.yzr-thin .yzr-cards {
    grid-area: seats; flex-direction: row; flex-wrap: wrap; gap: clamp(6px,1.6vw,9px);
  }
  .yzr-split.yzr-thin .yzr-cards > .yzr-pcard { flex: 1 1 calc(50% - 9px); min-width: 0; }
  .yzr-split.yzr-thin .yzr-btn { grid-area: roll; }
  /* ⛶ still collapses the whole rail away and gives the card the full width. */
  .yzr-split.yzr-thin.yzr-maxed .yzr-rail { display: none; }
  .yzr-split.yzr-thin.yzr-maxed { grid-template-columns: minmax(0, 1fr); grid-template-areas: "card"; }
}
.yzr-pcard {
  position: relative; display: flex; align-items: center; gap: 8px; min-width: 0;
  background: var(--pgr-seat, rgba(255,255,255,.025)); border: 1px solid var(--pgr-line, rgba(255,255,255,.075));
  border-radius: 13px; padding: clamp(6px,1.3vw,9px) clamp(7px,1.6vw,10px);
}
/* The active-seat ring is an overlay so it can glow without nudging the card's box. */
.yzr-pcard.active::after {
  content: ''; position: absolute; inset: 0; border-radius: 13px; pointer-events: none;
  border: 1.5px solid var(--pgr-sel, #1D9E75); box-shadow: 0 10px 26px -12px var(--pgr-sel, #1D9E75);
}
.yzr-avatar {
  width: clamp(26px,6vw,32px); height: clamp(26px,6vw,32px); border-radius: 50%; flex: none; z-index: 2;
  background: var(--pgr-panel, rgba(255,255,255,.04)); border: 1px solid var(--pgr-line, rgba(255,255,255,.075));
  display: flex; align-items: center; justify-content: center; font-size: clamp(15px,3.4vw,18px);
}
.yzr-avatar .face-wrap { position: relative; display: inline-flex; align-items: center; justify-content: center; }
.yzr-pmeta { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.18; z-index: 2; }
.yzr-pname { font-weight: 600; font-size: clamp(11px,2.5vw,13px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.yzr-pnote { color: var(--pgr-dim,#83858b); font-size: clamp(9px,1.9vw,10px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.yzr-pnote:empty { display: none; }
.yzr-ptotal { flex: none; z-index: 2; font-size: clamp(16px,4vw,21px); font-weight: 800; font-variant-numeric: tabular-nums; color: var(--pgr-dim,#83858b); }
.yzr-pcard.me .yzr-ptotal { color: var(--pgr-txt,#e9e8e4); }
.yzr-pcard.active .yzr-ptotal { color: var(--pgr-sel,#1D9E75); }
/* A seat that quit stays on the rail (its partial score still counts in the standings),
   just visibly out of the running. */
.yzr-pcard.dnf { opacity: .5; }
/* MP scoring glows. Deliberately NOT the .mp-seat keyframes above: those also drive
   border-color back to a hardcoded #2e2e2e, which would fight the board scheme's themed
   card border and leave it grey afterwards. Box-shadow only. */
@keyframes yzr-glow-good  { 0%, 100% { box-shadow: none; } 15%, 65% { box-shadow: 0 0 16px 4px #1D9E75; } }
@keyframes yzr-glow-great { 0%, 100% { box-shadow: none; } 15%, 65% { box-shadow: 0 0 22px 6px #f0a500; } }
@keyframes yzr-glow-bad   { 0%, 100% { box-shadow: none; } 15%, 65% { box-shadow: 0 0 16px 4px #E24B4A; } }
.yzr-pcard.yz-glow-good  { animation: yzr-glow-good  1.8s ease-out; }
.yzr-pcard.yz-glow-great { animation: yzr-glow-great 2.2s ease-out; }
.yzr-pcard.yz-glow-bad   { animation: yzr-glow-bad   1.8s ease-out; }

.yzr-dicewrap { display: flex; flex-direction: column; align-items: center; gap: clamp(6px,1.4vh,10px); width: 100%; padding-top: clamp(4px,1.4vh,10px); }
.yzr-dicebox {
  width: 100%; background: var(--pgr-panel, rgba(255,255,255,.04));
  border: 1px solid var(--pgr-line, rgba(255,255,255,.075)); border-radius: 16px;
  padding: clamp(10px,2vh,14px) clamp(8px,2vw,12px);
}
/* MP: the shared cup changing hands. Flipping the card is the only cue that the turn
   moved (and it lands on dice already repainted in the new roller's skin). Transform-only,
   so nothing in the layout reflows; backface-visibility keeps the mid-flip edge clean. */
.yzr-dicebox.yzr-flipin { animation: yzr-cardflip .42s cubic-bezier(.34,1.05,.55,1) both; transform-origin: 50% 50%; backface-visibility: hidden; }
@keyframes yzr-cardflip {
  0%   { transform: perspective(700px) rotateY(-88deg) scale(.94); opacity: .25; }
  60%  { transform: perspective(700px) rotateY(8deg)   scale(1.01); opacity: 1; }
  100% { transform: perspective(700px) rotateY(0deg)   scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .yzr-dicebox.yzr-flipin { animation: none; } }
.yzr-dice { display: flex; flex-wrap: wrap; gap: clamp(6px,1.5vw,9px); justify-content: center; width: 100%; max-width: 116px; margin: 0 auto; min-height: 42px; }
.yzr-dieslot { display: flex; flex-direction: column; align-items: center; gap: 4px; background: none; border: none; padding: 0; cursor: pointer; font: inherit; }
/* DieRenderer owns this container while it animates a roll, and paints bare dice into it
   (no slot wrapper, so no 4px gap + 11px HOLD row underneath). Without this the dice box
   shrank and the dice re-packed for the length of every roll, then jumped back. 15px is
   exactly the slot's caption block, so a rolling die occupies the same 57px as a still one. */
.yzr-dice > .dr-die, .yzr-dice > .dr-wrap { margin-bottom: 15px; }
.yzr-dieslot:disabled { cursor: default; }
.yzr-diewrap { position: relative; border-radius: 9px; transition: box-shadow .2s; }
.yzr-dieslot.held .yzr-diewrap { box-shadow: 0 0 0 3px var(--pgr-selBg, rgba(29,158,117,.2)); }
.yzr-hold { height: 11px; display: flex; align-items: center; justify-content: center; }
.yzr-hold span { font-size: 8px; letter-spacing: .16em; font-weight: 800; color: var(--pgr-sel,#1D9E75); }
.yzr-turnlab { display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; width: 100%; }
.yzr-turn { font-size: clamp(11px,2.6vw,13px); font-weight: 700; color: var(--pgr-txt,#e9e8e4); }
/* Reserved height so the message never reflows the rail as it changes length. */
.yzr-msg { font-size: clamp(9px,2vw,11px); color: var(--pgr-dim,#83858b); line-height: 1.35; min-height: 2.7em; }
.yzr-turnlab .commentary { font-size: clamp(9px,2vw,11px); margin: 0; }
.yzr-btn {
  width: 100%; height: clamp(46px,9vw,54px); border-radius: 14px;
  border: 1px solid var(--pgr-line, rgba(255,255,255,.075)); background: var(--pgr-btn, linear-gradient(180deg,#2d3036,#22242a));
  color: var(--pgr-btnTxt,#f1f0ec); font-family: var(--pgr-font,inherit); font-size: clamp(13px,3vw,15px); font-weight: 700;
  cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 7px;
  transition: transform .13s cubic-bezier(.34,1.56,.64,1), filter .13s, opacity .2s; will-change: transform;
}
@media (hover: hover) { .yzr-btn:hover:not(:disabled) { transform: translateY(-2px); filter: brightness(1.08); } }
.yzr-btn:active:not(:disabled) { transform: translateY(2px) scale(.96); }
.yzr-btn:disabled { opacity: .45; cursor: default; }

.yzr-scorecard {
  flex: 1; min-width: 0; background: var(--pgr-seat, rgba(255,255,255,.025));
  border: 1px solid var(--pgr-line, rgba(255,255,255,.075)); border-radius: 16px; padding: clamp(10px,2.2vw,17px);
}
.yzr-sc-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: clamp(6px,1.4vw,10px); }
.yzr-sc-title { font-size: 9px; letter-spacing: .18em; font-weight: 700; color: var(--pgr-dim,#83858b); }
/* MP's header carries three controls (💬 / 🏳️ / ⛶), so they group on the right rather
   than being spread apart by the head's space-between. */
.yzr-sc-tools { display: flex; align-items: center; gap: 4px; }
.yzr-max-btn {
  flex: none; width: 26px; height: 26px; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--pgr-line, rgba(255,255,255,.075)); background: var(--pgr-panel, rgba(255,255,255,.04));
  color: var(--pgr-dim,#83858b); font-size: 13px; line-height: 1; display: flex; align-items: center; justify-content: center;
  transition: transform .13s cubic-bezier(.34,1.56,.64,1), color .13s;
}
@media (hover: hover) { .yzr-max-btn:hover { transform: scale(1.1); color: var(--pgr-txt,#e9e8e4); } }
.yzr-max-btn:active { transform: scale(.9); }
/* Forfeit flag — a bare emoji, no chrome, so it reads as an affordance rather than a
   second primary control competing with the rail's one button. Dimmed until hovered. */
.yzr-flag-btn {
  flex: none; width: 22px; height: 26px; padding: 0; cursor: pointer;
  border: 0; background: none; font-size: 13px; line-height: 1; opacity: .5;
  display: flex; align-items: center; justify-content: center;
  transition: transform .13s cubic-bezier(.34,1.56,.64,1), opacity .13s;
}
@media (hover: hover) { .yzr-flag-btn:hover { transform: scale(1.15); opacity: 1; } }
.yzr-flag-btn:active { transform: scale(.9); }
/* One grid for the whole card: a label column plus one column per player (set inline by
   the renderer). Rows are emitted as bare cells, so a seat count change is data-only.
   `align-items: stretch` is load-bearing: the mock centres its cells, which lets each cell
   be only as tall as its content, so the row divider (a border-bottom on every cell) lands
   at a different height in a score-pill cell than in a text cell — the dividers visibly
   step up on the right. Stretching every cell to the row height makes them one line, and
   each cell centres its own content instead. */
.yzr-grid { display: grid; align-items: stretch; width: 100%; column-gap: 0; }
.yzr-h {
  padding: 0 0 clamp(6px,1.4vw,9px) clamp(6px,1.5vw,9px); font-size: clamp(9px,2vw,11px); font-weight: 600;
  color: var(--pgr-dim,#83858b); display: flex; justify-content: flex-end;
}
.yzr-h.me { font-weight: 700; color: var(--pgr-sel,#1D9E75); }
.yzr-h > span { width: clamp(30px,7vw,42px); overflow: hidden; white-space: nowrap; text-align: right; }
/* A name too long for its column scrolls instead of being clipped (mock's marquee). */
.yzr-h .yzr-marq { display: inline-flex; animation: yzr-marquee 5s linear infinite; }
@keyframes yzr-marquee { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
.yzr-hblank { padding: 0 6px clamp(6px,1.4vw,9px) 0; }
.yzr-rl {
  padding: clamp(5px,1.3vw,8px) 6px clamp(5px,1.3vw,8px) 0; border-bottom: 1px solid var(--pgr-line, rgba(255,255,255,.075));
  font-size: clamp(10px,2.3vw,13px); font-weight: 500; color: var(--pgr-txt,#e9e8e4);
  display: flex; align-items: center; min-width: 0;
}
/* Inner box because a stretched flex cell can't ellipsize its own text node. */
.yzr-rl-t { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* A category label that doesn't fit scrolls instead of ellipsizing — the same treatment
   long player names get in the column headers. Applied ONLY to rows that actually
   overflow (the renderer measures and tags them), because fifteen labels marqueeing at
   once would be noise; in practice it's a safety net for the narrowest phones. */
.yzr-rl-t.yzr-scroll { text-overflow: clip; }
.yzr-rl-t .yzr-marq { display: inline-flex; animation: yzr-marquee 6s linear infinite; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) {
  .yzr-rl-t .yzr-marq { animation: none; }
  .yzr-rl-t.yzr-scroll { text-overflow: ellipsis; }
}
.yzr-v {
  padding: clamp(5px,1.3vw,8px) clamp(6px,1.5vw,9px); border-bottom: 1px solid var(--pgr-line, rgba(255,255,255,.075));
  font-size: clamp(10px,2.2vw,12px); font-weight: 500; font-variant-numeric: tabular-nums;
  color: var(--pgr-dim,#83858b); display: flex; align-items: center; justify-content: center;
}
.yzr-v.me { font-weight: 600; color: var(--pgr-txt,#e9e8e4); }
.yzr-bl { border-left: 1px solid var(--pgr-line, rgba(255,255,255,.075)); }
/* Tight mode — MP sets this at 3+ seats. The seat columns are content-sized
   (`minmax(24px,auto)`), so on a phone four of them plus their padding leave the category
   labels almost nothing (measured at 375px: 36.6px in Classic, 12.6px in Arcade, whose
   cabinet padding costs another ~24px). Trimming the per-cell gutters hands that space
   back to the labels without touching the locked column spec. The ⛶ maximize toggle is
   still the real answer for reading a 4-column card on a phone. */
@media (max-width: 430px) {
  .yzr-grid.yzr-tight .yzr-h,
  .yzr-grid.yzr-tight .yzr-v,
  .yzr-grid.yzr-tight .yzr-t { padding-left: 3px; }
  .yzr-grid.yzr-tight .yzr-sbcell { padding-left: 3px; padding-right: 3px; }
  .yzr-grid.yzr-tight .yzr-rl { padding-right: 3px; }
}
/* CONSTANT ROW HEIGHT — a scoreable cell must never be taller than the plain value cell
   it replaces. It was: the pill's own padding + border made the score rows ~3px taller, so
   the whole scorecard grew when your turn began and shrank when it ended, once per turn
   (owner's Android playtest, 2026-07-25). The label cell is the tallest cell in every row
   (its font clamps higher than the value font), so pinning the label/value line-height and
   capping the pill below that leaves the row height driven by the label alone — identical
   whether the row holds a pill, a number or an em dash. */
.yzr-rl, .yzr-v { line-height: 1.25; }
.yzr-sbcell { padding: 0 clamp(6px,1.5vw,9px); border-bottom: 1px solid var(--pgr-line, rgba(255,255,255,.075)); display: flex; align-items: center; justify-content: center; }
.yzr-sb {
  background: var(--pgr-selBg, rgba(29,158,117,.16)); color: var(--pgr-sel,#1D9E75);
  border: 1px solid var(--pgr-sel,#1D9E75); border-radius: 16px; padding: 0 8px;
  height: 20px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center; gap: 1px;
  font-size: 11px; font-weight: 700; cursor: pointer; font-family: var(--pgr-font,inherit); white-space: nowrap;
  transition: transform .12s cubic-bezier(.34,1.56,.64,1);
}
@media (hover: hover) { .yzr-sb:hover:not(:disabled) { transform: scale(1.08); } }
.yzr-sb:active:not(:disabled) { transform: scale(.91); }
.yzr-sb:disabled { opacity: .5; cursor: default; }
.yzr-sb .yzr-sb-bonus { font-size: 9px; opacity: .85; }
/* Post-match strip under the board. The old one was a bare .roll-btn, which is a black slab
   under an Arcade cabinet (owner's playtest, 2026-07-25) — this reads the same --pgr-* tokens
   the rest of the board does, so it themes with every palette in both layouts. */
.yzr-endbar {
  display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap;
  margin: clamp(8px,1.6vh,12px) auto 0; padding: 8px 12px; max-width: var(--pgr-shellMaxW, 560px);
  background: var(--pgr-panel, rgba(255,255,255,.04)); border: 1px solid var(--pgr-line, rgba(255,255,255,.075));
  border-radius: 14px; font-family: var(--pgr-font, inherit);
}
.yzr-endbar-note { font-size: clamp(12px,2.6vw,14px); color: var(--pgr-dim,#83858b); }
.yzr-endbar-btn {
  background: color-mix(in srgb, var(--pgr-sel,#1D9E75) 15%, transparent);
  color: var(--pgr-sel,#1D9E75); border: 1px solid var(--pgr-sel,#1D9E75);
  border-radius: 999px; padding: 7px 16px; font-size: clamp(12px,2.6vw,14px); font-weight: 700;
  font-family: inherit; cursor: pointer;
  /* Transform + colour only — never box metrics, so the strip can't resize under a thumb. */
  transition: transform .12s cubic-bezier(.34,1.56,.64,1), background .15s ease;
}
@media (hover: hover) { .yzr-endbar-btn:hover { background: color-mix(in srgb, var(--pgr-sel,#1D9E75) 45%, transparent); transform: translateY(-2px); } }
.yzr-endbar-btn:active { transform: scale(.97); }
.yzr-tl { padding: clamp(9px,2vw,13px) 6px 0 0; font-weight: 700; font-size: clamp(12px,2.7vw,15px); display: flex; align-items: center; }
.yzr-t {
  padding: clamp(9px,2vw,13px) 0 0; font-weight: 800; font-size: clamp(14px,3.2vw,20px);
  font-variant-numeric: tabular-nums; color: var(--pgr-dim,#83858b);
  display: flex; align-items: center; justify-content: center;
}
.yzr-t.me { color: var(--pgr-sel,#1D9E75); }

/* Kept for the two action flip-cards (.stbr-card) — the bet popup that first used these
   keyframes now lives in the shared BetPopup component below. */
@keyframes stbr-unfold { 0% { transform: perspective(2400px) rotateY(-90deg) scale(.93); opacity: 0; } 20% { opacity: 1; } 100% { transform: perspective(2400px) rotateY(0) scale(1); opacity: 1; } }
@keyframes stbr-fold   { 0% { transform: perspective(2400px) rotateY(0) scale(1); opacity: 1; } 80% { opacity: 1; } 100% { transform: perspective(2400px) rotateY(90deg) scale(.93); opacity: 0; } }

/* ─── BetPopup — shared "place your bet" popup (bet-popup.js) ───────────────────────
   Every wagered solo game (Shut the Box, Yahtzee, …) starts a round through this one
   component, so this block is the only place its look is defined. Consumes the --pgr-*
   tokens the component sets on .bp-overlay itself. Flips in over the blurred board
   (Quiz-style rotateY); .bp-blurred is what the game's board wears while it's open. */
.bp-blurred { filter: blur(7px) brightness(.62); pointer-events: none; }
.bp-overlay { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; padding: 20px; perspective: 2400px; }
.bp-popup {
  width: min(360px, 88vw); background: var(--pgr-solid,#1a1c22); color: var(--pgr-txt,#e9e8e4);
  border: var(--pgr-panelBd,1px solid var(--pgr-line,rgba(255,255,255,.12))); border-radius: 20px;
  box-shadow: 0 26px 60px rgba(0,0,0,.55); padding: clamp(18px,4vw,26px); text-align: center;
  display: flex; flex-direction: column; gap: 12px; transform-origin: 50% 50%;
  font-family: var(--pgr-font, inherit);
}
.bp-popup.bp-flip-in  { animation: bp-unfold .45s ease; }
.bp-popup.bp-flip-out { animation: bp-fold .45s ease forwards; }
@keyframes bp-unfold { 0% { transform: perspective(2400px) rotateY(-90deg) scale(.93); opacity: 0; } 20% { opacity: 1; } 100% { transform: perspective(2400px) rotateY(0) scale(1); opacity: 1; } }
@keyframes bp-fold   { 0% { transform: perspective(2400px) rotateY(0) scale(1); opacity: 1; } 80% { opacity: 1; } 100% { transform: perspective(2400px) rotateY(90deg) scale(.93); opacity: 0; } }
.bp-head { display: flex; flex-direction: column; gap: 6px; }
.bp-body { display: flex; flex-direction: column; gap: 12px; }
.bp-title { font-size: clamp(18px,4.5vw,22px); font-weight: 800; }
.bp-sub { font-size: 13px; color: var(--pgr-dim,#83858b); line-height: 1.4; }
.bp-popup input[type=number] {
  width: 100%; box-sizing: border-box; text-align: center; font-size: 22px; font-weight: 700;
  padding: 12px; border-radius: 12px; border: 1px solid var(--pgr-line,rgba(255,255,255,.15));
  background: var(--pgr-panel,rgba(255,255,255,.04)); color: var(--pgr-txt,#e9e8e4);
  font-family: var(--pgr-font, inherit);
}
.bp-chips { display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; }
/* Token-ised chips. The app-wide .bet-shortcuts buttons are hardcoded #111/#2e2e2e, which
   read as "the classic look leaked in" inside a themed popup — these are their own thing. */
.bp-chips button {
  flex: 1 1 0; min-width: 44px; padding: 6px 0; border-radius: 7px; cursor: pointer;
  background: var(--pgr-panel,#111); border: 1px solid var(--pgr-line,#2e2e2e);
  color: var(--pgr-dim,#888); font-family: var(--pgr-font, inherit); font-size: 13px; font-weight: 600;
  transition: border-color .15s, color .15s, background .15s;
}
@media (hover: hover) {
  .bp-chips button:hover { border-color: var(--pgr-accent,#555); color: var(--pgr-txt,#e8e8e8); }
}
.bp-chips button.bp-max-active { border-color: var(--pgr-sel,#1D9E75); color: var(--pgr-txt,#e8e8e8); }
.bp-chips button:disabled { opacity: .45; cursor: default; }
.bp-confirm {
  align-self: center; margin-top: 4px;
  min-width: clamp(140px,30vw,180px); height: clamp(48px,9vw,58px); border-radius: 14px;
  background: var(--pgr-btn,linear-gradient(180deg,#333841,#22252b)); color: var(--pgr-btnTxt,#e9e8e4);
  border: 1px solid var(--pgr-line,rgba(255,255,255,.12)); font-size: clamp(16px,3.4vw,19px); font-weight: 700;
  font-family: var(--pgr-font,inherit); cursor: pointer; display: flex; align-items: center; justify-content: center;
  gap: 8px; padding: 0 18px; transition: opacity .2s, transform .12s;
}
.bp-confirm:disabled { opacity: .45; cursor: default; }

/* ── Arcade: the same treatment as the end screen (.ges-arcade) ──────────────────────
   Flat --pgr-solid is near-black in every neon palette, so in Arcade the popup read as a
   classic-dark box in front of a neon board. Now: palette GRADIENT window, an accent-tinted
   hero band for the title (edge-to-edge, cut line, top corners clipped by the popup's
   overflow), padded body, outlined-accent buttons. Colour/transform only — the popup never
   resizes (same layout-lock rule as .ges-*). */
.bp-arcade .bp-popup { background: var(--pgr-bg,#141420); padding: 0 0 18px; gap: 14px; overflow: hidden; }
.bp-arcade .bp-head {
  padding: 20px 22px 16px; gap: 4px;
  background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 14%, transparent);
  border-bottom: 1px solid var(--pgr-line,rgba(255,255,255,.16));
}
.bp-arcade .bp-title { color: var(--pgr-accent,#e9e8e4); }
.bp-arcade .bp-body  { padding: 0 20px; }
.bp-arcade .bp-popup input[type=number] { background: var(--pgr-panel,rgba(255,255,255,.06)); border-color: rgba(255,255,255,.14); }
.bp-arcade .bp-chips button {
  background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 12%, var(--pgr-solid,#141420));
  border: 1px solid var(--pgr-accent,#60a5fa); color: var(--pgr-txt,#fff); border-radius: 8px;
}
@media (hover: hover) {
  .bp-arcade .bp-chips button:hover {
    background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 45%, var(--pgr-solid,#141420)); border-color: #fff;
  }
}
.bp-arcade .bp-chips button.bp-max-active { background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 45%, var(--pgr-solid,#141420)); border-color: #fff; }
.bp-arcade .bp-confirm {
  background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 30%, var(--pgr-solid,#141420));
  border: 2px solid var(--pgr-accent,#60a5fa); color: var(--pgr-txt,#fff);
}
@media (hover: hover) {
  .bp-arcade .bp-confirm:hover {
    background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 60%, var(--pgr-solid,#141420)); border-color: #fff;
  }
}

/* Personal-best card on the Yahtzee end screen (Y2). Rendered inside GameEndScreen's
   generic `html` section, so it inherits the shell's Classic/Arcade theming. */
.yzr-pb { display: flex; flex-direction: column; gap: 3px; align-items: center; text-align: center; font-size: 14px; }
.yzr-pb b { color: var(--pgr-txt,#e9e8e4); }
.yzr-pb span { font-size: 12.5px; color: var(--pgr-dim,#83858b); }
/* Gold literal, NOT var(--pgr-accent): in Classic --pgr-accent resolves to the same
   hairline value as --pgr-line (rgba(255,255,255,.075)), so accent-as-TEXT is invisible
   there — it only works as a text colour inside .bp-arcade/.ges-arcade. Gold is also just
   the right colour for a record line, and it reads on both shells. */
.yzr-pb-new { color: #f5c451; }

/* ─── ConfirmPopup — shared in-game "are you sure?" (confirm-popup.js) ─────────────
   Deliberately reuses .bp-overlay/.bp-popup/.bp-head/.bp-title (and so the flip animation
   and the whole .bp-arcade treatment) — it IS the bet popup's shell with a different body,
   which is the point: one themed popup object, not two that drift. Only the message block
   and the two-button row are its own. Replaces the native confirm(), which ignores the
   equipped layout entirely (owner call, 2026-07-25). */
.cp-text { font-size: 14px; line-height: 1.5; color: var(--pgr-dim,#b9bac0); }
.cp-text b { color: var(--pgr-txt,#e9e8e4); }
.cp-actions { display: flex; gap: 10px; justify-content: center; margin-top: 4px; }
.cp-btn {
  flex: 1 1 0; min-width: 0; height: clamp(44px,8.5vw,52px); border-radius: 12px;
  background: var(--pgr-btn,linear-gradient(180deg,#333841,#22252b)); color: var(--pgr-btnTxt,#e9e8e4);
  border: 1px solid var(--pgr-line,rgba(255,255,255,.12));
  font-size: clamp(14px,3vw,16px); font-weight: 700; font-family: var(--pgr-font,inherit);
  cursor: pointer; padding: 0 10px; transition: opacity .2s, border-color .15s, background .15s;
}
.cp-btn:disabled { opacity: .45; cursor: default; }
.cp-btn.cp-danger { border-color: #E24B4A; color: #ff8a89; }
@media (hover: hover) {
  .cp-btn:hover           { border-color: var(--pgr-accent,#555); }
  .cp-btn.cp-danger:hover { background: #E24B4A; color: #fff; border-color: #E24B4A; }
}
.bp-arcade .cp-btn {
  background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 12%, var(--pgr-solid,#141420));
  border: 2px solid var(--pgr-accent,#60a5fa); color: var(--pgr-txt,#fff);
}
.bp-arcade .cp-btn.cp-danger { background: color-mix(in oklch, #E24B4A 22%, var(--pgr-solid,#141420)); border-color: #E24B4A; color: #fff; }
@media (hover: hover) {
  .bp-arcade .cp-btn:hover           { background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 45%, var(--pgr-solid,#141420)); border-color: #fff; }
  .bp-arcade .cp-btn.cp-danger:hover { background: #E24B4A; border-color: #fff; }
}

/* ─── GameEndScreen — shared layout-aware game-over shell (game-end-screen.js) ─────
   Consumes the --pgr-* tokens the component sets on .ges-overlay itself (BoardSchemes +
   LayoutSchemes), so it themes to Classic (board scheme) or Arcade (neon cabinet) with no
   per-game markup. Classic = flat stacked sections; Arcade = each section in its own card. */
.ges-overlay { position: fixed; inset: 0; z-index: 90; display: flex; align-items: center; justify-content: center; padding: 20px; perspective: 2400px; background: rgba(0,0,0,.5); }
.ges-popup {
  width: min(400px, 92vw); background: var(--pgr-solid,#1a1c22); color: var(--pgr-txt,#e9e8e4);
  border: var(--pgr-panelBd,1px solid var(--pgr-line,rgba(255,255,255,.12))); border-radius: 22px;
  box-shadow: 0 26px 60px rgba(0,0,0,.6); padding: clamp(20px,5vw,30px); text-align: center;
  display: flex; flex-direction: column; gap: 12px; align-items: center; transform-origin: 50% 50%;
  max-height: 90vh; overflow-y: auto;
  font-family: var(--pgr-font, inherit);   /* Arcade → Poppins; Classic → its board font */
}
.ges-popup.ges-flip-in  { animation: ges-unfold .45s ease; }
.ges-popup.ges-flip-out { animation: ges-fold .45s ease forwards; }
@keyframes ges-unfold { 0% { transform: perspective(2400px) rotateY(-90deg) scale(.93); opacity: 0; } 20% { opacity: 1; } 100% { transform: perspective(2400px) rotateY(0) scale(1); opacity: 1; } }
@keyframes ges-fold   { 0% { transform: perspective(2400px) rotateY(0) scale(1); opacity: 1; } 80% { opacity: 1; } 100% { transform: perspective(2400px) rotateY(90deg) scale(.93); opacity: 0; } }
.ges-header { width: 100%; box-sizing: border-box; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.ges-emoji { font-size: 46px; line-height: 1; }
.ges-title { font-size: clamp(20px,5vw,26px); font-weight: 800; }
.ges-sub { font-size: 13.5px; color: var(--pgr-dim,#a9abb1); line-height: 1.55; }
.ges-sub b { color: var(--pgr-txt,#e9e8e4); }
.ges-pos { color: #1D9E75; font-weight: 700; }
.ges-neg { color: #E24B4A; font-weight: 700; }
.ges-body { width: 100%; display: flex; flex-direction: column; gap: 12px; }
.ges-body:empty { display: none; }
.ges-body .ur-xp-block, .ges-body .rb-table { width: 100%; }
.ges-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-top: 4px; width: 100%; }
/* A caveat about what an action DOES, under the buttons (first use: a craps
   tournament's rematch runs without a buy-in). */
.ges-foot-note { width: 100%; text-align: center; margin-top: 8px; font-size: 11px; line-height: 1.4; color: var(--pgr-dim, #83858b); }
.ges-actions .ges-btn { flex: 1 1 auto; min-width: 130px; }
.ges-btn {
  padding: 12px 16px; border-radius: 12px; border: 1px solid var(--pgr-line,rgba(255,255,255,.15));
  font-size: 15px; font-weight: 700; cursor: pointer; font-family: inherit;
  background: var(--pgr-panel,rgba(255,255,255,.06)); color: var(--pgr-txt,#e9e8e4);
  /* transform-only feedback → never reflows / resizes the popup (layout-locked, like quiz). */
  transition: transform .13s cubic-bezier(.34,1.56,.64,1), filter .13s ease, background .15s ease, border-color .15s ease;
  will-change: transform;
}
@media (hover: hover) { .ges-btn:hover { transform: translateY(-2px); filter: brightness(1.12); } }
.ges-btn:active { transform: translateY(1px) scale(.97); filter: none; }
.ges-btn--primary { background: var(--pgr-btn,var(--pgr-accent,#4caf50)); color: var(--pgr-btnTxt,#fff); border-color: transparent; }
/* Classic sections stack flat (a hairline between them); Arcade wraps each in a card. */
.ges-sec { width: 100%; }
.ges-sec + .ges-sec { border-top: 1px solid var(--pgr-line,rgba(255,255,255,.08)); padding-top: 12px; }
.ges-card {
  width: 100%; box-sizing: border-box; background: var(--pgr-panel,rgba(255,255,255,.05));
  border: 1px solid var(--pgr-line,rgba(255,255,255,.1)); border-radius: 16px; padding: 14px 14px 12px;
}
.ges-card-title, .ges-sec-title {
  font-size: 11px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--pgr-accent,var(--pgr-dim,#9aa)); margin-bottom: 8px; text-align: left;
}
/* Accent tone: Arcade neon title; Classic tints by outcome. */
.ges-arcade .ges-title { color: var(--pgr-accent,#e9e8e4); }
.ges-arcade .ges-emoji { filter: drop-shadow(0 0 10px color-mix(in oklch, var(--pgr-accent,#60a5fa) 60%, transparent)); }
.ges-classic .ges-popup[data-accent="win"] .ges-title { color: #1D9E75; }
.ges-classic .ges-popup[data-accent="lose"] .ges-title { color: #E24B4A; }

/* ── Arcade quiz-style treatment (mirrors the quiz .qz-end recipe) ──────────────
   Window uses the palette GRADIENT (not flat --pgr-solid), so the translucent cards
   read against it like the quiz screen. The header becomes a hero BAND: accent-tinted,
   spanning edge-to-edge with a flat bottom + cut line (its top corners clipped round by
   the popup's overflow:hidden). Sections carry their own padding since the popup itself
   goes padding-less. Buttons switch to the quiz outlined-accent look. */
.ges-arcade .ges-popup {
  background: var(--pgr-bg, #141420);
  padding: 0 0 16px; gap: 14px;
  overflow: hidden; overflow-y: auto;
}
.ges-arcade .ges-header {
  padding: 24px 24px 20px; gap: 6px;
  background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 14%, transparent);
  border-bottom: 1px solid var(--pgr-line, rgba(255,255,255,.16));
}
.ges-arcade .ges-body    { padding: 0 20px; }
.ges-arcade .ges-actions { padding: 0 20px; margin-top: 0; }
/* Cards: translucent panel over the gradient (like quiz rows) — soft hairline, not the
   neon --pgr-line border, so the gradient shows through cleanly. */
.ges-arcade .ges-card {
  background: var(--pgr-panel, rgba(255,255,255,.06));
  border-color: rgba(255,255,255,.08);
}
/* Outlined-accent buttons (quiz .primary-btn): an accent-tinted fill (visible, not near-black)
   + accent border. Hover fills bright + white border + lifts; active presses in. All transforms /
   colour changes only — the popup never resizes or shifts (owner: everything locked down). */
.ges-arcade .ges-btn {
  background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 15%, var(--pgr-solid,#141420));
  border: 2px solid var(--pgr-accent, #60a5fa); color: var(--pgr-txt, #fff);
}
.ges-arcade .ges-btn--primary { background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 30%, var(--pgr-solid,#141420)); }
@media (hover: hover) {
  .ges-arcade .ges-btn:hover {
    background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 60%, var(--pgr-solid,#141420));
    border-color: #fff; filter: none;   /* keeps the base translateY(-2px) lift, swaps brightness for a colour pop */
  }
}
.ges-arcade .ges-btn:active { filter: none; }   /* keeps the base translateY(1px) scale(.97) press */
/* Standings (MP; solo omits it) — same rows in both shells. */
.ges-standings { display: flex; flex-direction: column; gap: 6px; text-align: left; }
.ges-stand-row { display: grid; grid-template-columns: 26px 1fr auto; align-items: baseline; gap: 8px; font-size: 14px; color: var(--pgr-txt,#dcdcdc); }
.ges-stand-me { font-weight: 800; }
/* --pgr-sel, NOT --pgr-accent: in the Arcade shell `accent` IS the palette's bright "you"
   colour and this looked fine, but in Classic `--pgr-accent` resolves to `var(--pgr-line)`
   — a 10%-alpha HAIRLINE token — so the winner's own name rendered nearly invisible while
   every other row stayed readable (owner's playtest, 2026-07-26; charcoal/Classic). --pgr-sel
   is a real accent colour in BOTH shells (Arcade sets it to the same `you`, so the Arcade
   look is unchanged) and this row is shared by every game's end screen, not just Backgammon.
   Rule: never borrow a LINE token for TEXT. */
.ges-stand-me .ges-stand-n { color: var(--pgr-sel,#e9e8e4); }
.ges-stand-d { color: var(--pgr-dim,#9aa); font-size: 13px; }

/* ─── HistoryWindow — shared in-game history popup (history-window.js) ─────────────
   Same self-themed contract as .ges-*: the component sets the resolved --pgr-* tokens on
   .hw-overlay itself, so this themes to Classic (board scheme, flat sections) or Arcade
   (neon cabinet, each section a card) with no per-game markup. The footer (provably-fair
   line) is pinned at the bottom while the body scrolls. */
.hw-overlay { position: fixed; inset: 0; z-index: 92; display: flex; align-items: center; justify-content: center; padding: 20px; perspective: 2400px; background: rgba(0,0,0,.5); }
.hw-popup {
  width: min(430px, 94vw); max-height: 86vh; box-sizing: border-box;
  background: var(--pgr-solid,#1a1c22); color: var(--pgr-txt,#e9e8e4);
  border: var(--pgr-panelBd,1px solid var(--pgr-line,rgba(255,255,255,.12))); border-radius: 22px;
  box-shadow: 0 26px 60px rgba(0,0,0,.6); padding: clamp(16px,4vw,22px);
  display: flex; flex-direction: column; gap: 12px; transform-origin: 50% 50%;
  font-family: var(--pgr-font, inherit);   /* Arcade → Poppins; Classic → its board font */
}
.hw-popup.hw-flip-in  { animation: ges-unfold .45s ease; }
.hw-popup.hw-flip-out { animation: ges-fold .45s ease forwards; }
.hw-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex: 0 0 auto; }
.hw-title { font-size: clamp(16px,4vw,19px); font-weight: 800; }
.hw-close {
  width: 32px; height: 32px; flex: 0 0 auto; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--pgr-line,rgba(255,255,255,.15)); background: var(--pgr-panel,rgba(255,255,255,.06));
  color: var(--pgr-txt,#e9e8e4); font-size: 14px; font-family: inherit; line-height: 1;
  /* transform-only feedback → the popup never resizes (same layout-lock rule as .ges-btn) */
  transition: transform .13s cubic-bezier(.34,1.56,.64,1), filter .13s ease;
}
@media (hover: hover) { .hw-close:hover { transform: translateY(-2px); filter: brightness(1.15); } }
.hw-close:active { transform: scale(.94); filter: none; }
/* Body scrolls; header + footer stay put. */
.hw-body { flex: 1 1 auto; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }
.hw-sec { width: 100%; }
.hw-sec + .hw-sec { border-top: 1px solid var(--pgr-line,rgba(255,255,255,.08)); padding-top: 12px; }
.hw-card {
  width: 100%; box-sizing: border-box; background: var(--pgr-panel,rgba(255,255,255,.05));
  border: 1px solid rgba(255,255,255,.08); border-radius: 16px; padding: 12px 12px 10px;
}
.hw-sec-title {
  font-size: 11px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--pgr-accent,var(--pgr-dim,#9aa)); margin-bottom: 8px; text-align: left;
}
.hw-rows { display: flex; flex-direction: column; gap: 2px; }
.hw-row {
  display: flex; align-items: baseline; gap: 8px; text-align: left;
  padding: 6px 2px; font-size: 13.5px; line-height: 1.4;
}
.hw-row + .hw-row { border-top: 1px solid var(--pgr-line,rgba(255,255,255,.06)); }
.hw-row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.hw-row-label { color: var(--pgr-txt,#e9e8e4); }
.hw-row-sub { font-size: 11.5px; color: var(--pgr-dim,#9aa); }
.hw-row-right { flex: 0 0 auto; font-weight: 700; color: var(--pgr-txt,#e9e8e4); }
.hw-row-right.hw-pos { color: #1D9E75; }
.hw-row-right.hw-neg { color: #E24B4A; }
.hw-row-link { flex: 0 0 auto; font-size: 11.5px; color: var(--pgr-accent,#4caf50); text-decoration: none; }
@media (hover: hover) { .hw-row-link:hover { text-decoration: underline; } }
.hw-empty { font-size: 13px; color: var(--pgr-dim,#888); text-align: center; padding: .8rem 0; }
/* Provably-fair footer — pinned under the scrolling body. */
.hw-foot {
  flex: 0 0 auto; border-top: 1px solid var(--pgr-line,rgba(255,255,255,.1)); padding-top: 10px;
  font-size: 11.5px; line-height: 1.5; color: var(--pgr-dim,#9aa); text-align: center; word-break: break-all;
}
.hw-foot .hw-seed { color: var(--pgr-txt,#dcdcdc); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
/* Arcade: gradient window + accent-tinted header band, mirroring .ges-arcade. */
.hw-arcade .hw-popup { background: var(--pgr-bg, #141420); padding: 0; overflow: hidden; gap: 0; }
.hw-arcade .hw-header {
  padding: 16px 18px; background: color-mix(in oklch, var(--pgr-accent,#60a5fa) 14%, transparent);
  border-bottom: 1px solid var(--pgr-line, rgba(255,255,255,.16));
}
.hw-arcade .hw-title { color: var(--pgr-accent,#e9e8e4); }
.hw-arcade .hw-body { padding: 14px 16px; gap: 12px; }
.hw-arcade .hw-foot { margin: 0; padding: 12px 16px calc(14px + env(safe-area-inset-bottom, 0px)); }

/* STB MP — single-column stacked layout */
.stb-opp-strip {
  position: relative;
  display: flex; flex-direction: column; gap: 5px;
  padding: 8px 10px;
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 10px;
  margin-bottom: 10px;
}
.stb-opp-info {
  display: flex; align-items: center; gap: 6px;
}
.stb-opp-strip .face-wrap { font-size: 18px; }
.stb-opp-score {
  margin-left: auto; font-size: 12px; color: #aaa; white-space: nowrap;
}
.stb-opp-tiles {
  display: flex; gap: 4px;
}
.stb-opp-tiles .sb-tile {
  flex: 1; min-width: 0; font-size: 13px; padding: 4px 2px;
  border-radius: 6px; aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
}
.stb-my-section {
  max-width: 230px; margin: 4px auto 6px;
  box-sizing: border-box; padding: 8px 10px;
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 10px;
}
.stb-opp-strip.bust-flash, .stb-my-section.bust-flash { animation: mp-bust-flash .6s ease-out; }
.stb-opp-strip.bank-flash, .stb-my-section.bank-flash { animation: mp-bank-flash .6s ease-out; }
.stb-my-header {
  display: flex; flex-direction: row-reverse; align-items: center; gap: 8px;
  margin-bottom: 8px; position: relative;
}
.stb-my-tile-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
}
.stb-my-tile-grid .sb-tile {
  aspect-ratio: 1; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: clamp(20px, 7vw, 30px);
}
.stb-my-score {
  font-size: 13px; color: #aaa; text-align: center; margin-top: 5px;
}
.stb-center-text {
  font-size: 11px; color: #888; text-align: center; margin: 2px 0 4px;
}
.stb-opp-dice {
  display: flex; gap: 3px; font-size: 15px; margin-left: 4px;
}
/* STB MP action row: active button + banter side by side, centered */
.stb-action-bar {
  display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 10px;
}
.stb-action-btn {
  width: auto !important; padding: 12px 28px;
}
/* Keep STB MP dice as flex even on mobile (only 2 dice — grid would misalign them) */
#stb-mp-dice {
  display: flex !important; justify-content: center;
}

/* Jackpot MP — N-seat player list + 11-space board (3-6 players, dynamic) */
.jackpot-players {
  display: flex; flex-direction: column; gap: 6px;
  margin-bottom: 10px;
}
.jackpot-seat {
  display: flex; align-items: center; justify-content: space-between;
  padding: 7px 12px;
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 8px;
  font-size: 13px; transition: border-color .2s, opacity .2s;
}
.jackpot-seat.my-turn { border-color: #1D9E75; box-shadow: 0 0 0 1px #1D9E7544; }
.jackpot-seat.eliminated, .jackpot-seat.forfeited { opacity: .4; }
.jackpot-seat.eliminated .jackpot-seat-name, .jackpot-seat.forfeited .jackpot-seat-name { text-decoration: line-through; }
.jackpot-seat-name { font-weight: 500; }
.jackpot-seat-chips { color: #caa43d; font-weight: 600; white-space: nowrap; }
.jackpot-seat.bust-flash { animation: mp-bust-flash .6s ease-out; }
.jackpot-seat.bank-flash { animation: mp-bank-flash .6s ease-out; }

.jackpot-board {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px;
  margin-bottom: 8px;
}
.jackpot-space {
  aspect-ratio: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 8px;
  transition: border-color .2s, background .2s;
}
.jackpot-space.has-chips { border-color: #caa43d; background: #caa43d18; }
.jackpot-space.raid-eligible { border-color: #caa43d; }
.jackpot-space-num { font-size: 11px; color: #888; }
.jackpot-space-chips { font-size: clamp(14px, 4vw, 18px); font-weight: 600; color: #caa43d; }
.jackpot-space.just-acted { animation: jackpot-space-flash .6s ease-out; }
@keyframes jackpot-space-flash {
  0%   { transform: scale(1.18); border-color: #4caf50; }
  100% { transform: scale(1); }
}
.jackpot-raid-btn {
  width: auto !important; padding: 8px 14px !important; font-size: 13px !important;
}

/* LCR MP — N-seat player list (3-6 players, dynamic); no board, just chip counts */
.lcr-players {
  display: flex; flex-direction: column; gap: 6px;
  margin-bottom: 10px;
}
.lcr-seat {
  display: flex; align-items: center; justify-content: space-between;
  padding: 7px 12px;
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 8px;
  font-size: 13px; transition: border-color .2s, opacity .2s;
}
.lcr-seat.my-turn { border-color: #1D9E75; box-shadow: 0 0 0 1px #1D9E7544; }
.lcr-seat.forfeited { opacity: .4; }
.lcr-seat.forfeited .lcr-seat-name { text-decoration: line-through; }
.lcr-seat.out-of-chips { opacity: .65; }
.lcr-seat-name { font-weight: 500; }
.lcr-seat-chips { color: #caa43d; font-weight: 600; white-space: nowrap; }
.lcr-seat.bust-flash { animation: mp-bust-flash .6s ease-out; }
.lcr-seat.bank-flash { animation: mp-bank-flash .6s ease-out; }

/* Strike MP — N-seat player list (2-4 players), Global Die, clickable hand */
.strike-players {
  display: flex; flex-direction: column; gap: 6px;
  margin-bottom: 10px;
}
.strike-seat {
  display: flex; align-items: center; justify-content: space-between;
  padding: 7px 12px;
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 8px;
  font-size: 13px; transition: border-color .2s, opacity .2s;
}
.strike-seat.my-turn { border-color: #1D9E75; box-shadow: 0 0 0 1px #1D9E7544; }
.strike-seat.forfeited { opacity: .4; }
.strike-seat.forfeited .strike-seat-name { text-decoration: line-through; }
.strike-seat-name { font-weight: 500; }
.strike-seat-stats { color: #caa43d; font-weight: 600; white-space: nowrap; }
.strike-seat.bust-flash { animation: mp-bust-flash .6s ease-out; }
.strike-seat.bank-flash { animation: mp-bank-flash .6s ease-out; }

.strike-global-die-row {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin-bottom: 6px;
}
.strike-global-die-label { font-size: 12px; color: #888; }

.strike-die-selectable { cursor: pointer; transition: transform .15s; }
.strike-die-selectable:hover { transform: scale(1.12); }
.strike-die-picked {
  transform: scale(1.18);
  filter: drop-shadow(0 0 6px #1D9E75);
}

.strike-breakdown-player {
  background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 8px;
  padding: 4px 12px;
}

/* Narrow screens — topbar has up to 6 items (logo, balance/title, bell, store, profile,
   sign out/back); tighten spacing and drop the logo's text label (keep the emoji) so the
   sign-out/back button doesn't get pushed off-screen. */
@media (max-width: 480px) {
  .topbar { padding: calc(.6rem + env(safe-area-inset-top)) .85rem .6rem; gap: 6px; }
  .topbar-logo-text { display: none; }
  .topbar .signout-btn { padding: 5px 8px; font-size: 11px; }
  /* .notif-wrap sits near the left of the topbar, so anchoring the panel via
     "right" (relative to the wrap) pushes a near-viewport-width panel off the
     left edge — pin it to the viewport instead. */
  .notif-panel { position: fixed; top: 50px; left: 12px; right: 12px; width: auto; }
}

/* Build version tag (temporary, for dev cache-busting checks) */
.build-tag {
  position: fixed; bottom: 6px; right: 8px;
  font-size: 10px; color: #444; letter-spacing: .03em;
  pointer-events: none; z-index: 9998;
}

/* Admin (Predicely quiz) — internal tooling, reuses .card/.primary-btn/.signout-btn etc.
   from above, admin-prefixed classes are additions specific to these pages. */
.admin-wrap { padding: 1.5rem; max-width: 1100px; margin: 0 auto; }
.admin-nav { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 1.25rem; }
.admin-nav a {
  font-size: 13px; padding: 6px 12px; border-radius: 8px;
  border: 0.5px solid #2e2e2e; background: transparent;
  color: #888; text-decoration: none; transition: border-color .15s, color .15s;
}
.admin-nav a:hover, .admin-nav a.active { border-color: #555; color: #e8e8e8; }

.admin-section-title { font-size: 15px; font-weight: 600; margin-bottom: .75rem; }
.admin-stats-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 1.25rem; }
.admin-stat-cell { background: #1a1a1a; border: 0.5px solid #2e2e2e; border-radius: 10px; padding: .85rem 1rem; }
.admin-stat-val { font-size: 22px; font-weight: 600; }
.admin-stat-lbl { font-size: 12px; color: #888; margin-top: 2px; }

.admin-filter-bar { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 1rem; }
.admin-filter-bar select, .admin-filter-bar input[type=text] {
  padding: 7px 10px; background: #111; border: 0.5px solid #2e2e2e;
  border-radius: 8px; color: #e8e8e8; font-size: 13px;
}

.admin-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.admin-table th {
  text-align: left; color: #888; font-weight: 500; font-size: 11px;
  text-transform: uppercase; letter-spacing: .03em;
  padding: 8px 10px; border-bottom: 0.5px solid #2e2e2e;
}
.admin-table td { padding: 8px 10px; border-bottom: 0.5px solid #1e1e1e; vertical-align: top; }
.admin-table tr:last-child td { border-bottom: none; }
.admin-table tr:hover td { background: #ffffff06; }

.admin-badge {
  display: inline-block; font-size: 11px; padding: 2px 8px;
  border-radius: 999px; border: 0.5px solid #2e2e2e; color: #888;
}
.admin-badge.active { color: #1D9E75; border-color: #1D9E7555; }
.admin-badge.inactive { color: #888; }
.admin-badge.flagged { color: #E24B4A; border-color: #E24B4A55; }

.admin-row-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.admin-row-actions button {
  font-size: 11px; padding: 4px 9px;
  background: transparent; border: 0.5px solid #2e2e2e; border-radius: 6px;
  color: #888; cursor: pointer; transition: border-color .15s, color .15s, transform .05s, filter .05s;
}
.admin-row-actions button:hover { border-color: #555; color: #e8e8e8; }
.admin-row-actions button:active { transform: scale(.95); filter: brightness(.85); }
.admin-row-actions button.danger:hover { color: #E24B4A; border-color: #E24B4A; }

.admin-form-row { margin-bottom: 12px; }
.admin-form-row label { display: block; font-size: 12px; color: #888; margin-bottom: 4px; }
.admin-form-row input[type=text], .admin-form-row input[type=number],
.admin-form-row select, .admin-form-row textarea {
  width: 100%; padding: 9px 12px; background: #111; border: 0.5px solid #2e2e2e;
  border-radius: 8px; color: #e8e8e8; font-size: 14px; font-family: inherit;
}
.admin-form-row textarea { min-height: 70px; resize: vertical; }
.admin-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }

.admin-distractor-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.admin-distractor-row input { flex: 1; }

.admin-msg { font-size: 13px; padding: 8px 12px; border-radius: 8px; margin-bottom: 12px; }
.admin-msg.success { background: #1D9E7522; color: #1D9E75; }
.admin-msg.error { background: #E24B4A22; color: #E24B4A; }

.admin-config-key { font-size: 13px; font-weight: 600; margin-bottom: 2px; }
.admin-config-desc { font-size: 12px; color: #888; margin-bottom: 6px; }
.admin-config-row textarea {
  width: 100%; min-height: 60px; font-family: ui-monospace, monospace; font-size: 12px;
  background: #111; border: 0.5px solid #2e2e2e; border-radius: 8px; color: #e8e8e8; padding: 8px 10px;
}

/* ─── Bonespire (column dice duel) ──────────────────────────────────────────
   Ported from docs/design/Other Games/Rattlebones/ mockups (charcoal theme).
   Everything bsp- prefixed — keep it that way (cascade-collision history). */
@keyframes bsp-pop   { 0% { transform: scale(.8) translateY(4px); opacity: 0 } 55% { transform: scale(1.05) } 100% { transform: scale(1); opacity: 1 } }
@keyframes bsp-pulse { 0%, 100% { opacity: .45 } 50% { opacity: 1 } }
@keyframes bsp-shake { 10%, 90% { transform: translate3d(-1px,0,0) } 20%, 80% { transform: translate3d(2px,0,0) } 30%, 50%, 70% { transform: translate3d(-4px,0,0) } 40%, 60% { transform: translate3d(4px,0,0) } }
@keyframes bsp-fly-up   { 0% { transform: translateY(0) scale(1); opacity: 1 } 100% { transform: translateY(-90px) scale(.35); opacity: 0 } }
@keyframes bsp-fly-down { 0% { transform: translateY(0) scale(1); opacity: 1 } 100% { transform: translateY(90px) scale(.35); opacity: 0 } }
@keyframes bsp-slide { 0% { transform: translateY(var(--slideFrom, 0)) } 100% { transform: translateY(0) } }

.bsp-wrap {
  /* Board colour-scheme tokens (shared --pgr-* contract; charcoal defaults = today's
     look). applyBspTheme() overrides these inline per the equipped scheme; the board
     owns its background so a scheme recolours the whole table (zero change on charcoal). */
  --pgr-bg: radial-gradient(125% 120% at 50% -10%,#1c1e24 0%,#141519 58%,#0d0e11 100%);
  --pgr-seat: rgba(255,255,255,.025); --pgr-panel: rgba(255,255,255,.04);
  --pgr-line: rgba(255,255,255,.075); --pgr-txt: #e9e8e4; --pgr-dim: #83858b;
  --pgr-track: rgba(255,255,255,.07); --pgr-sel: oklch(.74 .14 152);
  --pgr-btn: linear-gradient(180deg,#333841,#22252b); --pgr-btnTxt: #e9e8e4;
  --pgr-bubbleBg: #2b2d33; --pgr-bubbleTxt: #f1f0ec; --pgr-selBg: oklch(.74 .14 152 / .16);
  width: 100%; display: flex; flex-direction: column; gap: clamp(8px, 1.6vh, 12px); margin: 0 auto;
  color: var(--pgr-txt);
  /* The board IS the layout shell. Classic = transparent, no chrome, merging into the
     game screen's full-bleed gradient (no seam); Arcade = the cabinet. Width lives on
     the seat-count modifiers below so Classic keeps its 420/460 columns. */
  background: var(--pgr-shellBg, transparent);
  border-radius: var(--pgr-shellRadius, 0);
  box-shadow: var(--pgr-shellShadow, none);
  padding: var(--pgr-shellPad, 0);
  box-sizing: border-box;
  font-family: var(--pgr-font, inherit);
}
.bsp-w2  { max-width: var(--pgr-shellMaxW, 420px); }
.bsp-w34 { max-width: var(--pgr-shellMaxW, 460px); }
.bsp-row { display: flex; gap: 10px; }
.bsp-row > .bsp-panel { flex: 1; min-width: 0; }

.bsp-panel { position: relative; display: flex; align-items: center; gap: 14px; background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 18px; padding: 12px 16px; }
/* Chat speech-bubble mount: overlays the panel box (out of flex flow) so the shared
   .pigr-bubble inside anchors above/below the whole card without adding a flex gap. */
.bsp-bubble-anchor { position: absolute; inset: 0; pointer-events: none; }
.bsp-panel-mini { gap: 10px; border-radius: 16px; padding: 10px 12px; }
.bsp-panel-you::after { content: ''; position: absolute; inset: 0; border-radius: inherit; border: 1.5px solid var(--seatcol); pointer-events: none; opacity: .5; }
.bsp-avatar { width: 44px; height: 44px; border-radius: 50%; background: var(--pgr-panel); border: 2px solid #888; display: flex; align-items: center; justify-content: center; font-size: 22px; flex: none; }
.bsp-panel-mini .bsp-avatar { width: 36px; height: 36px; font-size: 18px; }
.bsp-panel-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.bsp-panel-mini .bsp-panel-mid { gap: 2px; }
.bsp-panel-name { font-weight: 600; font-size: 15px; }
.bsp-panel-mini .bsp-panel-name { font-size: 13px; }
.bsp-thinking { font-size: 12px; font-weight: 600; animation: bsp-pulse 1s ease-in-out infinite; }
.bsp-panel-mini .bsp-thinking { font-size: 10px; }
.bsp-panel-sub { color: var(--pgr-dim); font-size: 12px; font-weight: 600; }
.bsp-panel-mini .bsp-panel-sub { font-size: 11px; }
.bsp-panel-score { flex: none; text-align: right; line-height: 1; }
.bsp-score { font-size: 26px; font-weight: 800; font-variant-numeric: tabular-nums; }
.bsp-panel-mini .bsp-score { font-size: 19px; }
.bsp-score-lbl { color: var(--pgr-dim); font-size: 10px; letter-spacing: .08em; font-weight: 700; }

.bsp-board { background: var(--pgr-panel); border: 1px solid var(--pgr-line); border-radius: 20px; padding: 16px 10px; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.bsp-w34 .bsp-board { padding: 14px 10px; }
.bsp-boardrow { display: flex; justify-content: center; gap: 10px; }
.bsp-boardrow-top { align-items: flex-end; }
.bsp-boardrow-bottom { align-items: flex-start; }
.bsp-gap { width: 12px; }

.bsp-frame { width: 100%; max-width: 220px; border: 2px solid transparent; border-radius: 16px; padding: 8px; transition: box-shadow .3s ease, border-color .3s ease; display: flex; flex-direction: column; gap: 8px; align-items: center; }
.bsp-frame-n { width: 152px; max-width: none; border-radius: 14px; padding: 6px; }
.bsp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; width: 100%; }
.bsp-frame-n .bsp-grid, .bsp-w34 .bsp-grid { gap: 8px; }
.bsp-w2 .bsp-labels { max-width: 220px; }
.bsp-collbl { text-align: center; font-size: 11px; font-weight: 700; opacity: .85; }
.bsp-frame-n .bsp-collbl { font-size: 10px; }

.bsp-colwrap { display: flex; flex-direction: column; gap: 8px; align-items: center; padding: 6px 0; border-radius: 12px; position: relative; transition: background .15s ease; }
.bsp-frame-n .bsp-colwrap { padding: 4px 0; border-radius: 10px; }
.bsp-col-up { flex-direction: column-reverse; }
.bsp-clickable { cursor: pointer; background: var(--pgr-panel); }

.bsp-slot { position: relative; flex: none; transition: box-shadow .3s ease; border-radius: 10px; }
.bsp-w2  .bsp-slot { width: 44px; height: 44px; }
.bsp-w34 .bsp-slot { width: 40px; height: 40px; }
.bsp-slot-empty { background: rgba(0,0,0,.28); border: 1px dashed var(--pgr-line); }
.bsp-symbol { color: var(--pgr-dim); opacity: .4; font-weight: 700; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; line-height: 1; }
.bsp-die { background: linear-gradient(150deg,#fcfaf5,#ece6d8); border: 1px solid rgba(0,0,0,.16); box-shadow: 0 3px 7px -3px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.8); display: grid; grid-template-columns: repeat(3,1fr); grid-template-rows: repeat(3,1fr); flex: none; }

.bsp-msg { text-align: center; color: var(--pgr-dim); font-size: 13px; font-weight: 500; min-height: 1.3em; padding: 0 6px; }
.bsp-controls { height: 60px; display: flex; align-items: center; justify-content: center; }
.bsp-rollbtn { width: 100%; height: 56px; border-radius: 15px; border: 1px solid var(--pgr-line); background: var(--pgr-btn); color: var(--pgr-btnTxt); font-family: inherit; font-size: 18px; font-weight: 700; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 9px; transition: transform .13s cubic-bezier(.34,1.56,.64,1), filter .13s ease; }
.bsp-rollbtn:hover  { transform: translateY(-2px); filter: brightness(1.08); }
.bsp-rollbtn:active { transform: translateY(2px) scale(.96); filter: brightness(.94); }
.bsp-pending { display: flex; align-items: center; justify-content: center; gap: 14px; width: 100%; }
.bsp-pending span { font-size: 14px; font-weight: 600; color: var(--pgr-dim); }
.bsp-waiting { width: 100%; height: 56px; border-radius: 15px; border: 1px solid var(--pgr-line); background: var(--pgr-panel); color: var(--pgr-dim); font-size: 14px; font-weight: 600; display: flex; align-items: center; justify-content: center; animation: bsp-pulse 1.4s ease-in-out infinite; }

/* Setup card */
.bsp-setup-card { max-width: 420px; margin: 0 auto; }
.bsp-seg { display: flex; gap: 8px; margin: .35rem 0 1rem; }
.bsp-seg button { flex: 1; padding: 12px 0; border-radius: 12px; border: 1px solid #2e2e2e; background: #161616; color: #e8e8e8; font-size: 15px; font-weight: 600; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; }
.bsp-seg button:hover { border-color: #555; }
.bsp-seg .bsp-seg-active { border-color: oklch(.62 .17 250); box-shadow: 0 0 0 1px oklch(.62 .17 250 / .5), 0 0 12px 0 oklch(.62 .17 250 / .25); }
.bsp-seg small { display: block; font-weight: 500; font-size: 11px; color: #888; margin-top: 2px; }

/* Result modal */
.bsp-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); display: flex; align-items: center; justify-content: center; z-index: 50; animation: bsp-pop .3s ease; }
.bsp-modal { background: #17181c; border: 1px solid rgba(255,255,255,.1); border-radius: 20px; padding: 28px 26px; display: flex; flex-direction: column; align-items: center; gap: 14px; text-align: center; max-width: 320px; box-shadow: 0 20px 60px rgba(0,0,0,.5); }
.bsp-modal-title { font-size: 22px; font-weight: 800; }
.bsp-modal-sub { color: #83858b; font-size: 14px; font-weight: 600; }
.bsp-modal button { height: 48px; padding: 0 22px; border-radius: 13px; border: 1.5px solid oklch(.74 .14 152); background: rgba(255,255,255,.04); color: #e8e8e8; font-family: inherit; font-size: 15px; font-weight: 700; cursor: pointer; }
.bsp-modal button:hover { filter: brightness(1.15); }
.bsp-modal .bsp-modal-alt { border-color: #2e2e2e; color: #aaa; font-weight: 600; }

/* ─── Royal Game of Ur (ur.js) ─────────────────────────────────────────────
   Keyframes copied verbatim from the mockup (docs/design/Other Games/
   RoyalGameOfUr/Royal game of UR Theme3.dc.html). Names are Ur-unique
   (checked: no collisions with existing keyframes) so they stay unprefixed
   to match the ported inline styles. */
@keyframes hopBounce{0%{transform:translateY(0) scale(1)}30%{transform:translateY(-18px) scale(1.06)}60%{transform:translateY(2px) scale(.96)}100%{transform:translateY(0) scale(1)}}
@keyframes rollPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.05)}}
@keyframes glowPulse{0%,100%{box-shadow:0 0 0 3px #ffffff inset, 0 0 0 3px var(--glow-c), 0 0 10px 2px var(--glow-c)}50%{box-shadow:0 0 0 3px #ffffff inset, 0 0 0 3px var(--glow-c), 0 0 4px 0px var(--glow-c)}}
@keyframes rolledPop{0%{transform:scale(1)}40%{transform:scale(1.12)}100%{transform:scale(1)}}
@keyframes tileGlow{0%,100%{opacity:0}50%{opacity:.5}}
@keyframes attackShake{0%,100%{transform:translateX(0)}20%{transform:translateX(-4px)}40%{transform:translateX(4px)}60%{transform:translateX(-3px)}80%{transform:translateX(2px)}}
@keyframes boardShake{0%,100%{transform:translateX(0)}15%{transform:translateX(-7px)}30%{transform:translateX(6px)}45%{transform:translateX(-5px)}60%{transform:translateX(4px)}75%{transform:translateX(-2px)}90%{transform:translateX(1px)}}
@keyframes rosetteRing{0%{transform:scale(.3);opacity:.9;border-width:5px}100%{transform:scale(2);opacity:0;border-width:1px}}
@keyframes rosetteSpark{0%{opacity:1;transform:translate(0,0) scale(1)}100%{opacity:0;transform:translate(var(--tx),var(--ty)) scale(.2)}}
@keyframes rosetteBannerPop{0%{transform:scale(1)}30%{transform:scale(1.08)}60%{transform:scale(.97)}100%{transform:scale(1)}}
@keyframes lineGrow{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}
@keyframes arrowFade{0%{opacity:0;transform:scale(.4)}100%{opacity:1;transform:scale(1)}}

/* Roll button hover/active (mockup's Classic style-hover / style-active) */
.ur-rollbtn-live:hover{ transform:translateY(-2px); filter:brightness(1.08); }
.ur-rollbtn-live:active{ transform:translateY(2px) scale(.96); filter:brightness(.94); }

/* Game-over XP block — first use of the XP UI pattern (Quiz will copy this). */
.ur-xp-block { width: 100%; display: flex; flex-direction: column; align-items: center; gap: 6px; margin: 4px 0 2px; }
.ur-xp-gain { font-size: 20px; font-weight: 800; color: oklch(.74 .14 152); }
.ur-xp-level { font-size: 13px; font-weight: 700; color: #b9b8b3; }
.ur-xp-levelup { font-size: 14px; font-weight: 800; color: #facc15; letter-spacing: .3px; animation: rolledPop .5s ease; }
.ur-xp-bar { width: 100%; height: 10px; border-radius: 6px; background: rgba(255,255,255,.08); overflow: hidden; }
.ur-xp-fill { height: 100%; border-radius: 6px; background: linear-gradient(90deg, oklch(.62 .17 250), oklch(.74 .14 152)); transition: width .6s cubic-bezier(.34,1.56,.64,1); }
.ur-xp-next { font-size: 11px; font-weight: 600; color: #83858b; }

/* Reward breakdown — shared itemized game-over table (RewardBreakdown, reward-breakdown.js).
   Two columns (XP · 🪙); rows reveal with a stagger, the Total counts up. Self-contained
   colours (no quiz --qz-* vars) so it works on the lobby games' game-over screens. */
.rb-table { width: 100%; margin-bottom: 10px; }
.rb-row { display: grid; grid-template-columns: 1fr 62px 62px; align-items: baseline; gap: 6px;
  padding: 4px 2px; transition: opacity .3s ease, transform .3s ease; }
.rb-l { text-align: left; }
.rb-xp, .rb-c { text-align: right; font-variant-numeric: tabular-nums; font-weight: 700; }
.rb-head { color: #83858b; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; padding-bottom: 0; }
.rb-row:not(.rb-head):not(.rb-total) { color: #cfd0d3; font-size: 14px; }
.rb-row .rb-xp { color: oklch(.74 .14 152); }
.rb-row .rb-c  { color: #ffd54a; }
.rb-neg .rb-c { color: #ff6b6b; }
.rb-pos .rb-c { color: #7dd37d; }
.rb-pot .rb-c { color: #ffd54a; }
.rb-total { border-top: 1px solid rgba(255,255,255,.14); margin-top: 4px; padding-top: 8px;
  color: #f3f3f3; font-weight: 800; }
.rb-total .rb-l { font-size: 15px; }
.rb-total .rb-xp { font-size: 18px; color: oklch(.74 .14 152); }
.rb-total .rb-c  { font-size: 18px; color: #ffd54a; }
.rb-hidden { opacity: 0; transform: translateY(6px); }
.rb-bump { display: inline-block; animation: rolledPop .3s ease; }
@media (prefers-reduced-motion: reduce) { .rb-hidden { transform: none; } }

/* Match superlative cards (Slice 4) — under the reward table, above the level line. */
.rb-awards { display: flex; flex-direction: column; gap: 4px; margin: 2px 0 10px; }
.rb-award { display: flex; align-items: baseline; gap: 8px; padding: 5px 9px;
  background: rgba(255,255,255,.06); border-radius: 8px; font-size: 13px;
  transition: opacity .3s ease, transform .3s ease; }
.rb-award-l { color: #cfd0d3; white-space: nowrap; }
.rb-award-h { color: #f3f3f3; font-weight: 800; margin-left: auto; text-align: right;
  overflow: hidden; text-overflow: ellipsis; }
.rb-award-v { color: #83858b; font-size: 12px; white-space: nowrap; }
/* Conditional (threshold-tripped) awards — the earned ones get a golden tint. */
.rb-award--cond { background: rgba(255, 213, 74, .10); box-shadow: inset 0 0 0 1px rgba(255, 213, 74, .22); }

/* Full-bleed board-scheme page: a game screen whose whole viewport carries the
   equipped scheme's --pgr-bg gradient (set inline by applyPigTheme/applyBspTheme/Ur),
   so the themed table fills the screen and the board merges into it. Scoped to the
   game-screen div, so it's self-clearing — leaving the game (display:none) hides it,
   and other screens keep their own background. The topbar blends into the gradient. */
.board-scheme-page { min-height: 100vh; }
.board-scheme-page .topbar { background: transparent; border-bottom-color: rgba(255,255,255,.08); }

/* ── Pig — classic-race V2 shell (docs/design/Other Games/Pig V2) ────────────── */
/* Charcoal tokens are the defaults; applyPigTheme() overrides them inline from the
   equipped board scheme (BoardSchemes / frontend/board-schemes.js). These defaults
   MUST match charcoal exactly so a player who never picks a scheme sees today's board. */
.pigr-root {
  --pgr-bg: radial-gradient(125% 120% at 50% -10%,#1c1e24 0%,#141519 58%,#0d0e11 100%);
  --pgr-seat: rgba(255,255,255,.025); --pgr-panel: rgba(255,255,255,.04);
  --pgr-line: rgba(255,255,255,.075); --pgr-txt: #e9e8e4; --pgr-dim: #83858b;
  --pgr-track: rgba(255,255,255,.07); --pgr-sel: oklch(.74 .14 152);
  --pgr-btn: linear-gradient(180deg,#333841,#22252b); --pgr-btnTxt: #e9e8e4;
  --pgr-bubbleBg: #2b2d33; --pgr-bubbleTxt: #f1f0ec; --pgr-selBg: oklch(.74 .14 152 / .16);
  margin: 0 auto; display: flex; flex-direction: column;
  gap: clamp(10px,2vh,16px); color: var(--pgr-txt);
  /* The board IS the layout shell (Pig + Farkle share this root). Classic = the 560px
     transparent column it has always been, merging into the game screen's full-bleed
     gradient (no seam); Arcade = the 480px cabinet. All four values come from the
     layout scheme, so the switch is pure data — see PIGR_CLASSIC_SHELL. */
  max-width: var(--pgr-shellMaxW, 560px);
  background: var(--pgr-shellBg, transparent);
  border-radius: var(--pgr-shellRadius, 0);
  box-shadow: var(--pgr-shellShadow, none);
  padding: var(--pgr-shellPad, 0);
  box-sizing: border-box;
  font-family: var(--pgr-font, inherit);
}
.pigr-top { display: flex; flex-direction: column; gap: clamp(10px,2vh,16px); }

.pigr-seat { position: relative; background: var(--pgr-seat); border: 1px solid var(--pgr-line); border-radius: 20px; padding: clamp(12px,2.4vw,16px) clamp(16px,3vw,22px); }
.pigr-seat-active { background: var(--pgr-panel); }
.pigr-seat-glow { position: absolute; inset: 0; border-radius: 20px; border: 1.5px solid var(--pgr-sel); pointer-events: none; }
.pigr-seat-main { position: relative; z-index: 2; display: flex; align-items: center; gap: clamp(12px,3vw,20px); }
.pigr-avatar { position: relative; flex: none; width: clamp(44px,8.5vw,54px); height: clamp(44px,8.5vw,54px); border-radius: 50%; background: var(--pgr-panel); border: 1px solid var(--pgr-line); display: flex; align-items: center; justify-content: center; font-size: clamp(22px,4.6vw,28px); }
.pigr-seat-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 9px; }
.pigr-seat-namerow { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.pigr-name { font-weight: 600; font-size: clamp(15px,3vw,17px); }
.pigr-note { color: var(--pgr-dim); font-size: clamp(11px,2.3vw,12px); }
.pigr-tag { font-weight: 700; font-size: clamp(11px,2.2vw,13px); }
.pigr-tag-green { color: var(--pgr-sel); }
.pigr-tag-red { color: #ff4a4a; }
.pigr-progress { height: 7px; border-radius: 99px; background: var(--pgr-track); overflow: hidden; }
.pigr-progress-fill { height: 100%; border-radius: 99px; background: var(--pgr-sel); transition: width .4s cubic-bezier(.22,1,.36,1); }
.pigr-seat-score { flex: none; display: flex; flex-direction: column; align-items: flex-end; line-height: 1; }
/* Score size is CONSTANT across active/inactive so the card never resizes on a turn
   change — the active seat is signalled by its glow + panel bg, not by growing. */
.pigr-score { font-size: clamp(28px,7vw,38px); font-weight: 800; font-variant-numeric: tabular-nums; transition: transform .18s cubic-bezier(.34,1.56,.64,1); }
.pigr-score-pop { transform: scale(1.22); color: var(--pgr-sel); }
.pigr-score-sub { color: var(--pgr-dim); font-size: clamp(11px,2.3vw,13px); }
/* Trail height is always reserved (even with 0 dice) so build-up dice appearing or
   busting away never grow/shrink the card. */
.pigr-trail { position: relative; z-index: 2; display: flex; gap: 5px; align-items: center; min-height: 28px; margin-top: 10px; flex-wrap: wrap; }
.pigr-trail-die { display: inline-flex; will-change: transform, opacity; }
.pigr-die-bust { animation: pigr-die-red .5s ease both; }
.pigr-die-bank { animation: pigr-die-green .3s ease both; }

/* Instant (first-roll) bust: shake the whole card + flash it red for a bit of kick. */
.pigr-seat-hit { animation: pigr-hit .5s ease both; }

/* On-fire aura — a layered overlay above the seat bg but BELOW content, so it never
   touches the card's own border (custom borders stay visible under a hot streak). */
.pigr-seat-fire { position: absolute; inset: -1px; border-radius: 20px; pointer-events: none; z-index: 1;
  box-shadow: 0 0 0 1.5px rgba(255,150,40,.9), 0 0 22px 2px rgba(255,120,20,.55); animation: pigr-fire 1.1s ease-in-out infinite; }
/* Floating pill on the card's top edge — OUT of the name-row flow, so a hot streak
   igniting never wraps the name row and grows the card (the "streak resizes the
   window" jank). transform is set here (not in the keyframe, which only pulses
   opacity) so it stays centred. */
.pigr-fire-badge { position: absolute; top: -11px; left: 50%; transform: translateX(-50%); z-index: 5;
  padding: 3px 12px; border-radius: 999px; background: linear-gradient(180deg,#ffab45,#f0641e); color: #1a1205;
  font-weight: 800; font-size: clamp(10px,2.1vw,12px); letter-spacing: .04em; white-space: nowrap;
  box-shadow: 0 4px 14px -3px rgba(255,110,20,.7); animation: pigr-fire-badge 1.1s ease-in-out infinite; }

.pigr-bubble { position: absolute; left: -4px; background: var(--pgr-bubbleBg); color: var(--pgr-bubbleTxt); border-radius: 18px; padding: 11px 17px; font-weight: 600; font-size: clamp(14px,3vw,18px); white-space: nowrap; box-shadow: 0 14px 30px -10px rgba(0,0,0,.55); z-index: 60; animation: pigr-pop .26s cubic-bezier(.34,1.56,.64,1); }
.pigr-bubble-up { bottom: calc(100% + 14px); border-bottom-left-radius: 6px; }
.pigr-bubble-down { top: calc(100% + 14px); border-top-left-radius: 6px; }
.pigr-bubble-tail { position: absolute; left: 16px; width: 12px; height: 12px; background: var(--pgr-bubbleBg); border-radius: 3px; transform: rotate(45deg); }
.pigr-bubble-up .pigr-bubble-tail { bottom: -5px; }
.pigr-bubble-down .pigr-bubble-tail { top: -5px; }

.pigr-center { position: relative; display: flex; flex-direction: column; align-items: center; gap: clamp(8px,1.6vh,14px); padding: clamp(4px,1.4vh,12px) 0; perspective: 1100px; }
/* The active turn lives in its own bordered card. On every turn change it does a
   two-sided flip (quiz-popup feel) — never an instant swap: a ghost clone of the
   OUTGOING card flips away (.pigr-flipout) while this live card, repainted for the
   new turn, flips in (.pigr-flipin). transform-origin/backface keep the rotateY clean. */
.pigr-center-card { display: flex; flex-direction: column; align-items: center; gap: clamp(8px,1.6vh,14px);
  padding: clamp(12px,2.2vh,18px) clamp(22px,7vw,40px); border: 1px solid var(--pgr-line); border-radius: 22px;
  background: var(--pgr-panel); transform-origin: center center; backface-visibility: hidden; will-change: transform, opacity; }
.pigr-flipin { animation: pigr-cardflip .42s cubic-bezier(.34,1.05,.55,1) both; }
.pigr-flipout { animation: pigr-cardflipout .24s cubic-bezier(.5,0,1,1) both; }
.pigr-turnlabel { font-size: clamp(11px,2.4vw,13px); letter-spacing: .2em; color: var(--pgr-dim); font-weight: 700; }
.pigr-center-die { animation: pigr-float 3.2s ease-in-out infinite; min-height: 88px; display: flex; align-items: center; justify-content: center; }
.pigr-center-idle { font-size: 74px; line-height: 1; }
.pigr-tt-row { display: flex; align-items: baseline; gap: 10px; }
.pigr-tt-lbl { color: var(--pgr-dim); font-size: clamp(10px,2.2vw,12px); font-weight: 700; letter-spacing: .14em; }
.pigr-tt-val { font-size: clamp(34px,9vw,48px); font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; }
/* Two lines are ALWAYS reserved so the roll/bust message changing length never pushes
   the card or controls (the mobile "everything snaps up on a bust" jank). */
.pigr-msg { color: var(--pgr-dim); font-size: clamp(13px,2.8vw,15px); text-align: center; line-height: 1.32;
  min-height: 2.64em; display: flex; align-items: center; justify-content: center; padding: 0 6px; font-weight: 500; }

/* Roll-bonus variant panel — "Bonus +N" + quiz-style tier dots (2 small + 1 big).
   Colour escalates warm with the tier (dim → gold → ember → red-hot). */
.pigr-bonus { display: flex; align-items: center; gap: 10px; padding: 5px 12px; border-radius: 999px;
  background: rgba(255,170,60,.10); border: 1px solid rgba(255,170,60,.28); --bdot: #d9a441; }
.pigr-bonus-amt { font-weight: 800; font-size: clamp(13px,3vw,15px); color: var(--bdot); letter-spacing: .01em; }
.pigr-bonus-dots { display: inline-flex; align-items: center; gap: 6px; }
.pigr-bdot { width: 8px; height: 8px; border-radius: 50%; background: transparent; border: 2px solid var(--bdot); opacity: .5; transition: all .2s ease; }
.pigr-bdot-big { width: 13px; height: 13px; border-width: 2.5px; }
.pigr-bdot-on { background: var(--bdot); opacity: 1; box-shadow: 0 0 8px var(--bdot); }
.pigr-bonus-tier { font-size: clamp(10px,2.2vw,12px); font-weight: 600; color: var(--pgr-dim); }
.pigr-bonus.pigr-bonus-t1 { --bdot: #d9a441; }
.pigr-bonus.pigr-bonus-t2 { --bdot: #f0a020; background: rgba(240,160,32,.12); border-color: rgba(240,160,32,.34); }
.pigr-bonus.pigr-bonus-t3 { --bdot: #ff7a2f; background: rgba(255,122,47,.14); border-color: rgba(255,122,47,.4); }
.pigr-bonus.pigr-bonus-t4 { --bdot: #ff4d4d; background: rgba(255,77,77,.16); border-color: rgba(255,77,77,.46); box-shadow: 0 0 14px rgba(255,77,77,.3); }

.pigr-controls { display: flex; gap: clamp(10px,2.5vw,14px); align-items: stretch; margin-top: clamp(2px,1vh,6px); }
/* Standardised click feel (matches the Bonespire buttons): gradient depth + a shadow
   that reads as a raised key, a hover lift, and a press that sinks + dims. */
.pigr-btn { height: clamp(54px,10vw,62px); border-radius: 15px; font-family: inherit; font-size: clamp(16px,3.4vw,19px); font-weight: 700; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 9px; transition: transform .11s cubic-bezier(.34,1.56,.64,1), filter .11s ease, box-shadow .11s ease, opacity .28s ease; will-change: transform; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
/* Solid raised "key" like the Bonespire buttons (the faint white-over-panel gradient
   read as flat). A 5px bottom shadow is the key's depth; hover lifts it (shadow grows +
   accent glow), press slams it flat to the surface + dims for an unmistakable click. */
.pigr-btn:active { transform: translateY(5px) scale(.955); filter: brightness(.8); box-shadow: 0 0 0 rgba(0,0,0,.35) !important; }
.pigr-btn:disabled { opacity: .38; cursor: default; box-shadow: none; filter: saturate(.6); transform: none; }
.pigr-btn-bank, .pigr-btn-roll { flex: 1; border: 1.5px solid var(--pgr-sel); background: var(--pgr-btn); color: var(--pgr-btnTxt); box-shadow: 0 5px 0 rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.1); }
.pigr-btn-bank:not(:disabled):hover, .pigr-btn-roll:not(:disabled):hover { transform: translateY(-3px); filter: brightness(1.18); box-shadow: 0 8px 0 rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.14), 0 0 20px -4px var(--pgr-sel); }
.pigr-btn-chat { flex: none; width: clamp(54px,10vw,62px); border: 1px solid var(--pgr-line); background: var(--pgr-btn); color: var(--pgr-btnTxt); font-size: 22px; box-shadow: 0 5px 0 rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.06); }
.pigr-btn-chat:not(:disabled):hover { transform: translateY(-3px); filter: brightness(1.18); box-shadow: 0 8px 0 rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.09); }
/* The live (rollable) button gets a gentle accent pulse so "it's your move" is obvious
   at a glance — the recurring "am I even able to act?" confusion. Paused on hover/press
   so the direct-interaction feedback reads cleanly over the idle pulse. */
.pigr-btn-live { animation: pigr-btn-pulse 1.7s ease-in-out infinite; }
.pigr-btn-live:hover, .pigr-btn-live:active { animation: none; }
@keyframes pigr-btn-pulse {
  0%,100% { box-shadow: 0 5px 0 rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.1), 0 0 10px -4px var(--pgr-sel); }
  50%     { box-shadow: 0 5px 0 rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.1), 0 0 22px 0 var(--pgr-sel); }
}
/* Controls flip in/out as a whole bar on turn changes (quiz-popup feel), never an
   instant swap. `visibility:hidden` (not display) keeps the row's height reserved so
   the layout never jumps. perspective() lives in the transform so one element suffices. */
.pigr-controls-hidden { visibility: hidden; opacity: 0; pointer-events: none; }
.pigr-ctrl-flipin  { animation: pigr-ctrl-flipin .42s cubic-bezier(.34,1.05,.55,1) both; transform-origin: center; }
.pigr-ctrl-flipout { animation: pigr-ctrl-flipout .24s cubic-bezier(.5,0,1,1) both; transform-origin: center; }
@keyframes pigr-ctrl-flipin  { 0% { transform: perspective(900px) rotateY(90deg) scale(.96); opacity: 0; } 45% { opacity: .35; } 100% { transform: perspective(900px) rotateY(0) scale(1); opacity: 1; } }
@keyframes pigr-ctrl-flipout { 0% { transform: perspective(900px) rotateY(0) scale(1); opacity: 1; } 100% { transform: perspective(900px) rotateY(-90deg) scale(.96); opacity: 0; } }
/* Optimistic pre-roll motion on the center die: kicked instantly on a roll tap so the
   roundtrip never feels dead, replaced by the real DieRenderer tumble when it lands.
   More specific than .pigr-center-die's float, so it wins for the brief window. */
.pigr-center-die.pigr-die-prespin { animation: pigr-die-prespin .3s ease-in-out infinite; }
@keyframes pigr-die-prespin { 0% { transform: rotate(-14deg) scale(.92); } 50% { transform: rotate(14deg) scale(.92); } 100% { transform: rotate(-14deg) scale(.92); } }

.pigr-forfeit { display: block; text-align: center; margin-top: 6px; color: var(--pgr-dim); font-size: clamp(12px,2.4vw,14px); cursor: pointer; opacity: .8; }
.pigr-forfeit:hover { opacity: 1; }

/* ── Farkle race (.fkr-*) — the center dice row on the shared .pigr-* shell ── */
.fkr-dice-wrap { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: clamp(7px,2vw,12px); min-height: 62px; }
.fkr-live { display: inline-flex; gap: clamp(7px,2vw,12px); }
/* No per-die padding: the rolling dice tumble into a bare #fkr-dice-live container
   (DieRenderer owns its innerHTML, no .fkr-die wrapper), so any padding here would
   make the settled/ghost dice WIDER than the tumbling ones — the centered row then
   shifts on settle and shoves the set-aside keepers sideways. Zero padding keeps
   ghost/rolling/settled all the same footprint, so the dice never move. Gap comes
   from the flex container; the held ring is box-shadow (outside the layout box). */
.fkr-die { display: inline-flex; border-radius: 13px; padding: 0; transition: transform .12s ease, box-shadow .15s ease; will-change: transform, opacity; }
.fkr-die-live { cursor: pointer; }
.fkr-die-live:hover { transform: translateY(-3px); }
/* Held (selected to set aside): green ring + the design's bop */
.fkr-die-held { box-shadow: 0 0 0 2px var(--pgr-sel), 0 0 14px -2px var(--pgr-sel); animation: fkr-bop 1.18s ease-in-out infinite; }
/* Already set aside this turn: small, dimmed keepers */
.fkr-die-aside { opacity: .45; filter: saturate(.7); }
/* Bust flash, Farkle edition. The shared pigr-die-red is tuned for Pig's TINY trail
   dice, where the red drop-shadow glow carries the moment; on Farkle's big 46px faces
   the face area dominates and sepia+saturate CANNOT redden a near-white die (sepia on
   white → pale cream, saturate amplifies it to YELLOW). Pre-darkening with brightness(<1)
   before sepia gives the white face real chroma, so hue-rotate lands on true red. Hue
   held constant across every keyframe (only saturate ramps) so no frame passes through
   yellow. Scoped to .fkr-die so Pig's approved red is untouched. */
.fkr-die.pigr-die-bust { animation-name: fkr-die-red; }
@keyframes fkr-die-red {
  0%   { filter: grayscale(1) brightness(.58) sepia(1) saturate(4) hue-rotate(-50deg) drop-shadow(0 0 0 #ff1010); }
  22%  { filter: grayscale(1) brightness(.66) sepia(1) saturate(9) hue-rotate(-50deg) drop-shadow(0 0 13px #ff1414); }
  60%  { filter: grayscale(1) brightness(.6) sepia(1) saturate(8) hue-rotate(-50deg) drop-shadow(0 0 9px #ff1a1a); }
  100% { filter: grayscale(1) brightness(.6) sepia(1) saturate(7) hue-rotate(-50deg) drop-shadow(0 0 6px #ff2020); }
}
/* Pre-roll ghost slots */
.fkr-die-ghost { width: 46px; height: 46px; border-radius: 13px; border: 1.5px dashed var(--pgr-line); opacity: .55; }
.fkr-stats { display: flex; gap: clamp(28px,9vw,56px); align-items: flex-start; }
.fkr-stat { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.fkr-stat-val { font-size: clamp(24px,6vw,32px); font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; }
.fkr-stat-lbl { color: var(--pgr-dim); font-size: clamp(11px,2.4vw,13px); font-weight: 600; }
@keyframes fkr-bop { 0%,100% { transform: translateY(0) rotate(-2.5deg) scale(1); } 50% { transform: translateY(-6px) rotate(2.5deg) scale(1.05); } }

@keyframes pigr-float { 0%,100% { transform: translateY(0) rotate(-3deg); } 50% { transform: translateY(-7px) rotate(3deg); } }
@keyframes pigr-pop { 0% { transform: scale(.85) translateY(4px); opacity: 0; } 55% { transform: scale(1.03); } 100% { transform: scale(1); opacity: 1; } }
/* Flash animates FILTER ONLY — never transform — so the inline translate/scale we
   set for the fall-off (bust) and fly-to-score (bank) isn't clobbered by the
   animation's `both` fill winning over inline style on the same property. */
/* The trail dice are small and mostly grey/white, so a red drop-shadow glow alone is
   near-invisible. sepia() tones the whole die warm, then saturate()+hue-rotate() push it
   to vivid red — this actually reddens the die FACE (not just a halo), so a bust reads. */
@keyframes pigr-die-red {
  /* Every keyframe carries the SAME function list with hue-rotate/sepia already at
     full value — only saturation ramps. Interpolating from `filter:none` passed
     through sepia-without-enough-hue-rotate frames, i.e. bright YELLOW, and under
     jank/reparent restarts those midway frames were what actually showed on screen. */
  0%   { filter: grayscale(1) sepia(1) saturate(1) hue-rotate(-45deg) brightness(1) drop-shadow(0 0 0 #ff1010); }
  20%  { filter: grayscale(1) sepia(1) saturate(9) hue-rotate(-45deg) brightness(1.2) drop-shadow(0 0 12px #ff1010); }
  55%  { filter: grayscale(1) sepia(1) saturate(7.5) hue-rotate(-45deg) brightness(1.08) drop-shadow(0 0 9px #ff1a1a); }
  100% { filter: grayscale(1) sepia(1) saturate(6.5) hue-rotate(-45deg) brightness(1) drop-shadow(0 0 6px #ff2020); }
}
/* Incoming card enters from the +Y edge; its opacity is held low until ~40% so it
   doesn't visibly overlap the outgoing ghost mid-swap. Outgoing ghost exits to −Y. */
@keyframes pigr-cardflip { 0% { transform: rotateY(88deg) scale(.96); opacity: 0; } 40% { opacity: .18; } 100% { transform: rotateY(0deg) scale(1); opacity: 1; } }
@keyframes pigr-cardflipout { 0% { transform: rotateY(0deg) scale(1); opacity: 1; } 100% { transform: rotateY(-88deg) scale(.96); opacity: 0; } }
@keyframes pigr-die-green { 0% { filter: none; } 45% { filter: drop-shadow(0 0 9px var(--pgr-sel)) brightness(1.2); } 100% { filter: drop-shadow(0 0 5px var(--pgr-sel)); } }
@keyframes pigr-hit { 0%,100% { transform: translateX(0); box-shadow: none; } 12% { transform: translateX(-8px); } 28% { transform: translateX(7px); box-shadow: inset 0 0 0 2px rgba(255,45,45,.85), 0 0 24px -4px rgba(255,45,45,.7); } 46% { transform: translateX(-5px); } 64% { transform: translateX(4px); box-shadow: inset 0 0 0 2px rgba(255,45,45,.5); } 82% { transform: translateX(-2px); } }
@keyframes pigr-fire { 0%,100% { box-shadow: 0 0 0 1.5px rgba(255,150,40,.75), 0 0 16px 1px rgba(255,120,20,.4); } 50% { box-shadow: 0 0 0 2px rgba(255,170,60,1), 0 0 30px 4px rgba(255,120,20,.7); } }
@keyframes pigr-fire-badge { 0%,100% { opacity: .8; } 50% { opacity: 1; } }

/* ── Achievements gallery (profile screen) — economy-design §7b ─────────────── */
.pach-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: .75rem; }
.pach-head .section-label { margin: 0; }
.pach-progress { font-size: 12px; color: #888; font-weight: 600; }
.pach-group-label { font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: #888; margin: 16px 0 8px; }
.pach-group-label:first-child { margin-top: 0; }
.pach-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 10px; background: #161616; border: 1px solid #222; margin-bottom: 6px; }
.pach-item.unlocked { background: rgba(255,215,106,.08); border-color: rgba(255,215,106,.35); }
.pach-emoji { font-size: 1.6em; line-height: 1; flex: 0 0 auto; width: 1.5em; text-align: center; }
.pach-item.locked .pach-emoji { filter: grayscale(1); opacity: .4; }
.pach-body { flex: 1; min-width: 0; text-align: left; }
.pach-name { font-weight: 700; line-height: 1.15; }
.pach-item.locked .pach-name { color: #8a8a8a; }
.pach-desc { opacity: .7; font-size: .82em; line-height: 1.2; margin-top: 1px; }
.pach-meta { flex: 0 0 auto; text-align: right; white-space: nowrap; }
.pach-xp { color: #ffd76a; font-weight: 700; font-size: .82em; }
.pach-date { color: #666; font-size: .72em; margin-top: 2px; }
.pach-lock { color: #555; font-size: 1.1em; }

/* ─── Profile tab shell (overhaul Step 1) ─────────────────────────────────── */
.profile-tabs {
  display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none;
  border-bottom: 0.5px solid #2e2e2e; background: #111;
  padding: 0 6px; position: sticky; top: 0; z-index: 5;
}
.profile-tabs::-webkit-scrollbar { display: none; }
.ptab-btn {
  appearance: none; flex: 1 0 auto; min-width: 64px; white-space: nowrap;
  border: 0; border-bottom: 2px solid transparent; background: transparent;
  color: #888; cursor: pointer; font-size: 12px; font-weight: 600;
  padding: 9px 8px 8px; display: flex; flex-direction: column; align-items: center; gap: 3px;
  transition: color .15s, border-color .15s;
}
.ptab-btn .ptab-ic { font-size: 16px; line-height: 1; }
.ptab-btn:hover { color: #bbb; }
.ptab-btn.on { color: #e8e8e8; border-bottom-color: #1D9E75; }

.ptab { display: none; }
.ptab.on { display: block; animation: ptab-fade .2s ease; }
@keyframes ptab-fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

.ptab-error {
  background: #2a1414; border: 0.5px solid #5a2020; border-radius: 10px;
  color: #e0a0a0; font-size: 13px; padding: 12px 14px; margin-bottom: 1rem;
}
.ptab-error button {
  margin-left: 8px; background: transparent; border: 0.5px solid #5a2020;
  color: #e0a0a0; border-radius: 6px; padding: 3px 9px; cursor: pointer; font-size: 12px;
}

/* ─── Overview tab (overhaul Step 2) ──────────────────────────────────────── */
.ov-hero {
  background: linear-gradient(135deg, rgba(29,158,117,.15), rgba(59,130,246,.10));
  border-color: rgba(29,158,117,.30);
}
.ov-hero-top { display: flex; align-items: baseline; gap: 10px; }
.ov-lv { font-size: 30px; font-weight: 800; line-height: 1; }
.ov-lv-sub { font-size: 12px; color: #888; }
.ov-coins { margin-left: auto; font-size: 15px; font-weight: 700; color: #f5c451; }
.ov-xpbar { height: 9px; border-radius: 6px; background: #ffffff14; margin-top: 12px; overflow: hidden; }
.ov-xpbar > i { display: block; height: 100%; border-radius: 6px; background: linear-gradient(90deg,#1D9E75,#3b82f6); transition: width .5s ease; }
.ov-xpnums { display: flex; justify-content: space-between; font-size: 11px; color: #888; margin-top: 5px; }

/* Profile is a read-only dashboard — no drag-to-highlight (feels app-like, avoids
   accidental text selection on long-press/drag). Inputs stay selectable/editable. */
#profile-screen { -webkit-user-select: none; -moz-user-select: none; user-select: none; -webkit-touch-callout: none; }
#profile-screen input, #profile-screen textarea, #profile-screen select { -webkit-user-select: text; user-select: text; }

/* Recent games: show 3, expand to 10 on tap (chevron in the header, whole card is the hit target). */
#ov-recent-card.ov-has-more { cursor: pointer; }
#ov-recent-card .ov-row:nth-child(n+4) { display: none; }
#ov-recent-card.expanded .ov-row:nth-child(n+4) { display: flex; }
.ov-chevron { transition: transform .2s ease; color: #888; font-size: 13px; }
#ov-recent-card.expanded .ov-chevron { transform: rotate(180deg); }
/* Centered "•••" affordance — only while collapsed with more to show. */
.ov-more-hint { display: none; text-align: center; color: #777; font-size: 15px; letter-spacing: 3px; line-height: 1; padding-top: 8px; }
#ov-recent-card.ov-has-more:not(.expanded) .ov-more-hint { display: block; }

.ov-row { display: flex; align-items: center; gap: 11px; padding: 9px 2px; border-top: 0.5px solid #1e1e1e; }
.ov-row:first-child { border-top: 0; }
.ov-row .ov-ic {
  width: 30px; height: 30px; border-radius: 9px; flex: none; display: grid; place-items: center;
  font-size: 16px; background: #222; border: 0.5px solid #2e2e2e;
}
.ov-row .ov-mid { flex: 1; min-width: 0; }
.ov-row .ov-t1 { font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ov-row .ov-t2 { font-size: 11.5px; color: #888; margin-top: 1px; }
.ov-row .ov-rt { font-size: 11.5px; color: #888; white-space: nowrap; }
.ov-row .ov-rt.win { color: #1D9E75; font-weight: 700; }
.ov-row .ov-rt.loss { color: #E24B4A; font-weight: 700; }
.ov-empty { font-size: 13px; color: #888; padding: 6px 2px; }

/* ─── Stats tab: game selector + per-game blocks (overhaul Step 3) ─────────── */
.gsel { display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; margin-bottom: 12px; padding-bottom: 2px;
  touch-action: pan-x; cursor: grab; -webkit-user-select: none; user-select: none; }
.gsel.dragging { cursor: grabbing; }
.gsel.dragging button { pointer-events: none; }
.gsel::-webkit-scrollbar { display: none; }
/* Edge fade-out affordance for the horizontally-scrollable chip strips (game
   filters/selectors). The scrollbar is hidden, so without this there's no hint
   more chips exist off-screen. A mask fades the content to transparent by ALPHA
   (so it works over any card background); JS (attachEdgeFade) toggles fade-l /
   fade-r by scroll position, so a side only fades when there's more that way.
   The two-class rule wins on specificity when both edges overflow. */
.gsel.fade-l { -webkit-mask-image: linear-gradient(to right, transparent 0, #000 26px); mask-image: linear-gradient(to right, transparent 0, #000 26px); }
.gsel.fade-r { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent 100%); mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent 100%); }
.gsel.fade-l.fade-r { -webkit-mask-image: linear-gradient(to right, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%); mask-image: linear-gradient(to right, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%); }
.gsel button {
  appearance: none; white-space: nowrap; border: 0.5px solid #2e2e2e; cursor: pointer;
  background: #222; color: #999; border-radius: 20px; padding: 7px 13px; font-size: 12.5px; font-weight: 700;
}
.gsel button.on { background: #1D9E75; color: #04121f; border-color: #1D9E75; }
.gpane { display: none; }
.gpane.on { display: block; animation: ptab-fade .2s ease; }

/* category drill-down panel (profile-only interactivity) */
.catdet {
  margin-top: 12px; padding: 12px; border: 0.5px solid #2a2a2a; border-radius: 12px;
  background: #1a1a1a; text-align: center; color: #888; font-size: 12.5px;
}
.catdet.on { text-align: left; }
.catdet h4 { margin: 0 0 9px; font-size: 14px; color: #e8e8e8; display: flex; align-items: center; gap: 8px; }
.catdet h4 .rt2 { margin-left: auto; font-size: 12px; color: #60a5fa; font-weight: 800; }
.dbar { margin: 8px 0; }
.dbar .dl { display: flex; justify-content: space-between; font-size: 12px; color: #bbb; margin-bottom: 4px; }
.dbar .dt { height: 9px; border-radius: 5px; background: #ffffff14; overflow: hidden; }
.dbar .dt i { display: block; height: 100%; border-radius: 5px; transition: width .5s ease; }
.catdet .tot { margin-top: 10px; padding-top: 9px; border-top: 0.5px solid #2a2a2a; font-size: 12px; color: #888; }
.catdet .tot b { color: #e8e8e8; }

/* ─── Customize tab: equip rows + swatches ────────────────────────────────── */
.selrow {
  display: flex; align-items: center; gap: 10px; padding: 10px 2px; font-size: 13.5px;
  color: #ccc; border-top: 0.5px solid #1e1e1e;
}
.selrow:first-of-type { border-top: 0; }
.selrow select, .selrow .swatch { margin-left: auto; }
.selrow select { background: #222; border: 0.5px solid #2e2e2e; color: #e8e8e8; border-radius: 8px; padding: 7px 10px; }
.swatch { display: flex; gap: 7px; flex-wrap: wrap; justify-content: flex-end; }
.swatch i { width: 24px; height: 24px; border-radius: 50%; cursor: pointer; border: 2px solid transparent; box-sizing: border-box; }
.swatch i.sel { border-color: #fff; }

/* Dice skin / roll-animation pickers (Customize · DieRenderer) */
.dr-preview { display: flex; flex-direction: column; align-items: center; padding: 6px 0 4px; }
.dice-pick-label { font-size: 11px; color: #888; letter-spacing: .04em; text-transform: uppercase; margin: 14px 0 8px; }
.dice-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.dice-chip {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  background: #222; border: 1.5px solid #2e2e2e; border-radius: 12px;
  padding: 6px 11px 6px 7px; color: #ccc; font-size: 12.5px; font-weight: 600;
}
.dice-chip.anim { padding: 8px 14px; }
.dice-chip.sel { border-color: #1D9E75; background: #16241d; color: #e8e8e8; }
.dice-chip.locked { border-style: dashed; border-color: #3a3320; color: #b8b0a0; }
.dice-chip .dice-chip-die { display: inline-flex; }
.dice-chip .dice-chip-price { font-size: 11px; color: #ffd54f; font-weight: 700; margin-left: 1px; }
.dice-chip:active { transform: scale(.97); }
.dice-chip.purchase { background: #16241d; border: 0.5px solid #1D9E7566; color: #4caf50; font-weight: 700; }
.dice-bal { font-size: 13px; color: #ffd54f; font-weight: 700; text-transform: none; letter-spacing: 0; }

/* Board colour-scheme picker (Customize). Each chip is a mini WYSIWYG of the board:
   its --pgr-bg background, a translucent seat row with the accent dot + track bar, and a
   button in the scheme's button gradient. All inline colours come from BoardSchemes. */
.board-chips { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 10px; }
.board-chip {
  display: flex; flex-direction: column; gap: 6px; cursor: pointer; text-align: left;
  border: 2px solid transparent; border-radius: 14px; padding: 9px; overflow: hidden;
  box-shadow: 0 4px 14px -8px rgba(0,0,0,.6);
}
.board-chip.sel { border-color: #fff; box-shadow: 0 0 0 2px rgba(255,255,255,.15), 0 4px 14px -6px rgba(0,0,0,.7); }
.board-chip:active { transform: scale(.97); }
.board-chip-seat { display: flex; align-items: center; gap: 6px; border: 1px solid; border-radius: 9px; padding: 7px 8px; }
.board-chip-dot { width: 13px; height: 13px; border-radius: 5px; flex: none; }
.board-chip-bar { flex: 1; height: 6px; border-radius: 99px; }
.board-chip-btn { align-self: flex-start; border: 1px solid; border-radius: 8px; padding: 3px 10px; font-size: 11px; font-weight: 800; }
.board-chip.purchase {
  background: #16241d; border: 0.5px solid #1D9E7566; color: #4caf50; font-weight: 700;
  align-items: center; justify-content: center; text-align: center; font-size: 12px; min-height: 84px;
}
.board-chip-label { font-size: 12.5px; font-weight: 700; }

/* ── Backgammon (.bgm-*) — token-driven, layout-scheme aware ──────────────────
   Two orientations share these classes: .bgm-board (wide) and .bgm-board-v
   (phone; the table rotated 90deg with the dead middle shortened). Checkers are
   literal cream/charcoal — they are physical objects and must stay tellable
   apart under every board scheme. --pgr-felt/--pgr-wood are local surfaces
   layered over whatever --pgr-bg is underneath. */
.bgm-root {
  --pgr-bg: radial-gradient(130% 120% at 50% -10%, #1c1d22 0%, #131418 55%, #0a0b0d 100%);
  --pgr-panel: rgba(255,255,255,.04);
  --pgr-line: rgba(255,255,255,.1);
  --pgr-txt: #ece9e2;
  --pgr-dim: #8b8d94;
  --pgr-sel: oklch(.74 .14 152);
  --pgr-selBg: oklch(.74 .14 152 / .16);
  --pgr-felt: rgba(0,0,0,.26);
  --pgr-wood: rgba(0,0,0,.4);
  position: relative;
  display: flex; flex-direction: column; gap: 10px;
  width: 100%; max-width: var(--pgr-shellMaxW, 440px);
  margin: 0 auto;
  padding: var(--pgr-shellPad, 14px);
  background: var(--pgr-shellBg, rgba(255,255,255,.03));
  border: var(--pgr-panelBd, 1px solid var(--pgr-line));
  border-radius: var(--pgr-shellRadius, 24px);
  box-shadow: var(--pgr-shellShadow, 0 40px 90px -30px rgba(0,0,0,.7));
  color: var(--pgr-txt);
  font-family: var(--pgr-font, 'Outfit', system-ui, sans-serif);
}
.gm-arcade .bgm-root { --pgr-felt: rgba(0,0,0,.34); --pgr-wood: rgba(0,0,0,.5); }

.bgm-fly { position: absolute; inset: 0; pointer-events: none; z-index: 70; }

/* banners */
.bgm-banner {
  display: flex; align-items: center; gap: 11px;
  background: var(--pgr-panel); border: 1.5px solid var(--pgr-line);
  border-radius: 14px; padding: 9px 13px;
}
.bgm-banner-on { border-color: var(--pgr-sel); box-shadow: 0 0 0 3px var(--pgr-selBg); }
.bgm-face {
  flex: none; width: 38px; height: 38px; border-radius: 50%;
  background: rgba(0,0,0,.25); border: 1px solid var(--pgr-line);
  display: flex; align-items: center; justify-content: center; font-size: 20px;
}
/* Two lines, and NEVER a third: this card's height is one of the numbers the board is
   sized against, so anything in here that wraps shrinks the board. Squeezed narrow (a
   340px board with the status line beside it) "Off: 0 / 15" wrapped to three lines and
   grew the card by 17px — caught by the renderer harness, never by looking at a phone. */
.bgm-who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.bgm-nm, .bgm-off { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bgm-nm { font-weight: 700; font-size: 14px; }
.bgm-off { font-size: 11px; color: var(--pgr-dim); font-weight: 600; }
.bgm-side { flex: none; display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--pgr-dim); font-weight: 600; }
.bgm-swatch { width: 16px; height: 16px; border-radius: 50%; }
/* The opponent's dice, inside their card. `min-height:0` is the point: the shared
   .bgm-dicerow reserves 42px for a row of its own, which would grow the banner — here the
   dice are no taller than the 38px avatar already setting the height, so the card costs
   the board nothing. Width is reserved inline (two dice + the gap). */
.bgm-bannerdice { flex: none; }
.bgm-bannerdice .bgm-dicerow { min-height: 0; gap: 8px; }
/* The status line lives in YOUR card, in the slot the opponent spends on their dice
   (owner's mockup, 2026-07-26) — which is what let the whole message ROW below the board
   be deleted and handed to the board. Two lines of 12px is shorter than the 38px avatar
   already setting this card's height, so the board loses nothing to it; `max-height` is
   the guard, because a card that grows would shrink the board under it. */
.bgm-bannermsg {
  flex: 1 1 auto; min-width: 0; text-align: center;
  font-size: 12px; font-weight: 600; line-height: 1.3; color: var(--pgr-dim);
  height: 2.6em; overflow: hidden;
}
.bgm-banner-you .bgm-who { flex: 0 1 auto; }

/* dice + controls */
.bgm-dicerow { display: flex; justify-content: center; gap: 10px; min-height: 42px; align-items: center; }
/* Nothing on this row is allowed to move. The primary button is anchored to the RIGHT at a
   FIXED width (so "Rolling…" / "Your move" / "🎲 New game" all occupy the same box), and the
   dice are centred in the space beside it. On doubles the extra pair is rendered OUTSIDE the
   first two, and because a centred row grows symmetrically the first two keep their exact
   position. Owner's call, and the same principle as the reserved Undo slot below. */
.bgm-controls { display: flex; align-items: center; gap: 10px; }
.bgm-controls .bgm-dicerow { flex: 1 1 auto; min-width: 0; }
/* Extra doubles dice slide out from underneath the pair that is already there. */
@keyframes bgm-die-out-l { from { transform: translateX(46px) scale(.5); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes bgm-die-out-r { from { transform: translateX(-46px) scale(.5); opacity: 0; } to { transform: none; opacity: 1; } }
.bgm-die-out-l { animation: bgm-die-out-l .28s cubic-bezier(.22,.9,.3,1.15) both; }
.bgm-die-out-r { animation: bgm-die-out-r .28s cubic-bezier(.22,.9,.3,1.15) both; }
.bgm-btn {
  flex: none; width: 132px; white-space: nowrap; overflow: hidden; text-align: center;
  height: 50px; padding: 0 6px; border-radius: 14px;
  border: 1px solid var(--pgr-line); background: var(--pgr-btn, linear-gradient(180deg,#2d3036,#22242a));
  color: var(--pgr-btnTxt, #f1f0ec); font-family: inherit; font-size: clamp(15px,4vw,17px);
  font-weight: 700; cursor: pointer; transition: filter .12s, transform .08s;
}
.bgm-btn:hover:not(:disabled) { filter: brightness(1.12); }
.bgm-btn:active:not(:disabled) { transform: translateY(1px) scale(.97); }
.bgm-btn:disabled { opacity: .45; cursor: default; }
/* The "This game" card inside GameEndScreen. It sits in the end screen's own themed
   subtree, so it reads --pgr-* tokens and needs no layout-scheme branch of its own. */
.bgm-sum { display: flex; flex-direction: column; gap: 6px; }
.bgm-sum-row { display: flex; align-items: baseline; gap: 10px; font-size: 13px; }
.bgm-sum-k { flex: none; color: var(--pgr-dim); font-weight: 600; }
.bgm-sum-v { flex: 1; min-width: 0; text-align: right; font-weight: 700; }
/* An optional control keeps its slot (.bgm-ghost hides without removing) rather than
   dropping out of its row: a control that appears must never shove its neighbours around.
   ↩ Undo is the one that does this — it sits at the left end of the controls row and is
   ghosted, never removed, so the dice beside it do not jump when a move becomes undoable. */
.bgm-ghost { visibility: hidden; pointer-events: none; }
/* Rotate-the-board button. It lives INSIDE the tray, between the two OFF buckets (owner's
   mockup, 2026-07-26) — a view control about the board, sitting on the board. Sized off the
   tray's own height, never in `vw`: nothing inside this board may scale with the viewport. */
.bgm-rotate {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  border: 1px solid var(--pgr-line); background: var(--pgr-panel);
  font-size: 14px; line-height: 1; cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
  transition: filter .12s, transform .25s;
}
.bgm-rotate:hover { filter: brightness(1.3); }
.bgm-rotate:active { transform: rotate(90deg); }
.bgm-rotate-tray {
  width: auto; aspect-ratio: 1; height: calc(100% - 8px); max-height: 30px;
  align-self: center; margin: 0 3px; border-radius: 8px;
}
.bgm-mini {
  flex: none; padding: 5px 11px; border-radius: 9px; border: 1px solid var(--pgr-line);
  background: var(--pgr-panel); color: var(--pgr-dim); font-family: inherit;
  font-size: 12px; font-weight: 700; cursor: pointer;
}
.bgm-mini:hover { filter: brightness(1.25); }
.bgm-mini-hot { color: var(--pgr-sel); border-color: var(--pgr-sel); }

/* board frame */
.bgm-board {
  display: flex; flex-direction: column;
  border: 2px solid var(--pgr-line); border-radius: 14px; overflow: hidden;
  background: var(--pgr-wood); aspect-ratio: 1 / 1.12;
}
.bgm-board-v { aspect-ratio: 1 / 1.5; }
.bgm-field { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--pgr-felt); }
.bgm-half { flex: 1; display: flex; min-height: 0; }
.bgm-half-v { flex: 1; display: flex; min-height: 0; background: var(--pgr-felt); }
.bgm-row { flex: 1; display: flex; min-width: 0; }
.bgm-col { flex: 1; display: flex; flex-direction: column; min-width: 0; }

/* the bar: a vertical gutter when wide, a horizontal strip when tall */
.bgm-bar {
  width: clamp(16px,5vw,26px); flex: none; background: var(--pgr-wood);
  border-left: 1px solid var(--pgr-line); border-right: 1px solid var(--pgr-line);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; padding: 4px 0;
}
.bgm-bar-h {
  height: clamp(16px,5vw,26px); flex: none; background: var(--pgr-wood);
  border-top: 1px solid var(--pgr-line); border-bottom: 1px solid var(--pgr-line);
  display: flex; align-items: center; justify-content: center; gap: clamp(10px,4vw,20px);
}
.bgm-bar-grp { display: flex; gap: 2px; }
.bgm-spine {
  width: clamp(14px,4vw,22px); flex: none; background: var(--pgr-wood);
  border-left: 1px solid var(--pgr-line); border-right: 1px solid var(--pgr-line);
}

/* a point */
.bgm-pt {
  cursor: pointer; position: relative; flex: 1; min-width: 0;
  display: flex; align-items: center; padding: 3px 0;
}
.bgm-pt-v { min-width: auto; min-height: 0; padding: 0 3px; }
.bgm-tri { position: absolute; inset: 0; }
.bgm-num {
  position: absolute; left: 0; right: 0; text-align: center;
  font-size: 8px; font-weight: 700; color: var(--pgr-dim); opacity: .7; z-index: 1;
}
.bgm-num-v { left: auto; right: auto; top: 0; bottom: 0; text-align: left; display: flex; align-items: center; }
.bgm-stack { position: relative; z-index: 2; display: flex; align-items: center; gap: 2px; }

/* bear-off tray */
.bgm-tray { height: clamp(30px,9vw,44px); flex: none; display: flex; background: var(--pgr-wood); border-top: 2px solid var(--pgr-line); }
.bgm-board-v .bgm-tray { height: clamp(28px,8vw,40px); }
.bgm-tray-cell { flex: 1; display: flex; align-items: center; gap: 3px; padding: 4px 9px; overflow: hidden; border-radius: 9px; transition: background .2s; }
.bgm-tray-you { cursor: pointer; flex-direction: row-reverse; }
.bgm-board-v .bgm-tray-you { flex-direction: row; }
.bgm-board-v .bgm-tray-cell:not(.bgm-tray-you) { flex-direction: row-reverse; }
.bgm-tray-lbl { font-size: 8px; font-weight: 700; color: var(--pgr-dim); opacity: .7; }
/* The chips are absolutely placed inside this and their pitch is a % of ITS width, so a
   full tray of 15 compresses to fit instead of running out under the divider. `min-width:0`
   is what lets a flex child actually be narrower than its content. */
.bgm-traychips { position: relative; flex: 1; min-width: 0; align-self: stretch; }

/* A legal destination gets a RING, not just a lighter triangle. The tint alone is a
   colour-on-colour difference, and on the Arcade/Blue board — a blue accent on a blue felt
   — the owner could not find it at all (playtest, 2026-07-26). A ring reads as an outline
   regardless of how close the two colours are, so this needs no per-scheme tuning; the
   per-theme tint values are a separate job, waiting on the asset/theme page. */
.bgm-pt-dest::before {
  content: ''; position: absolute; inset: 2px; border-radius: 7px; z-index: 1;
  box-shadow: inset 0 0 0 2px var(--pgr-sel),
              0 0 10px 1px color-mix(in oklch, var(--pgr-sel) 45%, transparent);
  animation: bgm-avail 1.3s ease-in-out infinite;
}

/* the opponent's last turn, still readable after the animation is over */
.bgm-pt-left::after {
  content: ''; position: absolute; inset: 1px; border-radius: 5px; z-index: 1;
  box-shadow: inset 0 0 0 1.5px rgba(255,222,160,.34);
}

/* spent dice sweep off instead of blinking out */
@keyframes bgm-dice-gone { to { opacity: 0; transform: translateY(7px) scale(.72); } }
.bgm-dice-gone { animation: bgm-dice-gone .32s cubic-bezier(.4,0,.75,.2) both; pointer-events: none; }

@keyframes bgm-avail { 0%,100% { filter: brightness(1); } 50% { filter: brightness(1.18); } }
@media (prefers-reduced-motion: reduce) { .bgm-root * { animation: none !important; } }

/* The whole board must fit without scrolling, and it must use ALL the room that is left
   over. `bgmFitBoard()` sets an inline width every render: it MEASURES the space the rest
   of the screen actually leaves (chrome rows, gaps, ancestor padding — including the 84px
   the solo chat bar reserves) and takes min(available width, available height / ratio).
   This replaces four hand-tuned `vh` caps that were fractions of the viewport rather than
   the leftover, and so left ~100px of width unused on a phone. These values are only the
   pre-fit fallback for the first paint / no-JS. */
.bgm-board { align-self: center; width: 100%; max-width: min(100%, 62vh); }
.bgm-board-v { max-width: min(100%, 44vh); }
@media (max-width: 430px) {
  #backgammon-game-screen .wrap { padding: 8px; }
  .bgm-root { gap: 7px; padding: 10px; }
  .bgm-banner { padding: 6px 10px; gap: 9px; }
  .bgm-face { width: 32px; height: 32px; font-size: 17px; }
  .bgm-nm { font-size: 13px; }
  .bgm-dicerow { min-height: 34px; }
  .bgm-btn { height: 44px; width: 126px; font-size: 15px; }
  .bgm-bannermsg { font-size: 11.5px; }
  .bgm-mini { padding: 5px 8px; font-size: 11px; }
}
@media (max-width: 380px) {
  .bgm-root { gap: 5px; padding: 8px; }
  .bgm-banner { padding: 5px 9px; }
  .bgm-face { width: 28px; height: 28px; font-size: 15px; }
  .bgm-dicerow { min-height: 30px; gap: 8px; }
  .bgm-btn { height: 40px; width: 112px; font-size: 14px; }
  .bgm-bannermsg { font-size: 11px; line-height: 1.25; }
  /* The controls row carries ↩ Undo + up to FOUR dice + the fixed-width button on the
     narrowest phone we support, and none of them may wrap: the row's height is what the
     board is measured against. Undo gives up the padding. */
  .bgm-mini { padding: 4px 6px; font-size: 10.5px; }
  .bgm-controls { gap: 8px; }
}

/* ─── Liar's Dice MP — the V2 circular table (.ldt-*) ──────────────────────────
   Every colour is a --pgr-* token (BoardSchemes Classic / LayoutSchemes Arcade);
   the only fixed colours are the four seat identity colours + the danger red,
   which liars_dice_mp.js sets inline as data. */
.ldt-wrap { display: flex; justify-content: center; padding: var(--pgr-outerPad, 0); padding-bottom: 84px; }
.ldt-loading { padding: 2rem 0; text-align: center; color: var(--pgr-dim, #9aa); font-family: var(--pgr-font, 'Outfit', sans-serif); }
.ldt-shell {
  width: 100%; max-width: var(--pgr-shellMaxW, 520px); margin: 0 auto;
  background: var(--pgr-shellBg, transparent); border-radius: var(--pgr-shellRadius, 0);
  box-shadow: var(--pgr-shellShadow, none); padding: var(--pgr-shellPad, 12px);
  display: flex; flex-direction: column; gap: var(--pgr-shellGap, 11px);
  font-family: var(--pgr-font, 'Outfit', sans-serif); color: var(--pgr-txt, #e9e8e4);
}
.gm-arcade .ldt-shell { border: var(--pgr-panelBd, 2px solid var(--pgr-accent)); }

.ldt-head { display: flex; align-items: center; justify-content: space-between; padding: 0 2px; }
.ldt-head-l { font-size: 11px; letter-spacing: .14em; font-weight: 700; color: var(--pgr-dim); }
.ldt-head-r { font-size: 11px; letter-spacing: .08em; font-weight: 700; color: var(--pgr-sel); }
/* Active-variant badge, in the header beside the round (ldMpRender toggles [hidden]). Reads
   as a chip rather than more header text so it registers as a RULE and not a caption — it is
   the only thing on the board that says 1s are wild. Sits in the accent so it cannot be
   mistaken for the dimmed round label it follows. */
/* selBg as the BACKGROUND and sel as the TEXT — that way round. --pgr-selBg is a ~16%-alpha
   tint OF the accent, not a surface to put accent-coloured text on; the inverse pairing is
   what made the lobby's Start button a blank green slab. Fallbacks because an undefined var
   invalidates its whole declaration — no border at all is a nicer failure than a chip that
   vanishes into the header, and the layout harness (no GameTheme) is exactly that case. */
.ldt-variant { display: inline-block; margin-left: 8px; padding: 1px 6px; border-radius: 999px;
  background: var(--pgr-selBg, rgba(125,211,125,.16)); color: var(--pgr-sel, #7dd37d);
  border: 1px solid var(--pgr-sel, #7dd37d); font-size: 10px; letter-spacing: .06em; white-space: nowrap; }
.ldt-variant[hidden] { display: none; }   /* [hidden] loses to the padding rule above without this */

/* ── Table + centre disc ── */
/* The table is sized per OPPONENT COUNT (.ldt-t1/2/3, set in ldMpBuildDom). The arc puts
   seats at different heights depending on how many there are — one opponent sits alone at
   the top, three fill the whole ellipse — so a single aspect-ratio left a dead band under
   the table in the small-table cases (2026-07-27 playtest, both phone and PC). The
   margin-top lifts the whole table+banner group off the top of the screen. */
/* The height transition is for the reveal only: ldMpRevealSpace hands the table exactly the
   height the collapsing controls + status give up, so the shell's total height is constant
   through a reveal and "Your hand" never rides up (2026-07-27 playtest 3). Both ends are
   fixed px, which is what makes this transitionable at all. */
.ldt-table {
  position: relative; width: 100%; max-width: 420px; margin: 10px auto 0;
  height: var(--ldt-h, 320px);
  transition: height .42s cubic-bezier(.4,0,.2,1);
}
/* Height is FIXED per opponent count, not an aspect-ratio. The table's contents are cards
   and a disc of roughly constant pixel size, so tying height to width meant a wide PC table
   grew a tall empty band under the disc while a narrow phone table squeezed. --ldt-disc is
   the disc's vertical centre, dropped on the sparser tables so it fills the space the
   missing seats left instead of leaving it dead. */
/* --ldt-discW is the disc's WIDTH, and with side seats it is derived, not picked. Seat centres
   sit at 50% ± 39%, so the gap between two rotated side cards is 78% of the table minus their
   60px rendered width; taking a fixed 22px of clearance off each side leaves
   calc(78% - 60px - 44px). That holds the same 22px gap at any table width, which a flat
   percentage cannot: the cards are a constant pixel size, so 56% would breathe on PC and
   collide on a phone. With one opponent there are no side cards to clear, so it is a plain
   percentage. Only the resting disc uses it — .ldt-center-box (the reveal panel) is width:100%. */
.ldt-t1 { --ldt-h: 210px; --ldt-disc: 71%; --ldt-discW: 68%; max-width: 380px; }
.ldt-t2 { --ldt-h: 225px; --ldt-disc: 64%; --ldt-discW: calc(78% - 104px); }
.ldt-t3 { --ldt-h: 280px; --ldt-disc: 62%; --ldt-discW: calc(78% - 104px); }
@media (min-width: 700px) {
  .ldt-table { max-width: 500px; margin-top: 18px; }
  .ldt-t1 { max-width: 440px; }
}
/* The disc NEVER morphs into the reveal panel — it flips out, swaps, and flips back in
   (see ldMpPlayReveal). It used to carry `transition: top/width/border-radius .5s`, which
   is what the 2026-07-27 playtest saw as "starts VERY stretched, then shrinks jankily":
   the width animated 40%→90% over half a second while the HEIGHT, being content-driven,
   snapped instantly to fit the reveal rows — so the panel appeared as a tall narrow column
   and widened into place. A transition can only animate the properties you name, and a
   content-sized box will always beat it to the final height. Hence: no geometry transition
   anywhere on this element, and the two states swap only while it is rotated out of view. */
.ldt-center {
  position: absolute; left: 50%; top: var(--ldt-disc, 44%); transform: translate(-50%, -50%);
  width: var(--ldt-discW, 40%); min-height: 108px; border-radius: 50%;
  background: radial-gradient(60% 60% at 50% 42%, rgba(255,255,255,.06), rgba(0,0,0,.22));
  border: 1px solid var(--pgr-line);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 12px 14px; text-align: center;
}
/* The reveal panel: a rounded card, sized to its content, centred on the table. Wider and
   flatter than the disc it replaces — the playtest asked for "a little bigger, especially
   on PC", and the per-seat hand rows need the horizontal room. */
.ldt-center-box {
  top: 50%; width: 100%; min-height: 0; border-radius: 20px; padding: 16px 18px; gap: 10px;
  background: var(--pgr-panel); border: 1.5px solid var(--pgr-line);
  box-shadow: 0 18px 44px -20px rgba(0,0,0,.75);
  /* Above the slid seat cards: on a short table the panel is taller than the gap under the
     top row, and the seats come later in the DOM so they would otherwise paint over it. */
  z-index: 6;
}
.ldt-boxout { animation: ldt-boxout .4s ease-in forwards; }
.ldt-boxin  { animation: ldt-boxin .45s ease-out both; }
.ldt-flash {
  position: absolute; inset: 0; border-radius: inherit; border: 2.5px solid var(--pgr-sel);
  animation: ldt-flash .7s ease forwards; pointer-events: none; z-index: 4;
}
.ldt-center-label { font-size: 9px; letter-spacing: .14em; font-weight: 700; color: var(--pgr-dim); opacity: .8; }
.ldt-bid-row { display: flex; align-items: center; gap: 9px; }
.ldt-bid-qty { font-size: 26px; font-weight: 800; color: var(--pgr-sel); font-variant-numeric: tabular-nums; display: inline-block; }
.ldt-numpop { animation: ldt-numpop .4s ease; }
.ldt-flipin { animation: ldt-flipin .45s ease-out both; display: inline-block; }
.ldt-bid-x { font-size: 18px; color: var(--pgr-dim); font-weight: 600; }
.ldt-bid-by { font-size: 11px; color: var(--pgr-dim); font-weight: 600; }
.ldt-nobid { font-size: 13px; color: var(--pgr-dim); font-weight: 600; }

/* ── Opponent seats on the arc ── */
.ldt-seat {
  position: absolute; transform: translate(-50%, -50%); width: clamp(104px, 30%, 132px);
  display: flex; flex-direction: column; align-items: stretch;
}
/* Seat cards leave and return with the reveal panel. Both keyframe sets MUST carry the base
   translate(-50%,-50%) — it lives in `transform`, so a keyframe that sets transform without
   it snaps the card to the arc point's top-left for the whole animation (the same trap
   .ldt-bubble-seat documents further down). */
.ldt-seatout { animation: ldt-seatout .4s ease-in both; }
.ldt-seatin  { animation: ldt-seatin .45s ease-out both; }
@keyframes ldt-seatout {
  0%   { transform: translate(-50%,-50%) perspective(900px) rotateY(0deg);   opacity: 1; }
  100% { transform: translate(-50%,-50%) perspective(900px) rotateY(90deg);  opacity: 0; }
}
@keyframes ldt-seatin {
  0%   { transform: translate(-50%,-50%) perspective(900px) rotateY(-90deg); opacity: 0; }
  100% { transform: translate(-50%,-50%) perspective(900px) rotateY(0deg);   opacity: 1; }
}
.ldt-seat-card {
  display: flex; flex-direction: column; gap: 5px;
  background: var(--pgr-panel); border: 1.5px solid var(--pgr-line); border-radius: 13px;
  padding: 7px 10px 8px; transition: border-color .3s, box-shadow .3s, opacity .3s;
}
/* ── Side seats turn to face the table (2026-07-27, owner) ──
   The rotation goes on the CARD, never on .ldt-seat: the wrapper owns translate(-50%,-50%)
   for arc placement AND the seatout/seatin rotateY keyframes, and composing a third rotation
   into those is how you get a card that flips out sideways or slides off its arc point.

   The wrapper's own width is deliberately left alone: .ldt-seat is absolutely positioned, so
   it reserves no space from anything and only the RENDERED card can collide with the disc.
   The card still sizes to the wrapper's clamp(104px, 30%, 132px) — a percentage of the TABLE,
   which is what keeps the arc maths in one unit — and rotating about its centre, which sits on
   the arc point, leaves it 60px wide on screen instead of 132px.

   −90 on the left and +90 on the right is the axis-label convention: the left card reads
   bottom-to-top, the right one top-to-bottom, mirror-symmetric about the table. Swap the two
   signs if you would rather both cards' tops point INWARD at the disc. */
.ldt-seat-left  .ldt-seat-card { transform: rotate(-90deg); }
.ldt-seat-right .ldt-seat-card { transform: rotate(90deg); }
/* The DICE stay upright (owner): a rotated card would lay a six on its side as three columns
   of two, and dice-remaining is the one thing you scan constantly. Counter-rotating each die
   by the opposite 90° puts every face back the way it is drawn — a six reads as two vertical
   columns of three — while the row itself still runs along the card's long edge. */
.ldt-seat-left  .ldt-seat-dice > * { transform: rotate(90deg); }
.ldt-seat-right .ldt-seat-dice > * { transform: rotate(-90deg); }
.ldt-seat-row { display: flex; align-items: center; justify-content: center; gap: 7px; }
/* The name clips against this wrapper; ldMpSetSeatName measures the two and only then
   arms the marquee. min-width:0 is what lets a flex child actually shrink and clip. */
.ldt-seat-namewrap { overflow: hidden; flex: 1; min-width: 0; }
.ldt-seat-name { display: inline-block; white-space: nowrap; font-weight: 700; font-size: 12.5px; }
.ldt-seat-name.ldt-marquee { animation: ldt-marquee 6.5s ease-in-out infinite; }
/* The face-down dice live INSIDE the card (they used to hang below it, outside the card's
   box, where they overlapped the table and the centre disc — 2026-07-27 playtest). Inside,
   the seat is one self-contained block, so the arc maths can no longer collide with it. */
/* nowrap + a 14px die is what keeps five dice on ONE row inside the narrowest card
   (5x14 + 4x2 = 78px against ~87px of inner width). At the old 17px/3px they wrapped to two
   rows on a phone, which is what made the card tall enough to reach the centre disc. */
.ldt-seat-dice { display: flex; justify-content: center; gap: 2px; min-height: 18px; flex-wrap: nowrap; }

/* Face-down die + the reveal flip */
.ldt-back {
  background: linear-gradient(145deg, rgba(255,255,255,.09), rgba(255,255,255,.03));
  border: 1px solid var(--pgr-line); display: flex; align-items: center; justify-content: center; flex: none;
  box-sizing: border-box;
}
.ldt-back > span { transform: rotate(45deg); background: var(--pgr-dim); opacity: .45; border-radius: 3px; display: block; }
.ldt-flip { position: relative; display: inline-block; flex: none; transform-style: preserve-3d; -webkit-transform-style: preserve-3d; animation: ldt-flip3d .5s cubic-bezier(.4,0,.2,1) both; }
.ldt-flip-front, .ldt-flip-back { position: absolute; inset: 0; backface-visibility: hidden; -webkit-backface-visibility: hidden; }
.ldt-flip-back { transform: rotateY(180deg); }

/* ── Status / hand / builder / actions ── */
/* iOS treats a quick second tap on the SAME element as double-tap-to-zoom, so raising a bid
   by tapping + repeatedly zoomed the page (2026-07-27 playtest, iPhone). `manipulation`
   opts these targets out of double-tap zoom (and its 300ms wait) while leaving pinch-zoom on
   the rest of the page alone — the same treatment .pigr-btn already carries. This must cover
   every RAPIDLY tapped control: the steppers, the face picker and the action row. */
.ldt-step, .ldt-btn, .ldt-face, .ldt-retry {
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none; user-select: none;
}
.ldt-status { min-height: 22px; text-align: center; font-size: 14px; font-weight: 600; color: var(--pgr-dim); padding: 0 8px; }
.ldt-retry {
  margin-left: 6px; padding: 4px 12px; border-radius: 9px; border: 1px solid var(--pgr-line);
  background: var(--pgr-btn); color: var(--pgr-btnTxt); font-family: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer;
}
.ldt-hand {
  position: relative; display: flex; flex-direction: column; gap: 9px;
  background: var(--pgr-panel); border: 1.5px solid var(--pgr-line); border-radius: 16px;
  padding: 12px 16px 14px; transition: border-color .3s, box-shadow .3s;
}
.ldt-hand-top { display: flex; align-items: center; justify-content: space-between; }
.ldt-hand-title { font-weight: 700; font-size: 15px; }
.ldt-hand-sub { font-size: 12px; color: var(--pgr-dim); font-weight: 600; }
.ldt-hand-dice { display: flex; justify-content: center; align-items: center; gap: 10px; min-height: 46px; flex-wrap: wrap; }
.ldt-hand-out { color: var(--pgr-dim); font-size: 13px; font-weight: 600; }

/* Bid builder + action row as one collapsible group (see ldMpRevealSpace) — and the status
   line collapses the same way, so "X called Liar! Revealing…" flips out with them once the
   panel is ready to take over.

   max-height is deliberately NOT set here — ldMpRevealSpace writes the exact measured px
   inline before it collapses anything. A CSS over-estimate makes the transition spend its
   first half clipping nothing, which is what stopped it cancelling the table's growth frame
   by frame (playtest 3: the hand was pushed down, then snapped back up). */
.ldt-controls, .ldt-status {
  overflow: hidden; transform-origin: top center;
  transition: max-height .42s cubic-bezier(.4,0,.2,1), min-height .42s cubic-bezier(.4,0,.2,1),
              opacity .28s ease, transform .42s cubic-bezier(.4,0,.2,1);
}
.ldt-controls { display: flex; flex-direction: column; gap: var(--pgr-shellGap, 11px); }
.ldt-ctrl-hidden {
  max-height: 0; opacity: 0; transform: perspective(900px) rotateX(-22deg);
  pointer-events: none;
  /* .ldt-status carries a min-height, which max-height:0 alone cannot beat — without this the
     status keeps a 22px band and the table's compensating growth overshoots the shell. */
  min-height: 0;
}
.ldt-builder {
  display: flex; flex-direction: column; gap: 10px;
  background: var(--pgr-panel); border: 1px solid var(--pgr-line); border-radius: 16px; padding: 12px 14px;
  transition: opacity .25s;
}
.ldt-builder.ldt-idle { opacity: .65; }
.ldt-builder-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ldt-builder-label { font-size: 11px; letter-spacing: .1em; font-weight: 700; color: var(--pgr-dim); }
.ldt-hint { font-size: 12px; font-weight: 600; }
.ldt-faces { display: flex; justify-content: center; gap: 7px; flex-wrap: wrap; }
.ldt-face { cursor: pointer; border-radius: 11px; padding: 3px; display: inline-flex; transition: background .12s, opacity .12s; }
.ldt-face.sel { background: var(--pgr-selBg); box-shadow: 0 0 0 1.5px var(--pgr-sel) inset; }
.ldt-face.off { opacity: .3; cursor: not-allowed; }
.ldt-qty { display: flex; align-items: center; gap: 10px; }
.ldt-step {
  flex: none; width: 52px; height: 44px; border-radius: 12px; border: 1px solid var(--pgr-line);
  background: var(--pgr-btn); color: var(--pgr-btnTxt); font-family: inherit; font-size: 22px; font-weight: 800;
  cursor: pointer; display: flex; align-items: center; justify-content: center; transition: filter .12s;
}
.ldt-step:active { transform: translateY(1px) scale(.97); }
.ldt-qty-val { flex: 1; text-align: center; font-size: 24px; font-weight: 800; font-variant-numeric: tabular-nums; }
.ldt-qty-x { font-size: 14px; color: var(--pgr-dim); font-weight: 600; }

.ldt-actions { display: flex; gap: 10px; width: 100%; }
.ldt-btn {
  height: 54px; border-radius: 15px; font-family: inherit; font-size: 15px; font-weight: 700; cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 7px; transition: transform .14s, filter .14s;
}
.ldt-btn:not(:disabled):hover { transform: translateY(-2px); filter: brightness(1.1); }
.ldt-btn:not(:disabled):active { transform: translateY(2px) scale(.97); }
.ldt-raise { flex: 1; border: 1px solid var(--pgr-line); background: var(--pgr-btn); color: var(--pgr-btnTxt); }
.ldt-raise:disabled { background: rgba(255,255,255,.03); color: var(--pgr-dim); opacity: .6; cursor: not-allowed; }
.ldt-call { flex: 1; border: 1.5px solid #E24B4A; background: rgba(226,75,74,.16); color: var(--pgr-txt); }
.ldt-call:disabled { border-color: var(--pgr-line); background: rgba(255,255,255,.03); color: var(--pgr-dim); opacity: .6; cursor: not-allowed; }
.ldt-chatbtn { flex: none; width: 54px; border: 1px solid var(--pgr-line); background: var(--pgr-panel); color: var(--pgr-txt); font-size: 20px; }
.ldt-foot { text-align: center; }

/* ── Speech bubbles ── */
.ldt-bubble {
  position: absolute; left: 22px; top: -44px; background: var(--pgr-bubbleBg); color: var(--pgr-bubbleTxt);
  padding: 8px 13px; border-radius: 14px; border-bottom-left-radius: 5px; font-size: 13px; font-weight: 600;
  white-space: nowrap; box-shadow: 0 12px 26px -10px rgba(0,0,0,.6); z-index: 6;
}
.ldt-bubble::after {
  content: ''; position: absolute; bottom: -5px; left: 16px; transform: rotate(45deg);
  width: 11px; height: 11px; background: var(--pgr-bubbleBg); border-radius: 3px;
}
.ldt-bubble-seat { left: 50%; transform: translateX(-50%); top: -34px; font-size: 11px; padding: 5px 9px; }
.ldt-bubble-seat::after { left: 50%; margin-left: -5px; }
.ldt-pop { animation: ldt-pop .24s cubic-bezier(.34,1.56,.64,1); }

/* ── Reveal panel rows ── */
/* The rows fade up INSIDE the already-flipped-in panel. They used to run `ldt-reveal`
   (scale .6 → 1.08 → 1), which was the second half of the janky-stretch report: a bouncing
   scale on the content while the panel itself was still resizing read as the whole box
   wobbling. The flip carries the motion now, so the content just settles. */
/* fit-content, NOT width:100% — the panel is much wider than a name column plus five dice, so
   a full-width block left the whole reveal hugging the left edge on PC while the label and the
   outcome lines above and below it were centred (owner, playtest 3). Shrinking the group to its
   content lets .ldt-center's align-items:center place it, and because every row still shares the
   group's width the name column and the dice stay aligned with each other rather than each row
   centring itself into a ragged stack. */
.ldt-rev-rows {
  display: flex; flex-direction: column; gap: 7px;
  width: fit-content; max-width: 100%; margin-inline: auto;
  animation: ldt-rev-fade .32s ease-out both; animation-delay: .12s;
}
.ldt-rev-row { display: flex; align-items: center; gap: 8px; }
/* flex-basis, NOT a fixed width: the name column holds 72/84px so the dice line up across
   rows, but it is allowed to SHRINK (and ellipsize) when the panel is tight. That sets the
   priority correctly — five dice must stay whole and readable, a long username can lose a few
   characters. min-width:0 is what actually lets a flex child shrink below its content. */
.ldt-rev-name {
  font-size: 13.5px; font-weight: 700; color: var(--pgr-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: left;
  flex: 0 1 72px; min-width: 0;
}
.ldt-rev-name.lose { color: #E24B4A; }
/* nowrap + flex:none — a hand is ONE row, always. It used to wrap, and on a narrow shell five
   dice were 10px too wide for the panel, so the fifth dropped to a second line for every seat
   (2026-07-27 playtest, on a PC with the screen half empty). Wrapping a hand also makes the
   panel taller for no reason; the tighter gap here plus a shrinkable name buys the room back. */
.ldt-rev-dice { display: flex; gap: 4px; flex-wrap: nowrap; flex: none; }
.ldt-rev-text { font-size: 14px; font-weight: 700; text-align: center; line-height: 1.35; margin-top: 4px; }
/* One fact per line (see ldMpPlayReveal). The last line is the consequence — who pays — so it
   gets the weight; the two above it are context and sit back a little. */
/* balance, so the one line that can still outgrow a phone panel — a 20-character username
   plus "loses a die" — splits into two even halves instead of stranding a word on its own,
   which is the awkward wrap the em-dash version was killed for. */
/* Each line fades and rises in on its OWN delay, written inline by ldMpPlayReveal (owner,
   playtest 4 — "stagger in the text line by line, ~1s apart", the way the reward-breakdown
   rows arrive on the game-over screen). Same motion as .rb-hidden's: opacity plus a 5px rise.
   `both` fill is doing the real work — it holds the line at the keyframe's 0% (invisible)
   through its whole delay, so a line is never briefly painted before its turn. It also keeps
   its LAYOUT the entire time, which is why the panel's height never moves. The resting
   opacity (.82 for context lines, 1 for the consequence) is the animation's end state, so it
   has to live in the keyframes, not here — `animation-fill-mode: both` pins the element at
   100% forever and a competing `opacity` on the rule would simply lose. */
.ldt-rev-line { text-wrap: balance; animation: ldt-rev-line .34s ease-out both; }
.ldt-rev-line + .ldt-rev-line { margin-top: 3px; }
/* The consequence line is the one you read; it only gets the size bump where there is room
   for it. At 1.12em on a 336px panel the longest name wrapped. */
.ldt-rev-final { animation-name: ldt-rev-line-final; }
/* The seat that pays, in the same red its row above is already drawn in (.ldt-rev-name.lose),
   so the eye ties the name in the outcome to the hand it belongs to. Wins over the container's
   colour by specificity; when the loser is YOU the whole block is red anyway and this is a
   no-op. */
.ldt-rev-lose { color: #E24B4A; }
@media (min-width: 700px) {
  .ldt-rev-name { flex-basis: 84px; font-size: 15px; }
  .ldt-rev-text { font-size: 15.5px; }
  .ldt-rev-final { font-size: 1.12em; }
}

/* ── Pending table ── */
.ldt-pending { text-align: center; padding: 2rem 1rem; }
.ldt-pending-emoji { font-size: 44px; }
.ldt-pending-title { font-weight: 700; font-size: 18px; margin-top: 10px; }
.ldt-pending-names { margin-top: 6px; color: var(--pgr-dim); font-size: 14px; }
.ldt-pending-note { margin-top: 14px; color: var(--pgr-dim); font-size: 12px; }

/* ── Keyframes ── */
@keyframes ldt-pop { 0% { transform: scale(.8) translateY(6px); opacity: 0; } 60% { transform: scale(1.05); } 100% { transform: scale(1) translateY(0); opacity: 1; } }
@keyframes ldt-rev-fade { 0% { opacity: 0; transform: translateY(5px); } 100% { opacity: 1; transform: translateY(0); } }
/* Two keyframes for one motion, because the resting opacity differs: a context line settles at
   .82, the consequence line at full strength (see .ldt-rev-line / .ldt-rev-final). */
@keyframes ldt-rev-line { 0% { opacity: 0; transform: translateY(5px); } 100% { opacity: .82; transform: translateY(0); } }
@keyframes ldt-rev-line-final { 0% { opacity: 0; transform: translateY(5px); } 100% { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .ldt-rev-line { animation-duration: .01ms; }   /* the stagger stays; the movement goes */
}
/* Scroll a too-long seat name left, hold, and come back. --ldt-mq is the exact overflow in
   px, set per-name by ldMpSetSeatName — so every name travels only as far as it must. */
@keyframes ldt-marquee {
  0%, 14%   { transform: translateX(0); }
  46%, 60%  { transform: translateX(var(--ldt-mq, 0)); }
  92%, 100% { transform: translateX(0); }
}
@keyframes ldt-flipin { 0% { opacity: 0; transform: perspective(1200px) rotateY(-90deg); } 60% { opacity: 1; } 100% { opacity: 1; transform: perspective(1200px) rotateY(0deg); } }
@keyframes ldt-flip3d { 0% { transform: perspective(600px) rotateY(0deg); } 100% { transform: perspective(600px) rotateY(180deg); } }
@keyframes ldt-numpop { 0% { transform: scale(.5); opacity: 0; } 60% { transform: scale(1.3); } 100% { transform: scale(1); opacity: 1; } }
@keyframes ldt-flash { 0% { opacity: .95; } 100% { opacity: 0; } }
@keyframes ldt-boxout { 0% { transform: translate(-50%,-50%) perspective(1200px) rotateY(0deg); opacity: 1; } 40% { opacity: 1; } 100% { transform: translate(-50%,-50%) perspective(1200px) rotateY(90deg); opacity: 0; } }
@keyframes ldt-boxin { 0% { transform: translate(-50%,-50%) perspective(1200px) rotateY(-90deg); opacity: 0; } 100% { transform: translate(-50%,-50%) perspective(1200px) rotateY(0deg); opacity: 1; } }

/* Bubble seat: keep the slide transition off the transform (translateX lives there) */
@media (max-width: 380px) {
  .ldt-step { width: 44px; height: 40px; }
  .ldt-btn { height: 48px; font-size: 14px; }
}

/* Seat bubbles carry translateX(-50%) in their base transform — the pop keyframes
   must keep it or the bubble jumps left for the duration of the animation. */
.ldt-bubble-seat.ldt-pop { animation: ldt-pop-seat .24s cubic-bezier(.34,1.56,.64,1); }
@keyframes ldt-pop-seat { 0% { transform: translateX(-50%) scale(.8) translateY(6px); opacity: 0; } 60% { transform: translateX(-50%) scale(1.05); } 100% { transform: translateX(-50%) scale(1) translateY(0); opacity: 1; } }

/* ── Craps SOLO (.crp-*) — token-driven felt, layout-scheme aware ───────────────
   Consumes the shared --pgr-* colour tokens (BoardSchemes) + shell tokens
   (LayoutSchemes) exactly like .stbr-*: all colours are vars, so a scheme or
   layout switch is pure data. The felt lettering is Playfair Display (the
   mock's serif); everything functional stays in --pgr-font.
   Chip text sits on a --pgr-sel background — there is NO selTxt token, and
   --pgr-solid (the solid version of the page bg) is the one colour guaranteed
   to contrast with the accent in every scheme, dark or light. */
.crp-root { min-height: 100vh; font-family: var(--pgr-font, 'Outfit', system-ui, sans-serif); color: var(--pgr-txt, #e9e8e4); display: flex; flex-direction: column; }
.crp-content { flex: 1; display: flex; flex-direction: column; transition: filter .35s ease, opacity .35s ease; }

/* Banner: CRAPS title + puck pill + one ellipsized message line (the mock's). */
.crp-banner { display: flex; align-items: center; gap: 10px; padding: 2px 2px 0; min-height: 34px; }
.crp-title { font-family: 'Playfair Display', Georgia, serif; font-weight: 900; font-size: clamp(20px,4.6vw,26px); letter-spacing: .04em; }
.crp-puck {
  flex: none; min-width: 52px; height: 26px; padding: 0 8px; border-radius: 14px; display: inline-flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 12px; letter-spacing: .08em; background: var(--pgr-panel,rgba(255,255,255,.05)); color: var(--pgr-dim,#83858b);
  border: 2px solid var(--pgr-line,rgba(255,255,255,.12));
}
.crp-puck.crp-puck-on { background: var(--pgr-sel,#1D9E75); color: var(--pgr-solid,#0d0e11); border-color: transparent; }
.crp-freepill { flex: none; font-size: 12px; font-weight: 700; color: var(--pgr-dim,#83858b); background: var(--pgr-panel,rgba(255,255,255,.05)); border: 1px solid var(--pgr-line,rgba(255,255,255,.1)); border-radius: 999px; padding: 3px 9px; }
/* The message line is the game's whole running commentary, so it never
   ellipsizes to one line — it wraps to two and clamps there (owner 2026-07-28:
   "Come-out roll — place your bets. The…" was unreadable on a phone). */
.crp-msg {
  flex: 1; font-size: 13px; color: var(--pgr-dim,#83858b); min-width: 0; line-height: 1.25;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
/* Phone stand-ins for the hidden topbar (see the @media block at the end). */
.crp-mbal  { display: none; flex: none; font-size: 12px; font-weight: 700; white-space: nowrap; }
.crp-mback {
  display: none; flex: none; width: 30px; height: 26px; border-radius: 8px; cursor: pointer;
  border: 2px solid var(--pgr-line,rgba(255,255,255,.12)); background: transparent;
  color: var(--pgr-dim,#83858b); font-family: var(--pgr-font,inherit); font-size: 15px; line-height: 1;
}

.crp-main { display: flex; flex-direction: column; gap: 12px; align-items: stretch; }

/* ── The felt ── */
.crp-board {
  position: relative; flex: 1 1 auto; min-width: 0; background: var(--pgr-panel,rgba(255,255,255,.05));
  border: 2px solid var(--pgr-line,rgba(255,255,255,.12)); border-radius: 18px; padding: 10px;
  display: flex; flex-direction: column; gap: 8px; font-family: 'Playfair Display', Georgia, serif;
}
.crp-board.crp-shake { animation: crp-board-shake .45s ease both; }
@keyframes crp-board-shake {
  0%,100% { transform: translate(0,0); }
  12% { transform: translate(-5px,2px) rotate(-.5deg); } 28% { transform: translate(5px,-2px) rotate(.5deg); }
  44% { transform: translate(-4px,-1px); } 60% { transform: translate(4px,2px) rotate(.3deg); } 78% { transform: translate(-2px,1px); }
}
.crp-row { display: flex; gap: 6px; min-width: 0; }
.crp-places { display: grid; grid-template-columns: repeat(6,minmax(0,1fr)); gap: 6px; }
.crp-props { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 6px; }

.crp-zone {
  position: relative; background: var(--pgr-seat,rgba(0,0,0,.2)); border: 2px solid var(--pgr-line,rgba(255,255,255,.12));
  border-radius: 10px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  cursor: pointer; user-select: none; min-width: 0; padding: 6px 4px; transition: border-color .15s, opacity .2s;
}
.crp-zone:hover { border-color: var(--pgr-sel,#1D9E75); }
.crp-zone.crp-haschip { border-color: var(--pgr-sel,#1D9E75); }
/* Closed zones stay TAPPABLE (a tap explains why, and take-down mode must reach
   a place bet that's "off" on the come-out) — they only dim.
   The dimming is on the zone's FELT CONTENT, never the zone itself: `opacity`
   on the zone creates a stacking context, which both faded your chip to 38%
   and trapped it UNDER the board's drama dim — so the chips falling off a
   seven-out were the hardest thing on screen to see. */
.crp-zone.crp-off > :not(.crp-chip):not(.crp-aichips):not(.crp-tilepuck) { opacity: .38; }

/* Settlement flash — green collected, red swept, amber pushed. Driven off the
   roll's events, so a zone only ever flashes when a chip of yours settled on it.
   The win green is a FIXED green, not --pgr-sel: won/lost is universal signage,
   and on the Arcade scheme the accent is blue — a win flashed blue on a blue
   board and read as nothing at all.
   z-index puts a flashing zone ABOVE the drama dim (z-index 1) — otherwise the
   hand-ending rolls, the ones whose flash matters most, dim the very zones they
   are announcing (same class of bug as the opacity stacking context). */
.crp-zone.crp-flash-win,
.crp-zone.crp-flash-lose,
.crp-zone.crp-flash-push { z-index: 6; }
.crp-zone.crp-falling    { z-index: 7; }   /* a chip mid-fall clears the flashes */
/* THREE keyframe stops, not four, and a timing function PER SEGMENT.
   The old curve was `ease` across 0% → 18% → 60% → 100%: CSS applies the
   timing function to EVERY segment, so the flash accelerated-then-braked three
   separate times, and the 60% stop (already most of the way back to base) put
   the longest brake right in the middle. That is the "staggering, laggy" middle
   the owner saw. Now: a fast ease-out up to the peak, then ONE unbroken
   ease-in-out decay to base. Same 620ms envelope, no kink. */
.crp-zone.crp-flash-win  { animation: crp-flash-win .62s both; }
.crp-zone.crp-flash-lose { animation: crp-flash-lose .62s both; }
.crp-zone.crp-flash-push { animation: crp-flash-push .62s both; }
@keyframes crp-flash-win {
  0%   { background: var(--pgr-seat,rgba(0,0,0,.2)); border-color: var(--pgr-line,rgba(255,255,255,.12));
         box-shadow: 0 0 0 -4px rgba(61,220,151,0); animation-timing-function: cubic-bezier(.2,.9,.35,1); }
  20%  { background: rgba(35,175,110,.62); border-color: #3ddc97;
         box-shadow: 0 0 22px -4px #3ddc97; animation-timing-function: cubic-bezier(.4,0,.4,1); }
  100% { background: var(--pgr-seat,rgba(0,0,0,.2)); border-color: var(--pgr-line,rgba(255,255,255,.12));
         box-shadow: 0 0 0 -4px rgba(61,220,151,0); }
}
@keyframes crp-flash-lose {
  0%   { background: var(--pgr-seat,rgba(0,0,0,.2)); border-color: var(--pgr-line,rgba(255,255,255,.12));
         box-shadow: 0 0 0 -4px rgba(214,51,47,0); animation-timing-function: cubic-bezier(.2,.9,.35,1); }
  20%  { background: rgba(214,51,47,.55); border-color: #d6332f;
         box-shadow: 0 0 22px -4px #d6332f; animation-timing-function: cubic-bezier(.4,0,.4,1); }
  100% { background: var(--pgr-seat,rgba(0,0,0,.2)); border-color: var(--pgr-line,rgba(255,255,255,.12));
         box-shadow: 0 0 0 -4px rgba(214,51,47,0); }
}
@keyframes crp-flash-push {
  0%   { background: var(--pgr-seat,rgba(0,0,0,.2)); border-color: var(--pgr-line,rgba(255,255,255,.12));
         animation-timing-function: cubic-bezier(.2,.9,.35,1); }
  28%  { background: rgba(214,160,47,.4); border-color: #d6a02f;
         animation-timing-function: cubic-bezier(.4,0,.4,1); }
  100% { background: var(--pgr-seat,rgba(0,0,0,.2)); border-color: var(--pgr-line,rgba(255,255,255,.12)); }
}

/* "Bet here before you can shoot" — the zones that open the roll gate breathe
   while the gate is shut. Everything else on the felt is greyed at that moment,
   so the contrast reads as an instruction. */
.crp-zone.crp-callout { animation: crp-breathe 1.9s ease-in-out infinite; }
@keyframes crp-breathe {
  0%,100% { border-color: var(--pgr-sel,#1D9E75); box-shadow: 0 0 0 0 rgba(0,0,0,0); }
  50%     { border-color: var(--pgr-sel,#1D9E75); box-shadow: 0 0 18px -2px var(--pgr-sel,#1D9E75); }
}
@media (prefers-reduced-motion: reduce) {
  .crp-zone.crp-callout { animation: none; border-color: var(--pgr-sel,#1D9E75); }
}
.crp-taking .crp-zone.crp-haschip { border-style: dashed; }

.crp-big { font-weight: 900; font-size: clamp(22px,5.6vw,32px); letter-spacing: .08em; white-space: nowrap; }
.crp-mid { font-style: italic; font-weight: 700; font-size: clamp(13px,3vw,16px); white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.crp-redink { color: color-mix(in oklch, var(--pgr-sel,#1D9E75) 65%, var(--pgr-txt,#fff)); }
.crp-pay { font-family: var(--pgr-font,'Outfit',sans-serif); font-style: italic; font-size: 10px; color: var(--pgr-dim,#83858b); white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }

.crp-place { min-height: 58px; }
.crp-place-num { font-style: italic; font-weight: 800; font-size: clamp(16px,4.6vw,24px); letter-spacing: .02em; white-space: nowrap; }
/* SIX / NINE are words, not numerals (the mock's smaller size) — a 24px serif
   "NINE" overflows a 1/6-width tile at 360px. */
.crp-place-word { font-size: clamp(11px,3.2vw,15px); }
.crp-place.crp-pointed { border-color: var(--pgr-sel,#1D9E75); box-shadow: 0 0 12px -4px var(--pgr-sel,#1D9E75); }
.crp-tilepuck {
  position: absolute; top: -8px; left: 8px; background: var(--pgr-sel,#1D9E75); color: var(--pgr-solid,#0d0e11);
  font-family: var(--pgr-font,'Outfit',sans-serif); font-weight: 800; font-size: 9px; letter-spacing: .1em;
  padding: 2px 7px; border-radius: 999px; box-shadow: 0 2px 6px rgba(0,0,0,.4);
}
/* COME no longer shares its row with the C&E circles, so it doesn't have to be
   tall enough for two of them — it's a band like the others, text centred. */
.crp-comerow { min-height: 56px; }
.crp-come { flex: 1; }
/* C & E: BOTH axes fixed. `flex:1` in a column stretched the height against a
   44px width and turned the circles into eggs (same bug the chips had). */
.crp-cecol { display: flex; flex-direction: column; justify-content: center; gap: 6px; flex: none; }
.crp-ce { width: 44px; height: 44px; flex: none; border-radius: 50%; padding: 0; }
.crp-ce-letter { font-weight: 800; font-size: 18px; color: var(--pgr-sel,#1D9E75); }
/* Pass + Odds stack in the row's flexible column; C & E sit in the slack. */
.crp-passrow { align-items: stretch; }
.crp-passcol { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
/* The C&E column (two 44px circles) is the taller of the two — let PASS LINE
   absorb the difference so the row's bottom edges line up. */
.crp-passcol .crp-pass { flex: 1; }
.crp-field { min-height: 56px; flex-direction: row; gap: 10px; padding: 8px 10px; flex-wrap: wrap; }
.crp-field-word { font-style: italic; font-weight: 900; font-size: clamp(16px,4.4vw,22px); color: var(--pgr-sel,#1D9E75); }
.crp-field-nums { font-weight: 800; font-size: clamp(13px,3.4vw,17px); letter-spacing: .12em; white-space: nowrap; }
.crp-dontrow .crp-dontpass { flex: 2; min-height: 38px; }
.crp-dontrow .crp-dontcome { flex: 1; min-height: 38px; }
.crp-pass { min-height: 54px; }
.crp-odds { min-height: 38px; flex-direction: row; gap: 10px; border-style: dashed; }
.crp-odds-word { font-style: normal; font-weight: 800; letter-spacing: .14em; }
.crp-prop { padding: 5px 3px; gap: 2px; }
.crp-prop-name { font-style: normal; font-weight: 700; color: var(--pgr-txt,#e9e8e4); opacity: .85; }
.crp-prop-odds { margin-top: -1px; }
.crp-prop-dice { display: flex; gap: 5px; align-items: center; }
.crp-prop-dice .dr-die, .crp-prop-dice .dr-wrap { flex: none; }
.crp-udie { font-size: 22px; line-height: 1; }

/* Your chip badge on a zone (top-right, the mock's dashed casino chip). */
.crp-chip {
  position: absolute; top: -8px; right: -4px; z-index: 5; min-width: 28px; height: 28px; padding: 0 5px; border-radius: 50%;
  background: var(--pgr-sel,#1D9E75); color: var(--pgr-solid,#0d0e11); display: flex; align-items: center; justify-content: center;
  font-family: var(--pgr-font,'Outfit',sans-serif); font-weight: 800; font-size: 11px;
  border: 2px dashed rgba(255,255,255,.75); box-shadow: 0 2px 6px rgba(0,0,0,.4);
}
.crp-chip.crp-fall { animation: crp-chip-fall .95s cubic-bezier(.4,0,.8,.4) both; }
/* Local confirmation of a tap — the chip is on the felt before the server has
   heard about it, so this pop IS the feedback. */
.crp-chip.crp-chip-pop { animation: crp-chip-pop .28s cubic-bezier(.34,1.56,.64,1); }
@keyframes crp-chip-pop { 0% { transform: scale(.5); opacity: .4; } 60% { transform: scale(1.22); } 100% { transform: scale(1); } }
@keyframes crp-chip-fall {
  0% { transform: translateY(0) rotate(0); opacity: 1; }
  15% { transform: translateY(-10px) rotate(-12deg); opacity: 1; }
  100% { transform: translateY(92vh) rotate(160deg); opacity: 0; }
}
/* ── Travelled come / don't-come points ──────────────────────────────────────
   A come bet that reaches a number is a CONTRACT you cannot take down, so it
   gets a real chip on the number it landed on — the dealer's own move — in the
   place tile's one free corner (bottom-right; top-left is the ON puck,
   top-right your place bet, bottom-left the AI cluster). It used to be a line
   of 10px grey text in the COME band, which is pay-string styling for money
   that is locked up, and left the deciding number blank. */
/* The contracts STACK like real chips instead of sitting side by side. A place
   tile is 48px wide at 360, and the bottom edge already spends its left half on
   the AI cluster — two 24px chips in a row overlapped it by 33px. Stacked, the
   slot is one chip wide however many contracts ride, and a chip overlapping a
   chip is what a real stack looks like anyway. */
.crp-riding {
  position: absolute; bottom: -8px; right: -4px; z-index: 5;
  display: flex; flex-direction: column-reverse; align-items: flex-end; gap: 0;
  font-family: var(--pgr-font,'Outfit',sans-serif); pointer-events: none;
}
.crp-ridechip {
  min-width: 24px; height: 24px; padding: 0 4px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 10px; box-shadow: 0 2px 6px rgba(0,0,0,.45);
}
/* column-reverse: the come chip sits on the bottom, its odds and the dark-side
   chip ride up over it. Negative margin toward the chip below = the overlap
   that makes it read as a stack instead of a column. */
.crp-riding > .crp-ride-odds,
.crp-riding > .crp-ride-dont { margin-bottom: -13px; }
/* In odds mode, the numbers you can actually back are the invitation. */
.crp-zone.crp-oddsable { border-style: dashed; border-color: var(--pgr-sel,#1D9E75); }
/* Solid ring, not the dashed one a placeable chip wears: dashed reads as
   "you can pick this back up", and that is exactly what a contract is not. */
.crp-ride-come { background: var(--pgr-sel,#1D9E75); color: var(--pgr-solid,#0d0e11); border: 2px solid rgba(255,255,255,.9); }
/* Odds behind that come point: the same accent, but hollow — on a real felt
   the odds chip is set on top of the flat bet and offset so both still read. */
.crp-ride-odds {
  background: var(--pgr-panel,#181b22); color: var(--pgr-sel,#1D9E75);
  border: 2px solid var(--pgr-sel,#1D9E75);
}
/* Dark side keeps the roster's don't-row treatment: inverted, muted, barred. */
.crp-ride-dont { background: #1b1d24; color: #cfd2da; border: 2px solid rgba(207,210,218,.55); }
.crp-ride-dont::after {
  content: ''; position: absolute; left: 3px; right: 3px; height: 2px;
  background: rgba(207,210,218,.75); transform: rotate(-38deg); border-radius: 2px;
}
.crp-ridechip { position: relative; }
/* The number you are live on reads as live even before it repeats. */
.crp-zone.crp-riding-on { border-color: var(--pgr-sel,#1D9E75); }
/* Landing bounce as the flying chip is handed over to the real badge. */
.crp-ridechip.crp-ride-land { animation: crp-ride-land .32s cubic-bezier(.34,1.56,.64,1); }
@keyframes crp-ride-land {
  0% { transform: scale(1.28); } 60% { transform: scale(.92); } 100% { transform: scale(1); }
}
/* The chip in flight: a floating copy over the board, so no tile's layout
   moves while it travels (the reserved-slot rule — the destination badge is
   already rendered underneath it, just hidden). */
.crp-ghostchip {
  position: absolute; z-index: 8; pointer-events: none; margin: 0;
  transition: transform .46s cubic-bezier(.4,.05,.25,1);
}

/* Other players' chips (bottom-left cluster, each bettor's own colour).
   Size and overlap are set INLINE by crapsFitChips — the gap here is only the
   uncrowded case; a crowded zone overlaps them into a peeking stack and then
   scales them down. Your own chips never take part in that. */
/* NO `gap` here: the spacing IS the pitch, written inline per chip. A CSS gap
   would be ADDED to that margin, so the stack came out wider than the space it
   was measured into — it overlapped the player's own chips by 7px. */
.crp-aichips { position: absolute; bottom: -8px; left: 8px; z-index: 5; display: flex; gap: 0; font-family: var(--pgr-font,'Outfit',sans-serif); pointer-events: none; }
.crp-aichip {
  min-width: 22px; height: 22px; padding: 0; border-radius: 50%; color: #fff; display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 9px; border: 2px dashed rgba(255,255,255,.7); box-shadow: 0 2px 5px rgba(0,0,0,.4);
  box-sizing: border-box; flex: none;
}
/* Last-resort stand-in for a tail that can't fit even at the size floor. */
.crp-aichip-more { background: rgba(20,22,28,.92); border-style: solid; border-color: rgba(255,255,255,.45); }
/* Drama dim overlay across the felt while a big-moment pill shows. Explicitly
   z-index:1 — it must sit ABOVE the felt and BELOW the chips (z-index:5), or
   the chips being swept away are dimmed by the very effect meant to spotlight
   them. */
.crp-dim { position: absolute; inset: 0; z-index: 1; background: rgba(0,0,0,.55); border-radius: 16px; opacity: 0; transition: opacity .3s ease; pointer-events: none; }
.crp-dim.crp-dim-on { opacity: 1; }

/* ── Side column: dice band + roll + tray ── */
.crp-side { flex: 0 0 auto; display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.crp-diceband {
  position: relative; background: var(--pgr-track,rgba(0,0,0,.3)); border: 2px solid var(--pgr-line,rgba(255,255,255,.12));
  border-radius: 16px; min-height: 96px; display: flex; align-items: center; justify-content: center; gap: 16px; z-index: 2;
}
.crp-dice { display: flex; gap: 16px; align-items: center; justify-content: center; }
.crp-dice .dr-die, .crp-dice .dr-wrap { flex: none; }
.crp-lasttotal { position: absolute; top: 7px; right: 11px; font-weight: 800; font-size: 14px; color: var(--pgr-dim,#83858b); }
.crp-dramapill {
  position: absolute; bottom: -13px; left: 50%; transform: translateX(-50%); z-index: 3;
  background: var(--pgr-sel,#1D9E75); color: var(--pgr-solid,#0d0e11); font-weight: 900; font-size: 15px; letter-spacing: .1em;
  padding: 5px 16px; border-radius: 999px; white-space: nowrap; box-shadow: 0 4px 14px rgba(0,0,0,.45);
}
.crp-dramapill.crp-drama-bad { background: #d6332f; color: #fff; }
.crp-dramapill.crp-drama-pop { animation: crp-settle-pop .5s ease; }
@keyframes crp-settle-pop { 0% { transform: translateX(-50%) scale(1); } 40% { transform: translateX(-50%) scale(1.16); } 100% { transform: translateX(-50%) scale(1); } }

.crp-roll-btn {
  border: none; border-radius: 14px; background: var(--pgr-btn,linear-gradient(180deg,#333841,#22252b)); color: var(--pgr-btnTxt,#e9e8e4);
  font-family: var(--pgr-font,inherit); font-weight: 800; font-size: clamp(16px,3.8vw,20px); letter-spacing: .06em; padding: 14px;
  cursor: pointer; box-shadow: 0 6px 16px rgba(0,0,0,.3); transition: filter .15s, opacity .2s;
  /* The label is swapped live (ROLL ⇄ the betting countdown ⇄ "<name> has the
     dice"), and a two-digit countdown wrapped it to two lines — which grew the
     button and shoved every control under it down the screen mid-hand (owner,
     2026-07-28). A control that changes size is a control you mis-tap: the
     label never wraps, it ellipsizes. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.crp-roll-btn:hover:not(:disabled) { filter: brightness(1.08); }
.crp-roll-btn:disabled { opacity: .55; cursor: default; }
.crp-banter { font-size: 12px; color: var(--pgr-dim,#83858b); text-align: center; min-height: 15px; }

.crp-tray { display: flex; gap: 8px; align-items: center; transition: opacity .25s; }
.crp-rolling .crp-tray { opacity: .4; pointer-events: none; }
.crp-rolling #craps-board { pointer-events: none; }
/* Fixed square box, NOT flex:1 — a flexed width against a fixed height made the
   "circle" an egg on a narrow phone (owner 2026-07-28). */
.crp-denom {
  flex: 0 0 44px; width: 44px; height: 44px; border-radius: 50%; border: 3px dashed rgba(255,255,255,.4);
  background: var(--pgr-seat,rgba(0,0,0,.2)); color: var(--pgr-txt,#e9e8e4); font-family: var(--pgr-font,inherit);
  font-weight: 800; font-size: 14px; cursor: pointer; box-shadow: 0 3px 8px rgba(0,0,0,.3);
}
.crp-denom.sel { border-color: var(--pgr-sel,#1D9E75); background: var(--pgr-sel,#1D9E75); color: var(--pgr-solid,#0d0e11); }
.crp-traybtn {
  border: 2px solid var(--pgr-line,rgba(255,255,255,.12)); background: transparent; color: var(--pgr-dim,#83858b);
  font-family: var(--pgr-font,inherit); font-weight: 700; font-size: 12px; padding: 9px 10px; border-radius: 10px; cursor: pointer;
  white-space: nowrap; transition: color .15s, border-color .15s;
}
.crp-traybtn:hover:not(:disabled) { color: var(--pgr-txt,#e9e8e4); border-color: var(--pgr-sel,#1D9E75); }
.crp-traybtn:disabled { opacity: .45; cursor: default; }
.crp-takedown-btn.crp-td-on, .crp-traybtn.crp-td-on { color: var(--pgr-txt,#e9e8e4); border-color: var(--pgr-sel,#1D9E75); border-style: dashed; }
.crp-cashout { margin-left: auto; }

/* Players popup (inline panel under the tray, the mock's). */
.crp-players { background: var(--pgr-panel,rgba(255,255,255,.05)); border: 2px solid var(--pgr-line,rgba(255,255,255,.12)); border-radius: 14px; padding: 10px; display: flex; flex-direction: column; gap: 7px; }
.crp-players-head { display: flex; align-items: baseline; gap: 8px; font-size: 11px; letter-spacing: .14em; color: var(--pgr-dim,#83858b); font-weight: 700; font-family: var(--pgr-font,inherit); }
.crp-players-legend { margin-left: auto; font-size: 9px; letter-spacing: .1em; opacity: .7; }
.crp-player { display: flex; align-items: center; gap: 9px; font-family: var(--pgr-font,inherit); padding: 2px 6px; margin: 0 -6px; border-radius: 8px; }
/* Dark side = betting against the shooter. The whole row goes darker, name to net. */
.crp-player-dark { background: rgba(0,0,0,.35); box-shadow: inset 0 0 0 1px rgba(255,255,255,.05); }
.crp-pdot { width: 13px; height: 13px; border-radius: 50%; flex: none; border: 2px dashed rgba(255,255,255,.6); }
.crp-pname { font-size: 14px; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crp-shooter { background: var(--pgr-sel,#1D9E75); color: var(--pgr-solid,#0d0e11); font-size: 9px; font-weight: 800; letter-spacing: .1em; padding: 3px 7px; border-radius: 999px; flex: none; }
/* What's riding + where they stand — never the bankroll (see crapsRenderPlayers). */
.crp-pbet { margin-left: auto; font-weight: 600; font-size: 12px; color: var(--pgr-dim,#83858b); white-space: nowrap; }
.crp-pnet { flex: none; min-width: 54px; text-align: right; font-weight: 800; font-size: 13px; color: var(--pgr-dim,#83858b); white-space: nowrap; }
.crp-pnet.crp-pnet-up   { color: var(--pgr-sel,#1D9E75); }
.crp-pnet.crp-pnet-down { color: #E24B4A; }

/* Desktop: board + side column go side by side and the shell widens (the
   mock's PC view). The property rule beats the var-driven .gm-shell max-width. */
@media (min-width: 940px) {
  #craps-game-screen .gm-shell { max-width: 1060px; }
  #craps-game-screen .crp-main { flex-direction: row; }
  #craps-game-screen .crp-side { flex: 0 0 300px; }
  #craps-game-screen .crp-diceband { min-height: 150px; order: 1; }
  /* The controls wrapper exists for the phone's 3-column row; on desktop it
     dissolves (display:contents) and its children stack in the side column in
     the order they always did — hence the explicit `order` on every child. */
  #craps-game-screen .crp-controls { display: contents; }
  #craps-game-screen .crp-roll-btn { order: 2; }
  #craps-game-screen .crp-banter   { order: 3; }
  #craps-game-screen #craps-tray   { order: 4; }
  #craps-game-screen .crp-tray2    { order: 5; }
  #craps-game-screen .crp-tray3    { order: 6; }
  #craps-game-screen .crp-players  { order: 7; }
  /* Phone sizes were being read on a 27" monitor — give the text room. */
  #craps-game-screen .crp-msg      { font-size: 19px; }
  #craps-game-screen .crp-banter   { font-size: 14px; min-height: 18px; }
  #craps-game-screen .crp-pay       { font-size: 12px; }
  /* MUST come after the generic .crp-pay above — same specificity (1,0,1), so
     load order is what decides (the reskin-tie rule). */
  #craps-game-screen .crp-prop      { padding: 9px 4px; gap: 3px; min-height: 82px; }
  #craps-game-screen .crp-prop-name { font-size: 14px; }
  #craps-game-screen .crp-prop-odds { font-size: 12px; }
  #craps-game-screen .crp-pbet     { font-size: 13px; }
  #craps-game-screen .crp-pnet     { font-size: 14px; }
}

/* Phone: every pixel is board. The global topbar is hidden (its balance and
   its way out move into the banner), the wordmark drops so the puck leads the
   banner, and the dice card becomes a middle column with the chip tray on one
   side and the table actions on the other — so felt, dice and controls fit one
   screen (owner 2026-07-28). */
@media (max-width: 939px) {
  #craps-game-screen .topbar    { display: none; }
  /* ☰ brings the real topbar back rather than a bespoke menu — it already
     carries Store / Profile / notifications / ← Games, including the shared
     #global-topbar-extras block placeGlobalExtras moves into it. */
  #craps-game-screen.crp-topbar-open .topbar { display: flex; }
  #craps-game-screen .crp-title { display: none; }
  #craps-game-screen .crp-mbal  { display: inline; }
  #craps-game-screen .crp-mback { display: block; }
  /* Two rows: [puck · free pill · balance · back] then the message across the
     FULL width. Sharing one row with the balance left the message ~200px wide
     and it still ellipsized — the point of dropping the wordmark was to make
     the commentary readable, so it gets the whole line. */
  #craps-game-screen .crp-banner { align-items: center; flex-wrap: wrap; gap: 6px 8px; padding-top: 4px; }
  #craps-game-screen .crp-mbal   { margin-left: auto; }
  #craps-game-screen .crp-msg    { flex: 1 0 100%; order: 9; }

  #craps-game-screen .crp-controls { display: flex; align-items: stretch; gap: 8px; }
  /* Chips: a 2×2 block, so the column is exactly two chips wide. */
  #craps-game-screen #craps-tray {
    flex: 0 0 auto; display: grid; grid-template-columns: repeat(2,44px); gap: 6px; align-content: center;
  }
  /* Shorter card — its height is set by the taller of the two side columns
     anyway (3 stacked action buttons), so this just stops it dominating. */
  #craps-game-screen .crp-diceband { flex: 1 1 auto; min-width: 0; min-height: 84px; }
  #craps-game-screen .crp-dice { gap: 12px; }
  #craps-game-screen .crp-tray2 {
    flex: 0 0 auto; flex-direction: column; justify-content: center; gap: 6px;
  }
  #craps-game-screen .crp-tray2 .crp-traybtn { width: 100%; padding: 7px 8px; font-size: 11px; }
  #craps-game-screen .crp-cashout { margin-left: 0; }
  #craps-game-screen .crp-tray3 { justify-content: center; }
  #craps-game-screen .crp-side { gap: 8px; }
  #craps-game-screen .crp-main { gap: 8px; }

  /* Felt trim: a few px off each band, which is what buys the ROLL button a
     place above the chat bar. Nothing here changes a zone's content — only its
     slack — so the SIX/NINE word-tile fit at 360px is untouched. */
  #craps-game-screen .crp-board   { padding: 8px; gap: 6px; }
  #craps-game-screen .crp-props   { gap: 5px; }
  #craps-game-screen .crp-prop    { padding: 3px 2px; gap: 1px; }
  #craps-game-screen .crp-prop .crp-pay { line-height: 1.15; }
  #craps-game-screen .crp-place   { min-height: 54px; }
  #craps-game-screen .crp-comerow { min-height: 50px; }
  #craps-game-screen .crp-field   { min-height: 48px; }
  #craps-game-screen .crp-dontrow .crp-zone { min-height: 34px; padding: 4px; }
  #craps-game-screen .crp-pass    { min-height: 48px; }
  #craps-game-screen .crp-odds    { min-height: 34px; }
}

/* ── Craps MP (slice 3b): window strip, dice hand-off flip, end-screen stats ── */
.crp-mpstrip { display: flex; flex-direction: column; gap: 6px; }
.crp-rolling .crp-mpstrip { opacity: .5; }
.crp-mp-hand { font-size: 12px; color: var(--pgr-dim, #9a9aa2); text-align: center; }
.crp-mp-hand b { color: var(--pgr-txt, #fff); font-weight: 800; }
.crp-mp-timer { position: relative; height: 22px; border-radius: 999px; overflow: hidden;
  background: var(--pgr-seat, rgba(255,255,255,.06)); border: 1px solid var(--pgr-line, rgba(255,255,255,.12)); }
.crp-mp-timerfill { position: absolute; inset: 0; width: 0%;
  background: var(--pgr-sel, #3D6FE0); opacity: .35; transition: width .45s linear, opacity .3s ease; }
/* A closed window FADES the bar rather than draining it. Draining animated a
   450ms right-to-left sweep every time the window closed — and since a window
   with nobody else betting is already "closed", the bar swept out and back in
   on every chip (owner: "it refills or something"). Time is the bar's only
   job; whether the shooter may roll is the text's. */
.crp-mp-timer-closed .crp-mp-timerfill { opacity: .08; }
.crp-mp-timertext { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; letter-spacing: .04em; color: var(--pgr-txt, #e8e8ea); white-space: nowrap; }
/* Three equal slots. The desktop side column is only 300px wide, so the row
   runs a size down from the tray buttons to keep all three labels intact
   rather than ellipsized. */
.crp-mp-btnrow { display: flex; gap: 6px; }
.crp-mp-btnrow .crp-traybtn {
  flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  font-size: 11px; padding: 9px 4px;
}
/* The contextual third slot. `visibility` (not display) keeps its width in the
   row whether or not the control applies, so nothing under the strip ever
   moves — and the flip is what draws the eye when it does apply. */
.crp-mp-pass.crp-mp-slot-empty { visibility: hidden; pointer-events: none; }
@keyframes crpSlotIn  { from { transform: rotateX(-90deg); opacity: 0; } }
@keyframes crpSlotOut { to   { transform: rotateX(90deg);  opacity: 0; } }
.crp-mp-pass.crp-mp-slot-in  { animation: crpSlotIn .26s ease-out; }
.crp-mp-pass.crp-mp-slot-out { animation: crpSlotOut .2s ease-in forwards; }
@media (prefers-reduced-motion: reduce) {
  .crp-mp-pass.crp-mp-slot-in, .crp-mp-pass.crp-mp-slot-out { animation: none; }
}
@media (min-width: 940px) {
  /* Desktop side column runs on explicit `order` (crp-controls dissolves);
     the strip sits directly above the ROLL button, which shares order 2 —
     DOM order breaks the tie in the strip's favour. */
  #craps-game-screen .crp-mpstrip { order: 2; }
  /* The column is 300px, so three slots are ~96px each and the longest label
     ("⏭ Retire shooter") lands exactly on that edge. A step down here buys the
     margin; the phone strip is board-wide and keeps the larger text. */
  #craps-game-screen .crp-mp-btnrow .crp-traybtn { font-size: 10px; padding: 9px 3px; }
}
/* Shooter hand-off: the dice flip away and return in the new shooter's skin.
   Only #craps-dice animates, inside the diceband's fixed slot — zero layout shift. */
@keyframes crpFlipOut { to { transform: rotateY(90deg); opacity: 0; } }
@keyframes crpFlipIn  { from { transform: rotateY(-90deg); opacity: 0; } }
#craps-dice.crp-flipout { animation: crpFlipOut .22s ease-in forwards; }
#craps-dice.crp-flipin  { animation: crpFlipIn .24s ease-out; }
/* End-screen stat lines: one stat per row, staggered in via rb-hidden. */
.crp-endstats { display: flex; flex-direction: column; gap: 7px; margin: 2px 0; }
.crp-endstat { display: flex; justify-content: space-between; gap: 14px; font-size: 14px;
  color: #cfd0d3; transition: opacity .3s ease, transform .3s ease; }
.crp-endstat-l { color: var(--pgr-dim, #9a9aa2); }
.crp-endstat-v { font-weight: 700; font-variant-numeric: tabular-nums; }
