/* GTO Drill — vanilla styling, dark theme, mobile-first. */

:root {
  --bg: #0f1a24;
  --bg-elev: #16222e;
  --bg-elev-2: #1c2c39;
  --text: #e6edf3;
  --text-dim: #8aa1b1;
  --accent: #4cb6ff;
  --accent-soft: #1e3a52;
  --hero: #4cb6ff;
  --hero-soft: rgba(76, 182, 255, 0.2);
  --villain: #e8a93f;
  --villain-soft: rgba(232, 169, 63, 0.2);
  --ok: #4ade80;
  --miss: #f87171;
  --border: #243646;
  --radius: 10px;
  --radius-box: 4px;        /* tags & badges — a box, not a pill (§2.4 shape pass) */
  --gap: 12px;
  --pad: 16px;
  --font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Mobile-affordance tokens. --border (#243646) is reserved for
     DIVIDERS only — it measures ~1.3:1 against the panels, below the
     3:1 WCAG non-text-contrast minimum, so anything tappable must use
     --border-interactive instead. */
  --border-interactive: #3a5670; /* visible border for tappable elements */
  --border-tap: #2f4658;         /* cell-level border (matrix cells) */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.30);
  --shadow-2: 0 2px 6px rgba(0, 0, 0, 0.35);
  --shadow-3: 0 4px 12px rgba(0, 0, 0, 0.45);
  --glow-accent: 0 0 8px rgba(76, 182, 255, 0.45);
  --glow-villain: 0 0 8px rgba(232, 169, 63, 0.40);
  --press: transform 0.08s ease;
  --tap-min: 44px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-stack);
  font-size: 16px;
  line-height: 1.45;
  min-height: 100vh;
  -webkit-text-size-adjust: 100%;
  /* Kill iOS Safari's double-tap-to-zoom. Pinch-zoom (the accessibility
     one) still works because we don't set user-scalable=no on the
     viewport meta. The * selector below is needed because iOS Safari
     checks `touch-action` on the EVENT TARGET, not its ancestors —
     so html/body alone isn't enough to suppress double-tap on a
     button or other interactive element. */
  touch-action: manipulation;
}
/* Apply touch-action: manipulation to EVERY element so double-tap-zoom
   is suppressed no matter what gets tapped. Pinch-zoom unaffected. */
*, *::before, *::after {
  touch-action: manipulation;
}

body { padding-bottom: 4rem; }

a { color: var(--accent); }

.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  max-width: 720px;
  margin: 0 auto;
  padding: 14px 16px;
}
.app-title {
  font-size: 1.4rem;
  margin: 0;
  letter-spacing: 0.02em;
}
.app-version {
  font-size: 0.62rem;
  font-weight: 500;
  color: var(--text-dim);
  letter-spacing: 0.04em;
  margin-left: 6px;
  vertical-align: 4px;
  cursor: help;
}
.app-version:empty { display: none; }
.header-user {
  display: flex;
  align-items: center;
  gap: 8px;
}
.header-user .avatar { width: 30px; height: 30px; }
.header-user .avatar-fallback { font-size: 0.95rem; }
.header-user-name {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-dim);
}

.app-root {
  max-width: 720px;
  margin: 0 auto;
  padding: 16px;
}

.app-footer {
  max-width: 720px;
  margin: 24px auto 0 auto;
  padding: 0 16px 16px 16px;
  text-align: center;
}

.muted { color: var(--text-dim); }

/* Buttons */

button {
  font: inherit;
  cursor: pointer;
  background: var(--bg-elev-2);
  color: var(--text);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius);
  padding: 10px 14px;
  min-height: 44px;
  transition: transform 0.08s ease, filter 0.12s ease,
              background 0.12s ease, border-color 0.12s ease;
}
@media (hover: hover) {
  button:hover { background: var(--bg-elev); }
}
button.primary {
  background: var(--accent);
  color: #061018;
  font-weight: 600;
  border-color: var(--accent);
  /* Tier-3 depth — the primary action lifts off the panel. */
  box-shadow: 0 2px 8px rgba(76, 182, 255, 0.25);
}
@media (hover: hover) {
  button.primary:hover { background: #6cc4ff; }
}
button.secondary {
  background: transparent;
  border-color: var(--accent);
  color: var(--accent);
}
button:disabled { opacity: 0.5; cursor: not-allowed; }

/* Inputs */

input, textarea {
  width: 100%;
  font: inherit;
  background: var(--bg-elev);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin-bottom: var(--gap);
}
input[type="number"] { width: 100%; }
label, .field-label {
  display: block;
  font-size: 0.92rem;
  color: var(--text-dim);
  margin-bottom: 4px;
}
.input-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap);
}
form { margin-top: 8px; }

/* Landing */
.landing { padding: 16px 0; }
.landing h1.appname { display: none; } /* the app header already shows the name */
.landing .tagline { color: var(--text-dim); margin-bottom: 24px; }
.landing-actions {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  margin-bottom: 24px;
}
.how-it-works {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad);
}
.how-it-works ol {
  margin: 8px 0 0 0;
  padding-left: 20px;
}

/* Create / Join */
.create-game h2, .join-game h2, .waiting h2 { margin-top: 4px; }

/* Players screen — roster of everyone with their library completion
   and GTO accuracy. */
.players-view h2 { margin-top: 4px; }
.players-hint { font-size: 0.88rem; margin: 0 0 6px 0; }
.players-status { font-size: 0.85rem; margin: 6px 0 12px 0; }
.players-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.players-card {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.players-card.is-me {
  border-color: rgba(120, 180, 255, 0.5);
  background: rgba(120, 180, 255, 0.06);
}
.players-card.is-clickable { cursor: pointer; }
.players-card.is-clickable:focus {
  background: var(--bg-elev-2);
  outline: none;
}
@media (hover: hover) {
  .players-card.is-clickable:hover { background: var(--bg-elev-2); }
  .players-card.is-me.is-clickable:hover { background: rgba(120, 180, 255, 0.1); }
}
.players-avatar { flex: 0 0 auto; }
.players-main { flex: 1 1 auto; min-width: 0; }
.players-name { font-weight: 700; font-size: 0.98rem; }
.players-stats { font-size: 0.84rem; margin-top: 2px; }
.players-go {
  flex: 0 0 auto;
  font-size: 1.4rem;
  color: var(--text-dim);
  line-height: 1;
}
.players-actions { margin-top: 16px; }
.players-back { width: 100%; }

/* Owner-only Database console — overview stats, comments table, and
   per-scenario coverage. */
.database-view h2 { margin-top: 4px; }
.db-hint { font-size: 0.88rem; margin: 0 0 10px 0; }
.db-status { font-size: 0.85rem; margin: 6px 0 12px 0; }
.db-overview {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: 18px;
}
.db-stat {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 8px;
  text-align: center;
}
.db-stat-num { font-size: 1.3rem; font-weight: 800; color: var(--accent); }
.db-stat-label {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-dim);
  margin-top: 3px;
}
.db-section-h {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  margin: 18px 0 8px 0;
}
.db-empty { font-size: 0.85rem; }
.db-comments { display: flex; flex-direction: column; gap: 8px; }
.db-comment {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 9px 11px;
}
.db-comment-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 4px;
}
.db-comment-link { font-weight: 700; font-size: 0.9rem; white-space: nowrap; }
.db-comment-title { font-size: 0.82rem; color: var(--text-dim); }
.db-comment-meta {
  margin-left: auto;
  font-size: 0.72rem;
  color: var(--text-dim);
  white-space: nowrap;
}
.db-comment-text { margin: 0; font-size: 0.9rem; line-height: 1.45; }
/* The answer a Database-console comment was written about. */
.db-comment-ctx {
  margin: 0 0 3px 0;
  font-size: 0.72rem;
  color: var(--text-dim);
}
.db-coverage {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
/* Each coverage row = the scenario link + an optional owner "Solver"
   export button beside it; the wrapper carries the row divider. */
.db-cov-rowwrap {
  display: flex;
  align-items: stretch;
  border-bottom: 1px solid var(--border);
}
.db-cov-rowwrap:last-child { border-bottom: none; }
.db-cov-row {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  font-size: 0.85rem;
  color: var(--text);
  text-decoration: none;
}
@media (hover: hover) {
  .db-cov-row:hover { background: var(--bg-elev); }
}
/* Owner export — the per-scenario TexasSolver-config download button. */
.db-cov-solver {
  flex: 0 0 auto;
  align-self: stretch;
  min-height: 0;
  padding: 0 11px;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--text-dim);
  background: none;
  border: none;
  border-left: 1px solid var(--border);
  border-radius: 0;
  cursor: pointer;
  white-space: nowrap;
}
@media (hover: hover) {
  .db-cov-solver:hover { background: var(--bg-elev); color: var(--accent); }
}
/* Per-row GTO+ setup button — opens the walkthrough modal jumped to this
   scenario. Visually parallel to .db-cov-solver but tinted hero-soft so the
   two distinct actions don't look like a single button. */
.db-cov-gtop {
  flex: 0 0 auto;
  align-self: stretch;
  min-height: 0;
  padding: 0 11px;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--accent);
  background: none;
  border: none;
  border-left: 1px solid var(--border);
  border-radius: 0;
  cursor: pointer;
  white-space: nowrap;
}
@media (hover: hover) {
  .db-cov-gtop:hover { background: var(--hero-soft); }
}

/* GTO+ batch banner — the "open walkthrough" CTA at the top of the
   coverage list, only mounted when there are postflop scenarios to solve. */
.db-gtop-banner {
  background: var(--hero-soft);
  border: 1px solid rgba(76, 182, 255, 0.4);
  border-radius: 10px;
  padding: 12px 14px;
  margin-bottom: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.db-gtop-banner-text {
  font-size: 0.84rem;
  line-height: 1.45;
  color: var(--text);
}
.db-gtop-banner-text strong { color: var(--accent); }
.db-gtop-start {
  align-self: flex-start;
  background: var(--accent);
  color: #061018;
  border: 0;
  border-radius: 999px;
  padding: 8px 16px;
  font-size: 0.86rem;
  font-weight: 700;
  cursor: pointer;
}
.db-gtop-start:active { transform: scale(0.98); filter: brightness(1.08); }

/* GTO+ setup modal — the per-scenario paste-and-click walkthrough.
   Same visual language as the scenario-briefing modal (sb-* classes) for
   consistency, but a separate class namespace so layout tweaks here don't
   ripple to the briefing. */
.gtop-scrim {
  position: fixed;
  inset: 0;
  background: rgba(5, 9, 13, 0.78);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 18px;
  z-index: 1000;
  animation: gtop-scrim-in 140ms ease-out;
}
@keyframes gtop-scrim-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.gtop-modal {
  width: 100%;
  max-width: 520px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  background: var(--bg-elev);
  border: 1px solid var(--border-interactive);
  border-radius: 14px;
  padding: 18px 18px 14px;
  box-shadow: var(--shadow-3), 0 20px 50px rgba(0, 0, 0, 0.7);
  animation: gtop-modal-in 180ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
@keyframes gtop-modal-in {
  from { opacity: 0; transform: translateY(8px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.gtop-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 12px;
}
.gtop-head-titles { flex: 1 1 auto; min-width: 0; }
.gtop-eyebrow {
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  color: var(--accent);
  margin-bottom: 4px;
}
.gtop-title {
  font-size: 1.05rem;
  font-weight: 800;
  margin: 0 0 2px;
  color: var(--text);
}
.gtop-subtitle {
  font-size: 0.78rem;
  margin: 0;
}
.gtop-close {
  flex: 0 0 28px;
  width: 28px;
  height: 28px;
  background: transparent;
  border: 1px solid var(--border-interactive);
  border-radius: 6px;
  color: var(--text-dim);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.gtop-close:hover { background: var(--bg-elev-2); color: var(--text); }
.gtop-context {
  font-size: 0.74rem;
  padding: 8px 10px;
  background: var(--bg-elev-2);
  border-radius: 6px;
  margin-bottom: 12px;
}

.gtop-steps {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin-bottom: 14px;
}
.gtop-step {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
}
.gtop-step-instruction {
  background: var(--bg-elev-2);
  border-style: dashed;
}
.gtop-step-head {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 5px;
}
.gtop-step-num {
  font-size: 0.66rem;
  font-weight: 800;
  color: var(--accent);
  letter-spacing: 0.04em;
}
.gtop-step-label {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--text);
}
.gtop-step-row {
  display: flex;
  align-items: stretch;
  gap: 7px;
}
.gtop-step-value {
  flex: 1 1 auto;
  min-width: 0;
  padding: 7px 9px;
  background: var(--bg);
  border: 1px solid var(--border-interactive);
  border-radius: 5px;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 0.74rem;
  color: var(--text);
  white-space: nowrap;
  overflow-x: auto;
  /* don't break user-selectable copy via mouse */
  user-select: text;
}
.gtop-step-hint {
  font-size: 0.7rem;
  margin-top: 6px;
  line-height: 1.4;
}
.gtop-copy-btn {
  flex: 0 0 auto;
  padding: 0 12px;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--accent);
  background: var(--bg);
  border: 1px solid rgba(76, 182, 255, 0.4);
  border-radius: 5px;
  cursor: pointer;
  min-width: 64px;
}
.gtop-copy-btn:hover { background: var(--hero-soft); }
.gtop-copy-btn.is-ok { background: rgba(74, 222, 128, 0.18); border-color: var(--ok); color: var(--ok); }
.gtop-copy-btn.is-err { background: rgba(248, 113, 113, 0.15); border-color: var(--miss); color: var(--miss); }

.gtop-nav {
  display: flex;
  align-items: center;
  gap: 10px;
  border-top: 1px solid var(--border);
  padding-top: 12px;
}
.gtop-nav-spacer {
  flex: 1 1 auto;
  text-align: center;
  font-size: 0.74rem;
}
.gtop-nav-btn {
  flex: 0 0 auto;
  min-height: 36px;
  padding: 0 14px;
  background: var(--bg-elev-2);
  color: var(--text);
  border: 1px solid var(--border-interactive);
  border-radius: 999px;
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 700;
  cursor: pointer;
}
.gtop-nav-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.gtop-nav-btn.primary {
  background: var(--accent);
  color: #061018;
  border-color: var(--accent);
}
.gtop-nav-btn.primary:active { transform: scale(0.98); filter: brightness(1.08); }

@media (prefers-reduced-motion: reduce) {
  .gtop-scrim, .gtop-modal { animation: none; }
}

.db-cov-row.is-empty { color: var(--text-dim); }
.db-cov-num { flex: 0 0 auto; font-weight: 700; color: var(--accent); min-width: 42px; }
.db-cov-row.is-empty .db-cov-num { color: var(--text-dim); }
.db-cov-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.db-cov-count { flex: 0 0 auto; font-size: 0.78rem; color: var(--text-dim); white-space: nowrap; }
.db-actions { margin-top: 18px; }
.db-back { width: 100%; }
/* The 4-across overview stats are too tight on a phone — stack 2×2. */
@media (max-width: 480px) {
  .db-overview { grid-template-columns: repeat(2, 1fr); }
}

/* Player profile — aggression bias, per-concept accuracy, confidence
   calibration, synthesised from a player's recorded responses. */
.profile-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.profile-avatar { flex: 0 0 auto; }
.profile-name { font-size: 1.15rem; font-weight: 700; }
.profile-sub { font-size: 0.85rem; margin-top: 2px; }
.profile-empty { margin: 20px 0; }
.profile-stat-big {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 12px 14px;
  background: rgba(120, 180, 255, 0.06);
  border: 1px solid rgba(120, 180, 255, 0.3);
  border-radius: var(--radius);
  margin-bottom: 16px;
}
.profile-stat-num { font-size: 1.8rem; font-weight: 800; color: var(--accent); }
.profile-stat-label { font-size: 0.85rem; }
.profile-section { margin-bottom: 20px; }
.profile-section-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 6px;
}
.profile-section-hint { font-size: 0.8rem; margin: 0 0 8px 0; }
.profile-verdict {
  font-size: 0.92rem;
  line-height: 1.5;
  margin: 6px 0 0 0;
  color: var(--text);
}

/* Aggression meter — a passive↔aggressive track with a marker. */
.aggr-meter { margin: 4px 0; }
.aggr-meter-track {
  position: relative;
  height: 10px;
  border-radius: 999px;
  background: linear-gradient(90deg,
    rgba(120, 180, 255, 0.4) 0%,
    rgba(255, 255, 255, 0.12) 50%,
    rgba(255, 184, 90, 0.5) 100%);
}
.aggr-meter-marker {
  position: absolute;
  top: 50%;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--text);
  border: 2px solid var(--bg);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  transform: translate(-50%, -50%);
}
.aggr-meter-marker.aggr-tone-hot { background: #ffb85a; }
.aggr-meter-marker.aggr-tone-cold { background: #5cb7ff; }
.aggr-meter-marker.aggr-tone-even { background: #6fd9a0; }
.aggr-meter-ends {
  display: flex;
  justify-content: space-between;
  font-size: 0.72rem;
  margin-top: 5px;
}
.aggr-meter-ends span:nth-child(2) { opacity: 0.7; }

/* Per-concept accuracy bars. */
.profile-concepts {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.profile-concept {
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.02);
}
.profile-concept.is-flagged { border-color: rgba(255, 213, 138, 0.4); }
.profile-concept-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
.profile-concept-name { font-weight: 600; font-size: 0.92rem; }
.profile-concept-pct { font-size: 0.8rem; margin-left: auto; white-space: nowrap; }
.profile-concept-flag {
  font-size: 0.68rem;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: var(--radius-box);
  white-space: nowrap;
}
.profile-concept-flag.is-blindspot {
  color: #ffd58a;
  background: rgba(255, 213, 138, 0.14);
  border: 1px solid rgba(255, 213, 138, 0.5);
}
.profile-concept-flag.is-strength {
  color: #6fd9a0;
  background: rgba(76, 200, 130, 0.14);
  border: 1px solid rgba(76, 200, 130, 0.5);
}
.profile-concept-bar {
  height: 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  overflow: hidden;
}
.profile-concept-fill {
  height: 100%;
  background: rgba(120, 180, 255, 0.5);
  transition: width 0.3s ease;
}
.profile-concept-fill.is-good { background: rgba(76, 200, 130, 0.55); }
.profile-concept-fill.is-weak { background: rgba(240, 130, 90, 0.55); }
.profile-actions { margin-top: 18px; }
.profile-back { width: 100%; }

/* Lobby list (Join screen) */
.lobby-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 6px;
}
/* Each lobby row = the big join-target card + a small delete (×)
   button on the right. Wrapping the two in a flex row keeps the card
   clickable for "join" without nesting buttons (invalid HTML). */
.lobby-row {
  display: flex;
  align-items: stretch;
  gap: 8px;
}
.lobby-card {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  background: var(--bg-elev);
  border: 1px solid var(--border);
}
@media (hover: hover) {
  .lobby-card:hover { background: var(--bg-elev-2); }
}
.lobby-delete {
  flex: 0 0 auto;
  width: 38px;
  align-self: stretch;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  border-radius: var(--radius);
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
@media (hover: hover) {
  .lobby-delete:hover {
    background: rgba(240, 100, 100, 0.12);
    border-color: rgba(240, 100, 100, 0.5);
    color: #f3c0c0;
  }
}
.lobby-delete:disabled { opacity: 0.5; cursor: not-allowed; }
.lobby-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.lobby-detail { font-size: 0.82rem; }
.lobby-go { flex: 0 0 auto; color: var(--accent); font-weight: 600; }
/* Back-to-home button on the Join screen — sits below the list +
   error box. The original lobby browser was a dead-end without it. */
.join-actions { margin-top: 16px; }
.join-back { width: 100%; }
.avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  flex: 0 0 auto;
  object-fit: cover;
  background: var(--bg-elev-2);
}
.avatar-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  color: var(--accent);
  font-size: 1.15rem;
}

.waiting .secondary { margin-top: 12px; }

/* "Waiting for opponent" screen affordances — added so the user has
   a way out (back-to-home button) and a signal about whether the
   opponent is still engaged (last-submitted activity line + a stalled
   warning badge when silence exceeds 7 days). */
.waiting .waiting-stalled-badge {
  display: inline-block;
  margin: 8px 0 4px 0;
  padding: 5px 10px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #ffd58a;
  background: rgba(255, 213, 138, 0.10);
  border: 1px solid rgba(255, 213, 138, 0.45);
  border-radius: var(--radius-box);
}
.waiting .waiting-activity {
  font-size: 0.95rem;
  margin: 6px 0 12px 0;
}
.waiting .waiting-activity.is-stalled {
  color: #ffd58a;
}
.waiting .waiting-actions {
  margin-top: 20px;
  display: flex;
  gap: 10px;
}
.waiting .waiting-back {
  flex: 1 1 auto;
  min-height: 42px;
}
.waiting.is-stalled {
  border-left: 3px solid #ffd58a;
  padding-left: 14px;
  background: rgba(255, 213, 138, 0.04);
}

/* In-game */
.game-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0 16px 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
  flex-wrap: wrap;
  gap: 8px;
}

/* "No opponent yet" invite banner — informational strip shown inside
   the in-game view when the second seat hasn't been filled. Lets the
   creator know they can play their batch right now and the opponent
   will catch up on their own time. Cool-blue accent so it reads as
   info, not a warning. */
.invite-banner {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 14px;
  margin-bottom: 14px;
  background: rgba(74, 138, 204, 0.08);
  border: 1px solid rgba(74, 138, 204, 0.35);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
}
.invite-banner-icon {
  font-size: 1.15rem;
  line-height: 1.4;
  flex-shrink: 0;
}
.invite-banner-body { flex: 1 1 auto; min-width: 0; }
.invite-banner-title {
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--text);
}
.invite-banner-text {
  font-size: 0.85rem;
  line-height: 1.4;
  margin-top: 2px;
}
.scenario-list {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.scenario-card, .reveal-card, .disagreement-card {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad);
}
.scenario-card h3, .reveal-card h3, .disagreement-card h4 { margin-top: 0; }
.scenario-desc { margin: 4px 0 10px 0; }
.board, .action-history { font-size: 0.92rem; color: var(--text-dim); margin-bottom: 8px; }

.actions-row, .confidence-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 4px 0 14px 0;
}
.action-btn, .conf-btn {
  flex: 0 1 auto;
  padding: 8px 12px;
  border-radius: var(--radius);
  background: var(--bg-elev-2);
  border: 1px solid var(--border-interactive);
  min-height: 44px;
}
.action-btn.selected, .conf-btn.selected {
  background: var(--accent);
  color: #061018;
  border-color: var(--accent);
  font-weight: 600;
  /* Tier-3 — the chosen move/confidence reads as the committed action. */
  box-shadow: 0 2px 8px rgba(76, 182, 255, 0.25);
}
.conf-btn { min-width: 44px; }

.note-input { min-height: 56px; resize: vertical; }

.submit-handful { margin-top: 8px; width: 100%; }

.error { color: var(--miss); margin-top: 8px; min-height: 1.2em; }

/* In-game — decision screen (one hand at a time) */
.hand-progress {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}
.hand-count { font-size: 0.9rem; font-weight: 600; color: var(--text-dim); }
.hand-dots { display: flex; gap: 6px; }
.hand-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--bg-elev-2); border: 1px solid var(--border);
}
.hand-dot.is-done { background: var(--accent-soft); border-color: var(--accent); }
.hand-dot.is-current { background: var(--accent); border-color: var(--accent); }

.hand-card {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad);
}
.hand-words { margin-top: 10px; font-size: 0.9rem; }
.hand-words summary { cursor: pointer; color: var(--accent); }
.hand-words p { margin: 8px 0 0 0; color: var(--text-dim); }

/* Compact mini-display of hand inflection points (replaces the prose
   "spot in words"). One row per street with: street label chip + board
   cards dealt this street on the left; the inflection actions stacked
   vertically on the right (each action on its own line so multi-action
   streets stay scannable). The row for the current decision street is
   highlighted and ends with "← your turn" on its own line. */
.spot-summary {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.88rem;
}
.spot-sum-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.02);
  flex-wrap: wrap;
}
.spot-sum-row.is-decision {
  background: rgba(120, 180, 255, 0.06);
  border-color: rgba(120, 180, 255, 0.3);
}
.spot-sum-street {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.04);
  padding: 4px 6px;
  border-radius: var(--radius-box);
  flex-shrink: 0;
  min-width: 44px;
  text-align: center;
  align-self: flex-start;
  margin-top: 2px;
}
.spot-sum-row.is-decision .spot-sum-street {
  color: var(--accent);
  background: rgba(120, 180, 255, 0.12);
}
.spot-sum-cards {
  display: inline-flex;
  gap: 3px;
  align-items: center;
  flex-shrink: 0;
  margin-top: 1px;
}
.spot-sum-actions {
  display: flex;
  flex-direction: column;
  gap: 4px;
  color: var(--text);
  line-height: 1.5;
  min-width: 0;
  flex: 1 1 auto;
}
.spot-sum-action {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 2px 6px;
  margin: 0 -6px;
  border-radius: 4px;
  transition: background-color 0.15s ease, box-shadow 0.15s ease;
}
/* The replay's current step drives this — the action whose effects are
   on the table right now gets a subtle highlight bar so the eye can
   track the table animation in the mini-display. */
.spot-sum-action.is-current {
  background: rgba(120, 180, 255, 0.14);
  box-shadow: inset 3px 0 0 var(--accent);
}
/* Two-way sync: when onJumpToStep is wired the summary is clickable.
   Cursor + hover state signal it; pressing rewinds/fast-forwards the
   table view to that action's state. */
.spot-summary.is-clickable .spot-sum-action,
.spot-summary.is-clickable .spot-sum-yourturn {
  cursor: pointer;
}
@media (hover: hover) {
  .spot-summary.is-clickable .spot-sum-action:hover {
    background: rgba(120, 180, 255, 0.08);
  }
  .spot-summary.is-clickable .spot-sum-action.is-current:hover {
    background: rgba(120, 180, 255, 0.2);
  }
  .spot-summary.is-clickable .spot-sum-yourturn:hover {
    text-decoration: underline;
  }
}
/* "YOU ▸" pinned chip on the decision row. Was a left-arrow sentence
   "← Action on HERO" — replaced per mockup M3 / Compressed-Workflow
   audit. Right-aligned via margin-left:auto so it pins to the end of
   the decision row; small letter-spacing + uppercase weight match the
   accent-chip idiom used elsewhere (e.g., the spot-summary street
   label). */
.spot-sum-yourturn {
  margin-left: auto;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 3px 7px;
  border-radius: var(--radius-box);
  white-space: nowrap;
  transition: background-color 0.15s ease, box-shadow 0.15s ease;
}
.spot-sum-yourturn-arrow {
  font-size: 0.8rem;
  line-height: 1;
  font-weight: 700;
}
/* When the replay is AT the decision point, the YOU chip is the
   current step — same highlight bar as an action chip. */
.spot-sum-yourturn.is-current {
  background: rgba(120, 180, 255, 0.22);
  box-shadow: inset 3px 0 0 var(--accent);
}
/* Per-action pot indicator — the running pot total AFTER an action that
   moved chips (bet / raise / call), right-aligned on that action's own
   line. margin-left:auto pins it to the line's right edge. */
.spot-sum-actpot {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.38);
}
.spot-sum-actpot-val { color: rgba(255, 255, 255, 0.62); }

.decide {
  margin-top: 12px;
  border-top: 1px solid var(--border);
  padding-top: 10px;
}
.decide-label {
  display: block;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 700;
  color: var(--text-dim);
  margin-bottom: 6px;
}
.decide-label-sub {
  margin-top: 16px;
  font-size: 0.66rem;
  letter-spacing: 0.06em;
}
/* Action — the primary control: big, stacked, full-width. */
.decide .actions-row {
  flex-direction: column;
  flex-wrap: nowrap;
  gap: 6px;
  margin: 0;
}
/* Action buttons — tightened from 50px tall to 44px (the touch-target
   minimum, no slack). Was contributing to the "giant bubbles" feel on
   the compact view — 3 buttons × 50px + 8px gaps = ~166px before any
   confidence row. Now ~144px. */
.decide .action-btn {
  width: 100%;
  min-height: 44px;
  padding: 10px 14px;
  font-size: 0.95rem;
  font-weight: 600;
}
/* Confidence — secondary: a compact, lighter strip. Wrapped in a
   .confidence-block (label + row) so the whole question reveals as
   one unit AFTER an action is picked (Move 04, decision-screen-
   recomposition audit — "the player meets one question at a time").
   .is-in is added on the frame after the block is unhidden so the
   transition has a 'from' state. Reduced-motion users get the
   final state instantly. */
.confidence-block {
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  transform: translateY(-4px);
  transition: opacity 0.28s ease, max-height 0.28s ease, transform 0.28s ease;
}
.confidence-block.is-in {
  opacity: 1;
  max-height: 200px;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .confidence-block { transition: none; }
}
.decide .confidence-row { gap: 6px; margin: 0; }
.decide .conf-btn {
  flex: 1 1 0;
  min-width: 0;
  min-height: 38px;
  font-size: 0.9rem;
}
/* When a move is picked but confidence isn't, the confidence row
   pulses to point the user at the one input still missing before
   they can lock in. Cleared as soon as a confidence is chosen. */
.confidence-row.needs-confidence {
  border-radius: var(--radius);
  animation: conf-glow 1.6s ease-in-out infinite;
}
@keyframes conf-glow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(76, 182, 255, 0); }
  50%      { box-shadow: 0 0 0 4px rgba(76, 182, 255, 0.28); }
}
@media (prefers-reduced-motion: reduce) {
  .confidence-row.needs-confidence {
    animation: none;
    box-shadow: 0 0 0 3px rgba(76, 182, 255, 0.3);
  }
}
.note-toggle { margin-top: 14px; font-size: 0.9rem; }
.note-toggle summary { cursor: pointer; color: var(--accent); }
.note-toggle .note-input { margin-top: 8px; }

.hand-nav { display: flex; gap: 10px; margin-top: 14px; }
.hand-nav .hand-back { flex: 0 0 auto; min-width: 92px; }
.hand-nav .hand-fwd { flex: 1 1 auto; }

/* Sticky nav pane — used on the reveal screen so the "Next hand →"
   button stays anchored to the top of the viewport as the user
   scrolls through the GTO read, options analysis, villain range.
   Background pad + subtle bottom border so the nav reads as its own
   bar separate from the scrolling content below. */
.hand-nav.hand-nav-sticky {
  position: sticky;
  top: 0;
  z-index: 5;
  margin-top: 0;
  margin-bottom: 12px;
  padding: 10px 0;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  /* Subtle shadow under the bar when it overlaps content beneath. */
  box-shadow: 0 4px 6px -4px rgba(0, 0, 0, 0.5);
}

/* Lock-in button when it lives INSIDE the decide body (below the note
   toggle) — needs a top margin to separate it from the note details
   row and full width to feel like a "submit the form" action. */
.decide .lock-in-btn {
  display: block;
  width: 100%;
  margin-top: 16px;
}
.decide .lock-in-btn[hidden] { display: none; }

/* The reveal body sits directly below the hand-summary (replay table +
   spot-summary). Without separation the lesson pill renders flush against
   the last spot-summary row and reads as overlapping it. Mirror the
   .decide treatment — top margin + hairline divider + padding — so the
   GTO reveal is clearly its own section under the hand summary. */
.hand-reveal {
  margin-top: 16px;
  border-top: 1px solid var(--border);
  padding-top: 16px;
}

/* Per-hand reveal — the educational moment of every hand. Replaces the
   old cramped text-block layout with a "matrix" of every selectable
   option, each carrying badges identifying who picked it:
     🏆 GTO         — the solver-optimal line
     "You"          — Hero's pick, with confidence dots
     <avatar>+name  — opponent's pick (multiplayer), with their dots
   Visual state of each option's card encodes the outcome:
     .is-gto              — green border baseline (the optimal line)
     .is-hero-pick        — Hero picked AND it was GTO → solid green tint
     .is-hero-miss        — Hero picked AND it WASN'T GTO → solid red tint
     .is-opp-pick         — opponent picked (decorative — color from GTO/hero) */

/* ----- Spot context: framing + villain range chips -------------------- */
/* Sits ABOVE the action buttons / reveal verdict, visible in BOTH the
   decide phase and the reveal phase. Holds situational info that's true
   regardless of which option Hero picks: range advantages, board
   texture, SPR, and clickable villain-range chips that pop the Monte
   Carlo equity panel. Slightly muted relative to per-option content so
   it reads as setup, not advice. */
.spot-context {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elev);
}
.spot-context-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  margin-bottom: 6px;
}
.spot-context-hint {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  font-size: 0.75rem;
}

/* Framing list (former .reveal-framing layout, now lives in spot-context). */
.spot-framing-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.spot-framing-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 0.90rem;
  line-height: 1.4;
  color: var(--text);
}
.spot-framing-marker {
  flex: 0 0 auto;
  width: 14px;
  text-align: center;
  color: var(--text-dim);
  font-weight: 700;
  line-height: 1.4;
}
.spot-framing-text { flex: 1 1 auto; min-width: 0; }

/* Hand-intro brief — kept for backwards compat; not currently used
   in the reveal (replay table + spot-summary action log cover the
   same ground more compactly). */
.hand-intro {
  margin: 16px 0 14px 0;
  padding: 10px 14px;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.hand-intro-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  font-weight: 700;
  margin-bottom: 4px;
}
.hand-intro-text {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--text);
}

/* GTO description preamble — paragraph that introduces the strategic
   landscape and the impact of each available option. Sits between the
   verdict and the per-option pros/cons matrix; sets up the thinking
   the user will need to evaluate the choices below. Cool-blue accent
   matches the GTO line / GTO read voice elsewhere. */
.gto-explanation {
  margin: 16px 0 14px 0;
  padding: 12px 16px;
  background: rgba(74, 138, 204, 0.06);
  border: 1px solid rgba(74, 138, 204, 0.3);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
}
.gto-explanation-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
  font-weight: 700;
  margin-bottom: 6px;
}
.gto-explanation-text {
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--text);
}

/* Options-analysis matrix — every available action as a card with
   For/Against bullets. GTO pick gets a green ring + ✓ GTO ribbon;
   the user's pick gets a "Your pick" tag (green if matched, red if
   missed). Lets the user compare trade-offs across choices, not
   just the one they made. */
.options-analysis {
  margin: 16px 0;
}
.options-analysis-header {
  margin-bottom: 10px;
}
.options-analysis-title {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--text);
}
.options-analysis-subtitle {
  font-size: 0.78rem;
  margin-top: 2px;
}
.options-analysis-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.options-analysis-card {
  padding: 10px 14px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.options-analysis-card.is-gto {
  background: rgba(76, 200, 130, 0.06);
  border-color: rgba(76, 200, 130, 0.45);
}
.options-analysis-card.is-miss {
  background: rgba(240, 100, 100, 0.05);
  border-color: rgba(240, 100, 100, 0.4);
}
.options-analysis-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
.options-analysis-action {
  font-size: 0.95rem;
  font-weight: 600;
}
.options-analysis-tags {
  display: inline-flex;
  gap: 6px;
  flex-wrap: wrap;
}
.options-analysis-tag {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 2px 8px;
  border-radius: var(--radius-box);
  white-space: nowrap;
}
.options-analysis-tag.is-gto {
  background: rgba(76, 200, 130, 0.18);
  color: #6fd9a0;
  border: 1px solid rgba(76, 200, 130, 0.5);
}
.options-analysis-tag.is-user.is-match {
  background: rgba(76, 200, 130, 0.12);
  color: #cdebd9;
  border: 1px solid rgba(76, 200, 130, 0.35);
}
.options-analysis-tag.is-user.is-miss {
  background: rgba(240, 100, 100, 0.15);
  color: #f3c0c0;
  border: 1px solid rgba(240, 100, 100, 0.5);
}
.options-analysis-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
@media (max-width: 600px) {
  .options-analysis-body { grid-template-columns: 1fr; }
}
.options-analysis-section-label {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--text-dim);
  margin-bottom: 4px;
}
.options-analysis-pros,
.options-analysis-cons {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 0.86rem;
  line-height: 1.4;
}
.options-analysis-pros li,
.options-analysis-cons li {
  padding-left: 14px;
  position: relative;
  color: var(--text);
}
.options-analysis-pros li::before {
  content: "+";
  position: absolute;
  left: 0;
  color: #6fd9a0;
  font-weight: 700;
}
.options-analysis-cons li::before {
  content: "−";
  position: absolute;
  left: 0;
  color: #e58787;
  font-weight: 700;
}

/* "How others played" crowd breakdown — per-option distribution of
   every recorded response: % bar, player count, avatar row, average
   confidence. The GTO option and the user's own pick get markers; a
   non-GTO option picked confidently by a meaningful slice gets a
   "⚠ Blind spot" flag. */
.crowd-breakdown { margin: 16px 0; }
.crowd-header { margin-bottom: 10px; }
.crowd-title {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--text);
}
.crowd-subtitle { font-size: 0.78rem; margin-top: 2px; }
/* Boxed player-count chip — "12 players" in a small badge that
   matches the .cbadge idiom used elsewhere. Per Results-Header-v2
   audit. Color stays muted (the count is ambient context, not a
   state notification). */
.crowd-count-badge {
  display: inline-block;
  padding: 1px 7px;
  border-radius: var(--radius-box);
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  color: var(--text);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  margin-right: 2px;
}
.crowd-empty {
  font-size: 0.88rem;
  padding: 10px 12px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}
/* Low-sample notice — shown above the crowd rows when only a handful of
   players have answered, so a tiny sample (e.g. "100% · 1 player") is
   not read as crowd wisdom. Soft amber "take this lightly" tone. */
.crowd-lown {
  margin: 0 0 10px 0;
  padding: 7px 11px;
  font-size: 0.82rem;
  line-height: 1.5;
  color: #ffd58a;
  background: rgba(255, 213, 138, 0.08);
  border: 1px solid rgba(255, 213, 138, 0.32);
  border-radius: 8px;
}
.crowd-rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* Three-tier hierarchy (spec §8.2 / mockup M6). A full card carries the
   lesson — the GTO line, the player's pick, or a crowd blind spot;
   every other action recedes to a thin dimmed line below. */
.crowd-row-card {
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
/* GTO line — green flag. Also the merged card (pick matched GTO). */
.crowd-row-gto,
.crowd-row-merged {
  background: rgba(76, 200, 130, 0.07);
  border-color: rgba(76, 200, 130, 0.45);
}
/* The player's pick — elevated so it reads unmistakably as "this is
   you": a left identity bar + a lifted shadow. Red when it missed the
   GTO line, green when it matched (the merged card). */
.crowd-row-pick {
  background: rgba(240, 100, 100, 0.06);
  border-color: rgba(240, 100, 100, 0.45);
  border-left: 3px solid var(--miss);
  box-shadow: var(--shadow-2);
}
.crowd-row-merged {
  border-left: 3px solid var(--ok);
  box-shadow: var(--shadow-2);
}
/* A crowd blind spot the player did NOT pick — amber; it stays a card
   so a spot the crowd misreads is never lost among the receded rows. */
.crowd-row-spot {
  background: rgba(255, 213, 138, 0.05);
  border-color: rgba(255, 213, 138, 0.4);
}
/* Marker row — the prominent YOUR PICK / GTO LINE label at the top of a
   card; the blind-spot tag rides the right edge. */
.crowd-row-markers {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 7px;
}
.crowd-row-markers .crowd-tag { margin-left: auto; }
.crowd-marker {
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 9px;
  border-radius: var(--radius-box);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* User-avatar lead-in for the marker chips ("✓ Your pick · GTO line"
   and "✗ Your pick") per Results-Highlight mockup. The avatar is
   shrunk to fit inline; the larger default .avatar styling (24-32px
   row use) would blow out the marker pill. */
.crowd-marker-avatar.avatar {
  width: 16px;
  height: 16px;
  font-size: 0.55rem;
  flex: 0 0 16px;
}
.crowd-marker.is-gto {
  color: #6fd9a0;
  background: rgba(76, 200, 130, 0.16);
  border: 1px solid rgba(76, 200, 130, 0.5);
}
.crowd-marker.is-miss {
  color: #f3c0c0;
  background: rgba(240, 100, 100, 0.16);
  border: 1px solid rgba(240, 100, 100, 0.5);
}
.crowd-row-head { margin-bottom: 6px; }
.crowd-row-action { font-size: 0.98rem; font-weight: 600; }
/* Receded row — a non-GTO, non-pick, non-blind-spot action. One thin
   dimmed line: there for completeness, kept out of the way. */
.crowd-row-receded {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 12px;
  opacity: 0.62;
}
.crowd-receded-action {
  font-size: 0.86rem;
  color: var(--text-dim);
  flex: 0 0 auto;
}
.crowd-receded-bar {
  position: relative;
  flex: 1 1 auto;
  height: 4px;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 999px;
  overflow: hidden;
}
.crowd-receded-fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  background: rgba(255, 255, 255, 0.22);
}
.crowd-receded-pct {
  font-size: 0.78rem;
  color: var(--text-dim);
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}
.crowd-tag {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 2px 8px;
  border-radius: var(--radius-box);
  white-space: nowrap;
  border: 1px solid transparent;
}
.crowd-tag.is-gto {
  background: rgba(76, 200, 130, 0.18);
  color: #6fd9a0;
  border-color: rgba(76, 200, 130, 0.5);
}
.crowd-tag.is-user {
  background: rgba(120, 180, 255, 0.14);
  color: #c4dcff;
  border-color: rgba(120, 180, 255, 0.45);
}
.crowd-tag.is-blindspot {
  background: rgba(255, 213, 138, 0.14);
  color: #ffd58a;
  border-color: rgba(255, 213, 138, 0.5);
}
/* % bar — track + fill, with the label overlaid. */
.crowd-bar {
  position: relative;
  height: 22px;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 5px;
  overflow: hidden;
  margin-bottom: 8px;
}
.crowd-bar-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background: rgba(120, 180, 255, 0.32);
  transition: width 0.3s ease;
}
.crowd-bar-fill.is-gto { background: rgba(76, 200, 130, 0.34); }
.crowd-bar-fill.is-miss { background: rgba(240, 100, 100, 0.34); }
/* The player's pick gets a taller bar + heavier label — its share reads
   bigger than the rest, the spec's "this is the row that matters" cue. */
.crowd-row-pick .crowd-bar,
.crowd-row-merged .crowd-bar { height: 26px; }
.crowd-row-pick .crowd-bar-label,
.crowd-row-merged .crowd-bar-label { font-size: 0.82rem; font-weight: 700; }
.crowd-bar-label {
  position: absolute;
  top: 0;
  left: 8px;
  height: 100%;
  display: flex;
  align-items: center;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text);
}
.crowd-row-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.crowd-avatars {
  display: inline-flex;
  align-items: center;
  gap: -4px;
}
.crowd-avatar {
  width: 22px !important;
  height: 22px !important;
  font-size: 0.66rem !important;
  margin-right: -4px;
  border: 1.5px solid var(--bg);
  box-sizing: border-box;
}
.crowd-avatar-more {
  font-size: 0.74rem;
  color: var(--text-dim);
  margin-left: 8px;
}
.crowd-conf { font-size: 0.78rem; white-space: nowrap; }

/* A crowd avatar whose player left a comment — green note-dot, plus
   a popover (hover on desktop, tap on mobile) showing the comment. */
/* Comments-as-glow (Results-View-Spec §5, mockup
   GTO-Duel-Results-Social-v2.html). A green glow ring on the avatar
   itself signals a take is available; tooltip on hover / tap shows the
   take text. The previous corner-dot indicator is gone — the ring is
   quieter and reads as part of the avatar at a glance. */
.crowd-avatar-wrap {
  position: relative;
  display: inline-flex;
  margin-right: -4px;
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  outline: none;
  font: inherit;
  color: inherit;
}
.crowd-avatar-wrap .crowd-avatar { margin-right: 0; }
/* Glow ring on the avatar itself — replaces the v.117 corner dot. */
.crowd-avatar-wrap.has-note .crowd-avatar {
  box-shadow: 0 0 0 2px var(--ok), 0 0 6px rgba(74, 222, 128, 0.55);
}
.crowd-avatar-wrap:focus-visible .crowd-avatar {
  box-shadow: 0 0 0 2px var(--ok), 0 0 6px rgba(74, 222, 128, 0.55),
              0 0 0 4px rgba(76, 182, 255, 0.45);
}
.crowd-note-pop {
  position: absolute;
  bottom: calc(100% + 9px);
  left: 50%;
  transform: translateX(-50%) scale(0.96);
  width: 200px;
  max-width: 62vw;
  padding: 8px 10px;
  background: #0a1219;
  border: 1px solid var(--ok);
  border-radius: 9px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.7);
  z-index: 30;
  opacity: 0;
  pointer-events: none;
  text-align: left;
  transition: opacity 0.12s ease, transform 0.12s ease;
}
/* Notch pointing down to the avatar. */
.crowd-note-pop::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: var(--ok);
}
.crowd-avatar-wrap.pop-open .crowd-note-pop {
  opacity: 1;
  transform: translateX(-50%) scale(1);
  pointer-events: auto;
}
@media (hover: hover) {
  .crowd-avatar-wrap:hover .crowd-note-pop {
    opacity: 1;
    transform: translateX(-50%) scale(1);
    pointer-events: auto;
  }
}
.crowd-note-pop-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  margin-bottom: 3px;
}
.crowd-note-pop-name {
  font-size: 0.78rem;
  font-weight: 800;
  color: #9ff0bd;
}
/* The pick + confidence the take was written about — keeps the take
   honest if the player later changed their answer on a retest. */
.crowd-note-pop-ctx {
  font-size: 0.66rem;
  font-weight: 700;
  color: var(--text-dim);
  white-space: nowrap;
}
.crowd-note-pop-text {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: #cdd9e3;
}

/* Hint line under the crowd block — Results-View-Spec §5: "Green ring
   on an avatar = a take is available · tap to read." Shown only when at
   least one response actually has a take to discover. */
.crowd-hint {
  margin: 8px 2px 0;
  font-size: 0.7rem;
  color: #6a8092;
  display: flex;
  align-items: center;
  gap: 6px;
}
.crowd-hint-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #2d4a63;
  box-shadow: 0 0 0 2px var(--ok), 0 0 5px rgba(74, 222, 128, 0.55);
  flex: 0 0 auto;
}

/* Post-reveal comment box — "your take on this hand". */
.comment-box {
  margin: 16px 0;
  padding: 12px 14px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.comment-label {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--text);
}
.comment-hint { font-size: 0.8rem; margin: 3px 0 8px 0; }
/* Stale-note flag — shown in the comment box on a retest when the
   player's new answer differs from the one their saved note was
   written about. Amber "needs attention" tone. */
.comment-stale-flag {
  margin: 0 0 8px 0;
  padding: 7px 10px;
  font-size: 0.82rem;
  line-height: 1.4;
  color: #ffd58a;
  background: rgba(255, 213, 138, 0.10);
  border: 1px solid rgba(255, 213, 138, 0.45);
  border-radius: 8px;
}
.comment-input {
  width: 100%;
  box-sizing: border-box;
  resize: vertical;
  font-family: inherit;
  font-size: 0.9rem;
  padding: 8px 10px;
  background: var(--bg-elev);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.comment-input:focus {
  outline: none;
  border-color: var(--accent);
}
.comment-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
}
.comment-save { flex: 0 0 auto; padding: 6px 14px; }
.comment-status { font-size: 0.82rem; }

/* Villain range chip — clickable, opens the equity panel pre-loaded.
   (Legacy class, used by the backwards-compatible buildSpotContext.
   Newer surfaces use .villain-range-card below for a fuller treatment.) */
.spot-ranges-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.spot-range-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(74, 138, 204, 0.12);
  border: 1px solid rgba(74, 138, 204, 0.45);
  color: var(--accent);
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1.2;
  transition: background 0.12s, border-color 0.12s;
}
.spot-range-chip.is-clickable { cursor: pointer; }
.spot-range-chip.is-clickable:focus {
  background: rgba(74, 138, 204, 0.22);
  border-color: var(--accent);
  outline: none;
}
@media (hover: hover) {
  .spot-range-chip.is-clickable:hover {
    background: rgba(74, 138, 204, 0.22);
    border-color: var(--accent);
  }
}
.spot-range-chip-icon { font-size: 0.9rem; line-height: 1; }

/* Villain range justification block — LEAD of the reveal. Each
   villain_range becomes a card showing its label + the summary that
   justifies why we narrowed villain to it. Cards are clickable to
   pop the Monte Carlo equity panel. More prominent than the legacy
   small chip — this is the primary surface because identifying
   what villain is doing is half the value of the app. */
.villain-range-block {
  margin-bottom: 12px;
  padding: 14px 16px;
  border: 1px solid rgba(232, 169, 63, 0.40);
  border-radius: var(--radius);
  background: rgba(232, 169, 63, 0.06);
}
/* Header — bigger section title + a descriptive subtitle framing the
   block as evidence-based deduction. Subtitle reads like a brief. */
.villain-range-header {
  margin-bottom: 12px;
}
.villain-range-title {
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--villain);
  font-weight: 700;
}
.villain-range-subtitle {
  font-size: 0.8rem;
  margin-top: 3px;
  line-height: 1.4;
}
/* Evidence row — the deductive inputs (villain's actions in order) or
   a predictive note (when villain hasn't acted yet). Sits between the
   header and the range cards so the reader sees the input → output
   chain. Compact chip layout. */
.villain-evidence {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  padding: 8px 10px;
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid rgba(232, 169, 63, 0.25);
  border-radius: 8px;
  flex-wrap: wrap;
}
.villain-evidence-label {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 700;
  color: var(--villain);
  flex-shrink: 0;
}
.villain-evidence-chips {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.villain-evidence-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border-radius: var(--radius-box);
  background: rgba(232, 169, 63, 0.12);
  border: 1px solid rgba(232, 169, 63, 0.35);
  font-size: 0.78rem;
  color: var(--text);
  white-space: nowrap;
}
.villain-evidence-street {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--villain);
  background: rgba(232, 169, 63, 0.18);
  padding: 1px 5px;
  border-radius: var(--radius-box);
}
.villain-evidence-act { font-weight: 500; }
/* Predictive variant — villain hasn't acted yet, so the row is a
   single explanatory note rather than a chip list. */
.villain-evidence-predictive .villain-evidence-text {
  font-size: 0.85rem;
  line-height: 1.4;
}
/* Legacy single-line label support — kept so older callers / tests
   don't break if they query .villain-range-label. */
.villain-range-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--villain);
  margin-bottom: 8px;
}
.villain-range-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.villain-range-card {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.025);
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 12px;
  transition: transform 0.08s ease, background 0.15s ease,
              border-color 0.15s ease, box-shadow 0.15s ease;
}
/* Mini range-grid thumbnail — a non-interactive 13×13 heat grid showing
   the range's SHAPE at a glance (spec §8.4 / mockup M9). */
.vr-minigrid {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: repeat(13, 1fr);
  gap: 1px;
  width: 52px;
  height: 52px;
  margin-top: 1px;
}
.vr-minicell {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 1px;
}
.vr-minicell.is-on { background: var(--villain); }
/* Text column — label, summary, combo count — beside the thumbnail. */
.villain-range-card-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
/* Tier-4 "explore" surface — solid panel fill, a villain-amber identity
   bar on the left edge (not a full amber border, which reads as a
   warning), and depth so the card reads as openable. :active and
   :focus-visible are handled by the global base patch. */
.villain-range-card.is-clickable {
  cursor: pointer;
  background: var(--bg-elev-2);
  border-color: var(--border-interactive);
  border-left: 3px solid var(--villain);
  box-shadow: var(--shadow-2);
}
@media (hover: hover) {
  .villain-range-card.is-clickable:hover {
    border-color: var(--villain);
    box-shadow: var(--shadow-3);
  }
}
.villain-range-card-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
}
.villain-range-card-label {
  font-weight: 700;
  color: var(--villain);
  font-size: 0.95rem;
}
.villain-range-card-icon { font-size: 0.95rem; opacity: 0.7; flex: 0 0 auto; }
.villain-range-card-summary {
  font-size: 0.9rem;
  color: var(--text);
  line-height: 1.4;
}
.villain-range-card-combos {
  font-size: 0.74rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  margin-top: 1px;
}

/* The equity panel mounts into this host (formerly .test-host). Lives
   right below spot-context so the panel pops in context. Empty when
   no range is loaded. */
.equity-host { margin: 0; }
.equity-host:empty { display: none; }

.reveal-result {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 12px;
  background: var(--bg-elev);
}
.reveal-result.is-ok   { border-color: rgba(74, 222, 128, 0.45); }
.reveal-result.is-miss { border-color: rgba(248, 113, 113, 0.45); }

/* Verdict bar across the top — symbol + words. Same as before. */
.reveal-verdict {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  font-weight: 700;
  font-size: 1.05rem;
  border-bottom: 1px solid var(--border);
}
.reveal-result.is-ok   .reveal-verdict { background: rgba(74, 222, 128, 0.12); color: var(--ok); }
.reveal-result.is-miss .reveal-verdict { background: rgba(248, 113, 113, 0.12); color: var(--miss); }
.reveal-verdict-icon { font-size: 1.35rem; line-height: 1; }

/* (Framing CSS moved up to .spot-context — was previously here inline in
   the reveal block. Lives outside the reveal now so it's visible during
   both decide and reveal phases.) */

/* Compact comparison block — replaces the per-option matrix that was
   tried earlier (judged too verbose).
     .reveal-compare-single  used when Hero matched the GTO line. One
                             centred tile (showing the action twice
                             would be redundant).
     .reveal-compare-split   used when Hero missed. Two-column "you
                             played | GTO line". Stacks vertically on
                             narrow widths. */
.reveal-compare {
  display: grid;
  gap: 0;
}
.reveal-compare-single { grid-template-columns: 1fr; }
.reveal-compare-split  { grid-template-columns: 1fr 1fr; }

.reveal-side {
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.reveal-compare-split .reveal-side + .reveal-side {
  border-left: 1px solid var(--border);
}
.reveal-side-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.reveal-side-action {
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1.3;
}
.reveal-side-correct .reveal-side-action,
.reveal-side-gto     .reveal-side-action { color: var(--ok); }
.reveal-side-miss    .reveal-side-action { color: var(--miss); }
/* On a single-tile (correct) reveal, centre everything so the action
   and the confidence row both sit on the centre axis. */
.reveal-side-correct {
  text-align: center;
  align-items: center;
}

/* Confidence row — five dots, filled vs empty. Sits inside the
   comparison block. */
.reveal-conf-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 2px;
}
.reveal-conf-label {
  font-size: 0.78rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.reveal-conf {
  display: inline-flex;
  gap: 4px;
}
.reveal-conf-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: transparent;
  border: 1.5px solid var(--text-dim);
  opacity: 0.55;
}
.reveal-conf-dot.is-filled {
  background: var(--text-dim);
  opacity: 1;
}
.reveal-side-correct .reveal-conf-dot         { border-color: var(--ok); opacity: 0.55; }
.reveal-side-correct .reveal-conf-dot.is-filled { background: var(--ok); border-color: var(--ok); opacity: 1; }

/* Single-paragraph GTO explanation — used by the wrap-up's
   disagreement cards (and potentially other future surfaces). The
   in-game reveal now uses .gto-read (the lead box) instead, but this
   class stays for the wrap-up paragraph rendering. */
.gto-explanation {
  color: var(--text-dim);
  margin: 12px 16px 0;
  font-size: 0.95rem;
  line-height: 1.5;
}

/* GTO line blurb — small inline tag between the villain-range lead and
   the verdict. Per user: "GTO line is a small blurb about the best
   option." Just the label + action chip on one line. The long
   explanation paragraph is gone; villain-range justification above
   covers the WHY, and the spot framing below covers situational facts. */
.gto-read-blurb {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 8px 14px;
  margin-bottom: 12px;
  background: rgba(74, 138, 204, 0.08);
  border: 1px solid rgba(74, 138, 204, 0.35);
  border-radius: var(--radius);
  flex-wrap: wrap;
}

/* Scenario INFO pane — a heads-up shown ABOVE the hand summary for any
   scenario whose setup deviates from the 100bb-cash, cards-shown default
   (tournament, short stack, hidden hole cards). Cool blue "notice"
   styling, distinct from the warm Lesson pill. One row per deviation. */
.scenario-info {
  margin-bottom: 14px;
  padding: 12px 14px;
  background: linear-gradient(180deg, rgba(76, 182, 255, 0.10) 0%, rgba(76, 182, 255, 0.05) 100%);
  border: 1px solid rgba(76, 182, 255, 0.38);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
}
.scenario-info-label {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-weight: 700;
  color: var(--accent);
  margin-bottom: 9px;
}
.scenario-info-icon { font-size: 0.95rem; }
/* Second and later deviations get a hairline rule above them so each
   "what + why" row stays visually distinct. */
.scenario-info-item + .scenario-info-item {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid rgba(76, 182, 255, 0.18);
}
.scenario-info-head {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 3px;
}
.scenario-info-reason {
  font-size: 0.84rem;
  line-height: 1.45;
  color: var(--text-dim);
}

/* Lesson-takeaway pill — the one-line crystallization of the hand's
   pattern. Sits at the very top of the reveal so the user carries
   away a transferable concept before working through detail. Warm
   highlight color (vs the GTO line's cooler blue) to read as "the
   thing to remember" rather than "the answer". */
/* Retest comparison — on the reveal when the player has answered this
   scenario before: their previous answer + a then-vs-now verdict.
   Tinted green when they held/improved, red when they slipped. */
.retest-compare {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  padding: 8px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.03);
  font-size: 0.85rem;
}
.retest-compare.retest-good {
  border-color: rgba(74, 222, 128, 0.4);
  background: rgba(74, 222, 128, 0.07);
}
.retest-compare.retest-bad {
  border-color: rgba(232, 120, 120, 0.38);
  background: rgba(232, 120, 120, 0.06);
}
.retest-tag {
  flex-shrink: 0;
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  background: rgba(76, 182, 255, 0.14);
  padding: 3px 7px;
  border-radius: var(--radius-box);
}
.retest-prior { color: var(--text-dim); }
.retest-prior-action { color: var(--text); font-weight: 600; }
.retest-dots {
  margin-left: 6px;
  letter-spacing: 1px;
  font-size: 0.7rem;
  color: var(--accent);
}
.retest-verdict { margin-left: auto; font-weight: 600; }
.retest-good .retest-verdict { color: var(--ok); }
.retest-bad .retest-verdict { color: #e87878; }

/* === M5 — GTO summary card (Results-View-Spec §3) ===
   One compact card carrying lesson + concept tags (row 1), solver-mix
   bar (row 2; only when scen.solver_data is present), verdict + EV +
   one-line why (row 3). Replaces lesson-takeaway + gto-read + the
   verdict portion of reveal-result in the reveal layout. */
.gto-card {
  background: var(--bg-elev);
  border: 1px solid var(--border-interactive);
  border-radius: 11px;
  padding: 10px 12px 11px;
  margin-bottom: 12px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.gto-card.is-ok   { border-color: rgba(74, 222, 128, 0.45); }
.gto-card.is-miss { border-color: rgba(248, 113, 113, 0.45); }
/* Row 1 — lesson + concept tags */
.gc-lesson {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
.gc-lname {
  font-size: 0.95rem;
  font-weight: 800;
  letter-spacing: -0.005em;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.gc-lname-ico {
  color: var(--villain);
  font-size: 0.78rem;
}
.gc-tag {
  font-size: 0.66rem;
  font-weight: 700;
  background: rgba(76, 182, 255, 0.1);
  border: 1px solid rgba(76, 182, 255, 0.32);
  color: #9fd0ef;
  border-radius: var(--radius-box);
  padding: 2px 6px;
  white-space: nowrap;
}
/* Tap-to-define concept tag — Results-Header-v2 spec. When the tag
   maps to a dictionary entry it renders as a real <button> with the
   shared term-tooltip wired (hover desktop, tap mobile). Visually
   inherits .gc-tag; this variant just turns the cursor and gives it
   button-button semantics (no native button chrome). */
button.gc-tag.gc-tag-term {
  font: inherit;
  line-height: 1;
  cursor: help;
  margin: 0;
}
button.gc-tag.gc-tag-term:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
@media (hover: hover) {
  button.gc-tag.gc-tag-term:hover {
    background: rgba(76, 182, 255, 0.2);
    color: var(--text);
  }
}
button.gc-tag.gc-tag-term.term-tip-active {
  background: rgba(76, 182, 255, 0.22);
  color: var(--text);
}
.gc-tag-more { opacity: 0.7; }
/* Row 2 — solver mix bar */
.gc-bar-block {
  display: flex;
  flex-direction: column;
  gap: 11px;        /* room above the bar for the YOU pin */
}
.gc-barlabel {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.gc-barlabel-l { color: var(--text-dim); }
.gc-barlabel-v {
  margin-left: auto;
  white-space: nowrap;
}
.gc-barlabel-v-ok   { color: var(--ok); }
.gc-barlabel-v-miss { color: var(--miss); }
.gc-bar {
  display: flex;
  height: 22px;
  border-radius: 5px;
  gap: 2px;
  overflow: visible;     /* let .gc-youpin escape above the bar */
}
.gc-seg {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.68rem;
  font-weight: 800;
  white-space: nowrap;
  overflow: hidden;
  border-radius: 3px;
  min-width: 0;
}
.gc-seg-text {
  padding: 0 6px;
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}
.gc-seg-gto  { background: rgba(74, 222, 128, 0.6);  color: #06210f; }
.gc-seg-you  { background: rgba(248, 113, 113, 0.66); color: #2a0e12;
               box-shadow: 0 0 0 1.5px var(--miss); }
.gc-seg-fold { background: rgba(120, 150, 170, 0.3); color: #cdd9e3; }
.gc-youpin {
  position: absolute;
  top: -13px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.58rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--miss);
  white-space: nowrap;
}
.gc-youpin::after {
  content: "";
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  border: 3px solid transparent;
  border-top-color: var(--miss);
}
/* Row 3 — verdict + EV + why */
.gc-why {
  font-size: 0.78rem;
  color: #cdd9e3;
  line-height: 1.45;
  display: flex;
  gap: 6px;
  align-items: flex-start;
}
.gc-why-icon {
  flex: 0 0 auto;
  font-weight: 800;
}
.gto-card.is-ok   .gc-why-icon { color: var(--ok); }
.gto-card.is-miss .gc-why-icon { color: var(--miss); }
.gc-why-text { min-width: 0; }
.gc-why-ev {
  color: var(--miss);
  font-weight: 800;
  white-space: nowrap;
}

.lesson-takeaway {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 10px 16px;
  margin-bottom: 12px;
  background: linear-gradient(180deg, rgba(255, 213, 138, 0.12) 0%, rgba(255, 213, 138, 0.06) 100%);
  border: 1px solid rgba(255, 213, 138, 0.45);
  border-left: 4px solid #ffd58a;
  border-radius: var(--radius);
  flex-wrap: wrap;
}
.lesson-takeaway-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #ffd58a;
  font-weight: 700;
  white-space: nowrap;
  flex-shrink: 0;
}
.lesson-takeaway-text {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
  line-height: 1.35;
}
.gto-read-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.gto-read-action {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--ok);
  line-height: 1.2;
}

/* Dedicated opponent's view panel — appears below the option matrix when
   the opponent has submitted. Their identity (avatar + name) is the
   header; below sit their pick, confidence, and note. This is THE
   surface where the asymmetric multiplayer story lives. */
.reveal-opp-panel {
  margin: 14px 12px 12px;
  padding: 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.025);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.reveal-opp-panel.is-ok   { border-color: rgba(74, 222, 128, 0.45); background: rgba(74, 222, 128, 0.05); }
.reveal-opp-panel.is-miss { border-color: rgba(248, 113, 113, 0.45); background: rgba(248, 113, 113, 0.05); }

.reveal-opp-panel-header {
  display: flex;
  align-items: center;
  gap: 10px;
}
.reveal-opp-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.18);
  font-size: 0.9rem;
}
.reveal-opp-panel-id { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.reveal-opp-panel-name { font-weight: 700; font-size: 1.0rem; }
.reveal-opp-panel-summary { font-size: 0.85rem; }

.reveal-opp-panel-pick,
.reveal-opp-panel-conf,
.reveal-opp-panel-note-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.reveal-opp-panel-note-row { align-items: flex-start; flex-direction: column; gap: 4px; }
.reveal-opp-panel-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  flex: 0 0 auto;
}
.reveal-opp-panel-action { font-weight: 700; font-size: 1.0rem; }
.reveal-opp-panel.is-ok   .reveal-opp-panel-action { color: var(--ok); }
.reveal-opp-panel.is-miss .reveal-opp-panel-action { color: var(--miss); }
.reveal-opp-panel-note {
  font-style: italic;
  color: var(--text-dim);
  margin: 0;
  border-left: 2px solid rgba(255, 255, 255, 0.15);
  padding-left: 10px;
  font-size: 0.92rem;
  line-height: 1.4;
}

.test-row { margin-top: 16px; }
.test-it { width: 100%; }
.test-host { margin-top: 8px; }

/* Narrow widths: the two-column compare collapses to stacked rows so
   each side keeps room and tokens don't wrap mid-action. */
@media (max-width: 560px) {
  .reveal-compare-split { grid-template-columns: 1fr; }
  .reveal-compare-split .reveal-side + .reveal-side {
    border-left: none;
    border-top: 1px solid var(--border);
  }
  .reveal-opp-panel { margin: 14px 8px 8px; }
}

/* ----- Equity (Monte Carlo) panel ------------------------------------- */
.eq-panel {
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.eq-header { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }
.eq-cards { display: flex; align-items: center; gap: 6px; }
.eq-cards-label {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-right: 2px;
}
.eq-picker-section { display: flex; flex-direction: column; gap: 6px; }
.eq-picker-title {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 0.85rem;
  color: var(--text-dim);
}
.eq-picker-title > span:first-child { color: var(--text); font-weight: 600; }
.eq-count { font-size: 0.78rem; }
.eq-range-label {
  font-size: 0.78rem;
  color: var(--villain);
  font-style: italic;
  margin-right: auto;
  padding-left: 8px;
}
.eq-range-label:empty { display: none; }
.eq-run-row { display: flex; gap: 8px; align-items: stretch; }
.eq-run { flex: 1 1 auto; }
.eq-trials-sel {
  flex: 0 0 auto;
  min-height: var(--tap-min);
  background: var(--bg-elev-2);
  color: var(--text);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius);
  padding: 0 10px;
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
}
.eq-reset {
  font-size: 0.82rem;
  color: var(--text-dim);
  text-decoration: underline;
  cursor: pointer;
  background: none;
  border: none;
  padding: 0;
}
@media (hover: hover) {
  .eq-reset:hover { color: var(--text); }
}
.eq-result {
  font-size: 0.92rem;
  padding: 8px 10px;
  border-radius: var(--radius);
  background: var(--bg-elev);
  border: 1px solid var(--border);
}
.eq-result-empty { font-style: italic; }
.eq-bar {
  display: flex;
  height: 14px;
  border-radius: 8px;
  overflow: hidden;
  background: #222;
  margin-bottom: 6px;
}
.eq-bar-hero { background: var(--hero); }
.eq-bar-vill { background: var(--villain); }
.eq-numbers {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  font-size: 0.85rem;
}
.eq-hero-pct { color: var(--hero); font-weight: 700; }
.eq-vill-pct { color: var(--villain); font-weight: 700; }

/* ----- Hero-hand picker (4 suits × 13 ranks) -------------------------- */
.eq-header-pickable { flex-direction: column; align-items: stretch; gap: 8px; }
.eq-hero-picker-host { width: 100%; }
.hero-pick {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px;
}
.hero-pick-title {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 700;
}
.hero-pick-slots {
  display: flex;
  gap: 8px;
}
.hero-pick-slot {
  width: 36px;
  height: 50px;
  padding: 0;
  border: 1px dashed var(--border);
  border-radius: 5px;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.hero-pick-slot.is-filled {
  border: 1px solid var(--hero);
  background: transparent;
  padding: 1px;
}
@media (hover: hover) {
  .hero-pick-slot.is-empty:hover { border-color: var(--text-dim); }
}
.hero-pick-grid {
  display: grid;
  grid-template-columns: repeat(13, 1fr);
  gap: 2px;
}
.hero-pick-cell {
  padding: 1px;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: 3px;
  cursor: pointer;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
@media (hover: hover) {
  .hero-pick-cell:hover { filter: brightness(1.15); }
}
.hero-pick-cell.is-selected { box-shadow: 0 0 0 2px var(--hero); }
.hero-pick-cell.is-dead {
  opacity: 0.25;
  cursor: not-allowed;
  filter: grayscale(40%);
}
.hero-pick-note { font-size: 0.78rem; }

/* ----- Range picker (13×13 hand grid) --------------------------------- */
.range-picker {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* ----- Range picker (matrix-first, controls collapsible) -------------- */
.range-picker { display: flex; flex-direction: column; gap: 10px; }

/* Header — stats + Customize disclosure button */
.rp-header {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; flex-wrap: wrap;
}
.rp-stats {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 6px 10px;
  font-size: 0.85rem; flex: 1 1 auto;
}
.rp-stats b { color: var(--text); font-weight: 600; }
.rp-loaded-label {
  color: var(--villain); font-weight: 600; font-size: 0.78rem;
  margin-right: 6px;
}
.rp-playable-flag { color: var(--villain); }
/* Tier-2 secondary control — solid fill + visible border so it never
   reads as disabled, and a real 44px tap target. */
.rp-customize-btn {
  background: var(--bg-elev-2); border: 1px solid var(--border-interactive);
  color: var(--text); font-size: 0.82rem; font-weight: 600;
  padding: 6px 12px; border-radius: 8px; min-height: var(--tap-min);
  box-shadow: var(--shadow-1);
  cursor: pointer; display: inline-flex; align-items: center; gap: 6px;
  flex: 0 0 auto;
}
@media (hover: hover) {
  .rp-customize-btn:hover { border-color: var(--accent); }
}
.rp-customize-btn.is-open {
  border-color: var(--accent); color: var(--accent); background: var(--accent-soft);
}
.rp-customize-btn .chev {
  display: inline-block; transition: transform 0.18s;
  font-size: 0.7rem; opacity: 0.7;
}
.rp-customize-btn.is-open .chev { transform: rotate(180deg); }

/* Empty hint (shown when matrix is blank and no label) */
.rp-empty-hint {
  background: rgba(76, 182, 255, 0.06);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 10px 12px; font-size: 0.85rem; color: var(--text-dim);
  text-align: center;
}
.rp-empty-hint b { color: var(--accent); font-weight: 600; }

/* Matrix — the visual hero */
.rp-matrix-wrap {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 6px;
}
.rp-matrix {
  display: grid; grid-template-columns: repeat(13, 1fr); gap: 3px;
}
/* Tier-1 tap surface — visible cell border, raised radius, an inset
   bevel and brighter text so the matrix reads as editable rather than
   a static data table. (Selected cells get an accent glow below.) */
.rp-cell {
  font-family: var(--font-stack); font-size: 0.62rem; font-weight: 700;
  padding: 0; aspect-ratio: 1;
  min-height: 0; min-width: 0;
  border-radius: 5px;
  background: var(--bg-elev-2); border: 1px solid var(--border-tap);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
  color: #9fb3c2; cursor: pointer;
  transition: transform 0.08s ease, background 0.12s ease,
              border-color 0.12s ease, box-shadow 0.12s ease;
}
@media (hover: hover) {
  .rp-cell:hover { filter: brightness(1.2); }
}
/* Faint pair / suited / offsuit tints — the matrix diagonal cue. Kept
   (informative) but layered over the solid Tier-1 base above. */
.rp-pair    { background: rgba(76, 182, 255, 0.08); }
.rp-suited  { background: rgba(74, 222, 128, 0.06); }
.rp-offsuit { background: rgba(248, 113, 113, 0.04); }
.rp-cell.is-on {
  background: var(--accent); color: #061018; font-weight: 700;
  border-color: #6cc4ff; box-shadow: var(--glow-accent);
}

/* Collapsible controls (Customize disclosure) */
.rp-controls-wrap {
  display: flex; flex-direction: column; gap: 10px;
  overflow: hidden; max-height: 0; opacity: 0;
  transition: max-height 0.22s ease, opacity 0.18s ease;
}
.rp-controls-wrap.is-open { max-height: 900px; opacity: 1; }

.rp-section {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: 8px; padding: 10px 12px;
  display: flex; flex-direction: column; gap: 8px;
}
.rp-section-label {
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  font-weight: 700; color: var(--text-dim);
  display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
}
.rp-section-hint {
  color: var(--text-dim); font-weight: 400; font-size: 10px;
  letter-spacing: .04em; text-transform: none; font-style: italic;
}

.rp-clear-btn {
  background: var(--bg-elev-2); border: 1px solid var(--border-interactive);
  color: var(--text); font-size: 0.78rem; font-weight: 600;
  padding: 6px 12px; border-radius: 8px; min-height: var(--tap-min);
  box-shadow: var(--shadow-1);
  cursor: pointer; flex: 0 0 auto;
}
@media (hover: hover) {
  .rp-clear-btn:hover {
    color: var(--miss); border-color: var(--miss); background: rgba(248, 113, 113, 0.06);
  }
}

/* Top % slider + jump-to preselects */
.rp-slider-row {
  display: flex; align-items: center; gap: 10px;
}
.rp-slider { flex: 1 1 auto; accent-color: var(--accent); min-width: 0; }
.rp-slider-label {
  font-size: 0.85rem; color: var(--text); font-weight: 600;
  min-width: 62px; text-align: right;
}
.rp-jumps {
  display: flex; gap: 4px; align-items: baseline;
  padding-top: 6px; border-top: 1px dashed var(--border);
  font-size: 11.5px;
}
.rp-jumps-label { color: var(--text-dim); margin-right: 4px; }
.rp-jump {
  background: none; border: none; padding: 2px 6px; min-height: 0;
  color: var(--text-dim); font-size: 11.5px; font-weight: 600;
  cursor: pointer; border-radius: 3px;
}
@media (hover: hover) {
  .rp-jump:hover { color: var(--accent); background: rgba(76, 182, 255, 0.08); }
}
.rp-jump.is-on { color: var(--accent); background: rgba(76, 182, 255, 0.18); }

/* Category chips */
.rp-cats { display: flex; flex-wrap: wrap; gap: 5px; }
.rp-cat {
  font-size: 0.74rem; padding: 5px 10px; border-radius: 999px;
  background: var(--bg-elev-2); border: 1px solid var(--border-interactive);
  color: var(--text); min-height: 0;
}
.rp-cat.is-on {
  background: var(--accent); color: #061018; border-color: var(--accent);
}

/* Filter subsection — lives INSIDE the Category section, separated by a
   dashed divider so it reads as a related-but-distinct sub-control. */
.rp-filter-sub-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 4px; padding-top: 10px;
  border-top: 1px dashed var(--border);
}
.rp-filter-sublabel {
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  font-weight: 700; color: var(--text-dim); margin-bottom: 3px;
}
.rp-filter-name { font-size: 0.88rem; color: var(--text); font-weight: 500; }
.rp-filter-sub { font-size: 0.72rem; color: var(--text-dim); margin-top: 1px; }
.rp-switch {
  position: relative; display: inline-block; width: 46px; height: 26px;
  flex: 0 0 auto; cursor: pointer; padding: 0; border: none; background: none;
  min-height: 0;
}
.rp-switch-track {
  position: absolute; inset: 0; background: var(--border);
  border-radius: 14px; transition: background 0.15s;
}
.rp-switch-thumb {
  position: absolute; top: 2px; left: 2px; width: 22px; height: 22px;
  background: #fff; border-radius: 50%;
  transition: transform 0.15s; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}
.rp-switch.is-on .rp-switch-track { background: var(--villain); }
.rp-switch.is-on .rp-switch-thumb { transform: translateX(20px); }

/* Reveal */
.reveal-list {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.player-result {
  background: var(--bg-elev-2);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin-bottom: 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: baseline;
}
.player-result strong { color: var(--accent); min-width: 80px; }
.player-result .ok { color: var(--ok); }
.player-result .miss { color: var(--miss); }
.player-result .conf { color: var(--text-dim); font-size: 0.9rem; }
.player-note {
  flex-basis: 100%;
  color: var(--text-dim);
  font-style: italic;
  margin: 4px 0 0 0;
}

.gto-line {
  margin-top: 8px;
  padding: 8px 12px;
  background: var(--accent-soft);
  border-radius: var(--radius);
}
.gto-line strong { color: var(--accent); }
.gto-action { font-weight: 600; }

/* Rich-text tokens — position chips & Hero/Villain identity in GTO prose. */
.tok-pos {
  display: inline-block;
  font-weight: 700;
  font-size: 0.82em;
  padding: 0 5px;
  border-radius: 4px;
  background: rgba(138, 161, 177, 0.18);
  color: var(--text);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.tok-pos.is-hero { background: var(--hero-soft); color: var(--hero); }
.tok-pos.is-villain { background: var(--villain-soft); color: var(--villain); }
/* Literal "Hero" / "Villain" words in prose share the SAME chip
   treatment as the .tok-pos.is-hero / .is-villain swapped-position
   chips. Without this, a paragraph that mixed "VILLAIN" (from a
   tokenized position) with "Villain" (from the literal word) showed
   inconsistent styling — one chip-shaped, the other bold-colored
   text. */
.tok-word {
  display: inline-block;
  font-weight: 700;
  font-size: 0.82em;
  padding: 0 5px;
  border-radius: 4px;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.tok-word.is-hero { background: var(--hero-soft); color: var(--hero); }
.tok-word.is-villain { background: var(--villain-soft); color: var(--villain); }

/* bb chip — every "Nbb" / "N.Nbb" gets a stylized pill. Number bold,
   unit subscripted small-caps. Reads as "thirteen big-blinds" while
   looking distinct from prose. */
.tok-bb {
  display: inline-flex;
  align-items: baseline;
  gap: 1px;
  padding: 0 4px;
  border-radius: 4px;
  background: rgba(76, 182, 255, 0.10);
  color: var(--text);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  white-space: nowrap;
}
.tok-bb-num { font-weight: 700; font-size: 0.95em; }
.tok-bb-unit {
  font-size: 0.62em;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  margin-left: 1px;
}
/* Pot-% suffix that appears INSIDE a bb chip when it's been recognised
   as a bet (preceded by "Bet "). The number is auto-computed from the
   scenario's pot at the decision point and rounded to the nearest 5% so
   the visual matches poker's usual "~30% / ~50% / ~75%" granularity.
   Separator dot sits between the bb number and the percent. */
.tok-bb-pct {
  font-size: 0.62em;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  margin-left: 5px;
  padding-left: 5px;
  border-left: 1px solid rgba(76, 182, 255, 0.35);
}
/* In already-tinted chrome (action button, GTO reveal), match the
   container's color so the suffix doesn't read as a separate element. */
.action-btn .tok-bb-pct,
.replay-pot .tok-bb-pct,
.rbet .tok-bb-pct {
  color: inherit;
  opacity: 0.78;
}

/* When the bb chip is nested inside an already-colored container (poker
   table chrome — pot pill, bet bubble, stack label, info bar), strip
   the chip's own pill background + dim-unit color and inherit the
   container's text color. The chip still reads as "Nbb" with the unit
   styled smaller, but it no longer fights the chrome's contrast. */
.replay-pot .tok-bb,
.rbet .tok-bb,
.rseat-stack .tok-bb,
.replay-gameinfo .tok-bb {
  background: transparent;
  padding: 0;
}
.replay-pot .tok-bb-unit,
.rbet .tok-bb-unit,
.rseat-stack .tok-bb-unit,
.replay-gameinfo .tok-bb-unit {
  color: inherit;
  opacity: 0.78;
}

/* Two "non-specific" single-card glyphs:
     .tok-anysuit-doesntmatter — "Kx" (analyst says suit irrelevant)
     .tok-anysuit-unknown      — "K?" (suit not determined yet)

   Board-texture modifiers (rainbow / monotone / two-tone) are NOT a per-
   card concern — they describe how the suits of a multi-card board relate
   to each other. They're rendered separately as .tok-modifier glyphs that
   sit next to a run of default doesn't-matter cards. So "K72 rainbow"
   composes as three default cards + one rainbow modifier glyph, which
   matches the math: K72 (ranks) × rainbow (suit pattern). */
.tok-anysuit {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 19px;
  height: 23px;
  border-radius: 3px;
  /* `middle` centres the card on the parent's text middle. Empirically
     this aligns much more reliably than a fixed pixel offset, because
     inline-flex's baseline shifts depending on how many flex children it
     has (e.g. .pcard-inline has rank+suit, .tok-anysuit has just a rank
     — they ended up at different vertical positions with the same -Npx
     value). `middle` sidesteps that entirely. */
  vertical-align: middle;
  margin: 0 1px;
  background: #fdfdfb;
  color: #1a1a1a;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
  line-height: 1;
  cursor: help;
}
.tok-anysuit-rank { font-weight: 700; font-size: 0.58rem; }
.tok-anysuit-mark { font-weight: 700; font-size: 0.58rem; line-height: 1; }
/* "K?" — small rank + yellow-amber ? in suit slot, suggesting "TBD". */
.tok-anysuit-unknown .tok-anysuit-mark { color: #b08a3a; }
/* "Kx" / hand-class shorthand — the DEFAULT non-specific card. No
   suit indicator at all; the rank fills the card so the absence of a
   suit IS the "any suit" signal. Cleaner than a literal x mark. */
.tok-anysuit-doesntmatter .tok-anysuit-rank {
  font-size: 0.95rem;
  letter-spacing: -0.04em;
}

/* Modifier tag — a run of doesn't-matter cards wrapped in a frame whose
   right side extends into a band carrying a short modifier marker.
   ONE unified treatment for every suit-pattern modifier:
     hand   — suited "s" / offsuit "o"
     board  — monotone "m" / two-tone "2" / rainbow 🌈
   The frame fill carries the texture: solid blue (one suit), red/black
   diagonal (offsuit), red/black horizontal stripes (two-tone), and a
   four-colour diagonal (rainbow). The whole tag is 23px tall — a
   standalone card glyph's height — so the frame costs no extra line
   height (vertical space is at a premium). */
.tok-modtag {
  position: relative;
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin: 0 3px;
  gap: 2px;
  padding: 2px;
  padding-right: 15px;
  border-radius: 4px;
}
.tok-modtag .tok-anysuit {
  margin: 0;
  width: 16px;
  height: 19px;
  border-radius: 2px;
}
.tok-modtag .tok-anysuit-doesntmatter .tok-anysuit-rank {
  font-size: 0.8rem;
}
/* The modifier marker — a (non-italic) letter on the frame's extended
   right band. */
.tok-modtag-glyph {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 0.68rem;
  line-height: 1;
  text-transform: uppercase;
  color: #fdfdfb;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
}
/* Frame fills. */
/* One suit (suited / monotone) — solid, in the app's accent-blue family. */
.tok-modtag-solid { background: #3f86c4; }
/* Offsuit — red/black diagonal. */
.tok-modtag-split {
  background: linear-gradient(135deg, #c0202a 0 55%, #1a1a1a 55% 100%);
}
/* Two-tone — horizontal red/black stripes; distinct from the offsuit
   diagonal. */
.tok-modtag-stripes {
  background: repeating-linear-gradient(180deg,
    #1a1a1a 0 5px, #c0202a 5px 10px);
}
/* Rainbow — the four 4-colour-deck suit hues, diagonally. */
.tok-modtag-rainbow {
  background: linear-gradient(135deg,
    #1a1a1a 0 25%, #c0202a 25% 50%, #2f6fb0 50% 75%, #2f8a52 75% 100%);
}
/* Rainbow's marker is the 🌈 emoji — a drop-shadow outline lifts it off
   the multi-colour frame band. */
.tok-modtag-rainbow .tok-modtag-glyph {
  font-size: 0.82rem;
  filter: drop-shadow(0 0 1px #000) drop-shadow(0 0 1px rgba(0, 0, 0, 0.9));
}

/* Inline range chip — clickable anchor that pre-loads the equity panel. */
.tok-range {
  color: var(--villain);
  border-bottom: 1px dotted var(--villain);
  cursor: pointer;
  padding: 0 1px;
  border-radius: 3px;
  transition: background 0.12s;
}
.tok-range:focus { background: var(--villain-soft); outline: none; }
@media (hover: hover) {
  .tok-range:hover { background: var(--villain-soft); }
}
.tok-range-icon {
  margin-left: 4px;
  font-size: 0.82em;
  vertical-align: 1px;
  opacity: 0.9;
}

/* Wrap-up */
.wrap-up h2 { margin-bottom: 4px; }
.wrap-up h3 { margin-bottom: 8px; margin-top: 24px; }
.stats-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap);
}
.stat-block {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad);
  text-align: center;
}
.stat-block.big {
  grid-column: span 2;
}
.stat-name { font-weight: 600; color: var(--accent); }
.stat-pct { font-size: 2rem; font-weight: 700; margin: 8px 0; }
.stat-detail { color: var(--text-dim); font-size: 0.88rem; }

.disagreement-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.dis-header {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 8px;
}
.dis-row {
  background: var(--bg-elev-2);
  border-radius: var(--radius);
  padding: 8px 12px;
  margin-bottom: 6px;
}
.dis-row.gto {
  background: var(--accent-soft);
}

/* Config error */
.config-error {
  background: var(--bg-elev);
  border: 1px solid var(--miss);
  border-radius: var(--radius);
  padding: var(--pad);
}
.config-error code {
  background: var(--bg-elev-2);
  padding: 1px 6px;
  border-radius: 4px;
}

/* "Your active games" panel (landing) — lists in-progress and
   waiting-for-opponent games the user is a participant in. Each row
   has Resume (continue play) and Cancel (close the stale game). */
.active-games {
  margin-top: 28px;
  border-top: 1px solid var(--border);
  padding-top: 20px;
}
.active-games[hidden] { display: none; }
.active-games h2 { margin: 0 0 4px 0; font-size: 1.15rem; }
.active-games-hint { font-size: 0.88rem; margin: 0 0 14px 0; }
.active-games-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.active-games-item {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  flex-wrap: wrap;
}
.active-games-main { flex: 1 1 240px; min-width: 0; }
.active-games-line {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.active-games-line strong {
  font-family: monospace;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
}
.active-games-status {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: var(--radius-box);
  border: 1px solid transparent;
}
.active-games-status.is-waiting {
  color: #ffd58a;
  background: rgba(255, 213, 138, 0.10);
  border-color: rgba(255, 213, 138, 0.4);
}
.active-games-status.is-progress {
  color: #6fd9a0;
  background: rgba(76, 200, 130, 0.10);
  border-color: rgba(76, 200, 130, 0.45);
}
/* Stalled badge — opponent hasn't acted in 7+ days. Warm-amber tone
   to read as "attention" without alarming-red. */
.active-games-status.is-stalled {
  color: #ffd58a;
  background: rgba(255, 213, 138, 0.10);
  border-color: rgba(255, 213, 138, 0.45);
}
.active-games-item.is-stalled {
  border-color: rgba(255, 213, 138, 0.4);
  background: rgba(255, 213, 138, 0.04);
}
.active-games-vs { font-size: 0.85rem; }
.active-games-progress { font-size: 0.84rem; margin-top: 2px; }
.active-games-activity { font-size: 0.82rem; margin-top: 2px; }
.active-games-item.is-stalled .active-games-activity { color: #ffd58a; }
.active-games-actions {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
}
.active-games-resume { padding: 6px 14px; min-height: 38px; }
.active-games-cancel { padding: 6px 14px; min-height: 38px; }

/* Past games (landing) */
.past-games {
  margin-top: 28px;
  border-top: 1px solid var(--border);
  padding-top: 20px;
}
.past-games h2 { margin: 0 0 4px 0; font-size: 1.15rem; }
.history-summary {
  color: var(--text-dim);
  font-size: 0.9rem;
  margin: 0 0 14px 0;
}
.history-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.history-item {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.history-main { flex: 1 1 auto; min-width: 0; }
.history-line {
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
.history-stats { font-size: 0.85rem; margin-top: 2px; }
.history-view { flex: 0 0 auto; min-height: 38px; padding: 6px 14px; }
.history-remove {
  flex: 0 0 auto;
  text-decoration: none;
  color: var(--text-dim);
  font-size: 1.05rem;
  line-height: 1;
  padding: 6px 8px;
}
@media (hover: hover) {
  .history-remove:hover { color: var(--miss); }
}

/* Wrap-up actions */
.wrap-actions {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

/* Sign-in gate */
.signin {
  text-align: center;
  max-width: 420px;
  margin: 0 auto;
  padding-top: 24px;
}
/* L-1: the page header already prints "GTO Drill"; don't repeat it in the
   sign-in card body. (Same pattern as the landing screen.) */
.signin h1.appname { display: none; }
.signin .tagline { margin-bottom: 28px; }
.google-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  background: #ffffff;
  color: #1f1f1f;
  border: 1px solid #dadce0;
  font-weight: 600;
  min-height: 48px;
}
@media (hover: hover) {
  .google-btn:hover { background: #f4f6f8; }
}
.google-btn svg { display: block; flex: 0 0 auto; }
/* Anonymous escape hatches on the sign-in screen (Practice solo + Equity
   calculator) — bold action label + muted subtext (M-2). Same overall size
   as the Google button, intentionally muted ("greyed out"). */
.solo-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 100%;
  margin-top: 10px;
  background: transparent;
  color: var(--text);
  border: 1px dashed var(--border);
  font-weight: 500;
  min-height: 56px;
  padding: 8px 14px;
  text-align: center;
}
@media (hover: hover) {
  .solo-btn:hover {
    background: var(--bg-elev);
    border-color: var(--text-dim);
  }
}
.alt-btn-label {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
}
.alt-btn-sub {
  font-size: 0.78rem;
  color: var(--text-dim);
  font-weight: 400;
}
.signin-note { margin-top: 20px; font-size: 0.82rem; }

/* ----- Knowledge-level onboarding (first sign-in) ---------------------- */
.knowledge-view {
  max-width: 460px;
  margin: 0 auto;
  padding-top: 20px;
}
.knowledge-view h2 { margin: 0 0 4px 0; }
.knowledge-hint { font-size: 0.9rem; margin: 0 0 16px 0; }
.knowledge-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.knowledge-option {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  width: 100%;
  text-align: left;
  padding: 12px 14px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
@media (hover: hover) {
  .knowledge-option:hover:not(:disabled) {
    background: var(--bg-elev-2);
    border-color: var(--accent);
  }
}
.knowledge-option:disabled { opacity: 0.5; cursor: default; }
.knowledge-option-label { font-size: 1rem; font-weight: 600; }
.knowledge-option-sub { font-size: 0.82rem; line-height: 1.35; }

/* ----- Standalone equity calculator ------------------------------------ */
.calc-view {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.calc-header {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 12px;
  align-items: start;
}
.calc-header h2 { margin: 0; font-size: 1.2rem; }
.calc-blurb { margin: 2px 0 0; font-size: 0.85rem; grid-column: 1; }
.calc-exit { padding: 4px 8px; font-size: 0.85rem; grid-row: 1; grid-column: 2; }
.calc-section {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.calc-slots {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
}
.calc-slot-label {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 700;
  min-width: 50px;
}
.calc-hero-slots .hero-pick-slot.is-filled { border-color: var(--hero); }
.calc-board-slots .hero-pick-slot.is-filled { border-color: var(--villain); }
.calc-grid { margin-top: 4px; }
.calc-grid .hero-pick-cell.is-board {
  box-shadow: 0 0 0 2px var(--villain);
}
.calc-status { font-size: 0.82rem; }
.calc-villain-section { margin-top: 0; }
.calc-btn { /* third main-menu button; same look as solo-btn */ }
.dict-btn { /* fourth main-menu button; same look as solo-btn */ }

/* ----- Poker dictionary + term tooltips -------------------------------- */
.term-trigger {
  display: inline;
  color: var(--accent);
  border-bottom: 1px dotted var(--accent);
  cursor: pointer;
  padding: 2px 1px;
  border-radius: 3px;
  transition: background 0.12s ease;
}
.term-trigger:focus,
.term-trigger.term-tip-active {
  background: rgba(76, 182, 255, 0.12);
}
@media (hover: hover) {
  .term-trigger:hover { background: rgba(76, 182, 255, 0.12); }
}

.term-tip {
  position: absolute;
  z-index: 1000;
  background: var(--bg-elev-2);
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 12px 14px;
  max-width: 320px;
  min-width: 220px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.55);
  color: var(--text);
  font-size: 0.88rem;
  line-height: 1.45;
}
.term-tip-head {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--accent);
  margin-bottom: 6px;
  letter-spacing: 0.02em;
}
.term-tip-body { margin: 0 0 8px; color: var(--text); }
.term-tip-link {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  min-height: 0;
  color: var(--accent);
  font-size: 0.82rem;
  font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
}
@media (hover: hover) {
  .term-tip-link:hover { color: #6cc4ff; background: none; }
}

/* Dictionary view */
.dict-view {
  display: flex; flex-direction: column; gap: 12px;
}
.dict-header {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: 12px; margin-bottom: 4px;
}
.dict-header h2 { margin: 0; font-size: 1.2rem; }
.dict-count { font-size: 0.85rem; flex: 1 1 auto; }
.dict-threshold {
  font-size: 0.82rem;
  padding: 4px 8px;
  background: var(--bg-elev);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  flex: 0 0 auto;
}
@media (hover: hover) {
  .dict-threshold:hover { border-color: var(--accent); }
}
.dict-search {
  width: 100%; margin-bottom: 0;
  padding: 10px 14px;
  font-size: 0.95rem;
}
.dict-list {
  display: flex; flex-direction: column; gap: 12px;
}
.dict-entry {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  scroll-margin-top: 16px;
}
.dict-entry-flash {
  animation: dict-flash 1.4s ease-out;
}
@keyframes dict-flash {
  0%, 40% { background: rgba(76, 182, 255, 0.18); border-color: var(--accent); }
  100% { background: var(--bg-elev); border-color: var(--border); }
}
.dict-term {
  margin: 0 0 4px;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: 0.01em;
}
.dict-aliases {
  margin: 0 0 8px;
  font-size: 0.78rem;
  font-style: italic;
}
.dict-short {
  margin: 0 0 10px;
  font-size: 0.95rem;
  color: var(--text);
}
.dict-long {
  margin: 0 0 10px;
  font-size: 0.9rem;
  color: var(--text-dim);
}
.dict-see-also {
  margin: 8px 0 0;
  font-size: 0.85rem;
}
.dict-term-link {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  min-height: 0;
  color: var(--accent);
  cursor: pointer;
  font: inherit;
  text-decoration: underline;
}
@media (hover: hover) {
  .dict-term-link:hover { color: #6cc4ff; background: none; }
}

/* ----- Solo practice view ---------------------------------------------- */
/* H-1: two-row header — title + action buttons on row 1, stats on row 2. */
.solo-header {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 12px;
}
.solo-header-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}
.solo-header-actions {
  display: flex;
  gap: 10px;
  flex: 0 0 auto;
}
.solo-header h2 { margin: 0; font-size: 1.1rem; flex: 0 0 auto; }
.solo-stats { font-size: 0.85rem; }

/* Header controls (Database + Players + Exit) — always-rendered labelled
   outlined buttons, at every width (never a bare icon / emoji). */
.solo-exit,
.solo-players,
.solo-database {
  flex: 0 0 auto;
  padding: 5px 12px;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1;
  color: var(--text-dim);
  background: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
}
@media (hover: hover) {
  .solo-exit:hover,
  .solo-players:hover,
  .solo-database:hover { background: var(--bg-elev); color: var(--text); }
}
/* The owner Database button gets a faint accent tint so it reads as a
   distinct (owner) control among the header buttons. */
.solo-database {
  color: var(--accent);
  border-color: rgba(76, 182, 255, 0.4);
}

/* Scenario headline — "Scenario #NNN", the atomic reference (the
   trailing number of the scenario slug). Sits at the top of the hand
   card, above the table's CASH/TOURNAMENT context line; it replaces the
   old share-link button as the way to refer to a specific scenario. */
.scenario-headline {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 12px;
}
/* First-run coach mark — a one-time hint pointing at the view toggle
   when a hand first overflows the viewport (spec §7). Absolutely
   positioned, so it costs the layout nothing. */
.view-coach {
  position: absolute;
  top: calc(100% + 9px);
  right: 0;
  z-index: 20;
  max-width: 250px;
  padding: 9px 12px;
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--text);
  background: var(--bg-elev-2);
  border: 1px solid var(--accent);
  border-radius: 8px;
  box-shadow: var(--shadow-3);
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.view-coach.is-in { opacity: 1; transform: translateY(0); }
.view-coach b { color: var(--accent); font-weight: 700; }
/* Pointer notch up toward the toggle. */
.view-coach::before {
  content: "";
  position: absolute;
  top: -5px;
  right: 22px;
  width: 9px;
  height: 9px;
  background: var(--bg-elev-2);
  border-left: 1px solid var(--accent);
  border-top: 1px solid var(--accent);
  transform: rotate(45deg);
}
.scenario-headline-main {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-dim);
}
/* View toggle — switches the hand display between the animated oval
   table and the compact one-screen layout (spec §6.1 / §7).
   The button label states the DESTINATION mode; the icon mirrors it
   (rows glyph when the target is compact; oval glyph when the target
   is table). CSS masks let the icons pick up the button's currentColor
   so the dim → accent state change applies to the glyph for free. */
.view-toggle {
  flex: 0 0 auto;
  min-height: 32px;
  padding: 4px 11px 4px 9px;
  font-size: 0.74rem;
  font-weight: 600;
  line-height: 1;
  color: var(--text-dim);
  background: var(--bg-elev-2);
  border: 1px solid var(--border-interactive);
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* Base-patch — was missing from the original PR #115 base-patch
     selector list. Adds the affordance triad every interactive control
     in the app should have (spec §2.3): touch-action prevents the
     iOS double-tap-zoom delay, :active gives the tap-down feedback,
     :focus-visible draws the keyboard focus ring. */
  touch-action: manipulation;
  transition: transform 80ms ease, background-color 80ms ease, border-color 80ms ease;
}
.view-toggle:active {
  transform: scale(0.94);
  background: var(--accent-soft);
}
.view-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.view-toggle::before {
  content: "";
  display: inline-block;
  width: 12px;
  height: 12px;
  flex: 0 0 12px;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
  -webkit-mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  mask-size: contain;
  /* Default state — current view is expanded, label says "Compact
     view", icon is the three-rows glyph hinting at the target. */
  -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><rect x="1.5" y="2" width="9" height="1.6" rx="0.8" fill="black"/><rect x="1.5" y="5.2" width="9" height="1.6" rx="0.8" fill="black"/><rect x="1.5" y="8.4" width="9" height="1.6" rx="0.8" fill="black"/></svg>');
  mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><rect x="1.5" y="2" width="9" height="1.6" rx="0.8" fill="black"/><rect x="1.5" y="5.2" width="9" height="1.6" rx="0.8" fill="black"/><rect x="1.5" y="8.4" width="9" height="1.6" rx="0.8" fill="black"/></svg>');
}
.view-toggle[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}
/* Pressed state — current view is compact, label says "Table view",
   icon swaps to the oval-table glyph. */
.view-toggle[aria-pressed="true"]::before {
  -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><ellipse cx="6" cy="6" rx="4.8" ry="2.8" fill="none" stroke="black" stroke-width="1.4"/></svg>');
  mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><ellipse cx="6" cy="6" rx="4.8" ry="2.8" fill="none" stroke="black" stroke-width="1.4"/></svg>');
}
@media (hover: hover) {
  .view-toggle:hover { border-color: var(--accent); color: var(--text); }
}
.scenario-headline-num {
  color: var(--accent);
  font-weight: 700;
}
/* "Replay" marker on the headline — flags a scenario the player has
   answered before. The prior answer stays hidden until the reveal. */
.scenario-replay-tag {
  margin-left: 8px;
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  background: rgba(76, 182, 255, 0.14);
  padding: 3px 7px;
  border-radius: var(--radius-box);
  vertical-align: middle;
}
/* Stake tag — "CASH · 100BB". Matches the mockup's card-header
   context line (GTO-Duel-Compressed-Workflow.html). Quieter than the
   replay tag (neutral foreground, faint background) since stake is
   ambient context, not a state notification. */
.scenario-stake-tag {
  margin-left: 8px;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.04);
  padding: 3px 7px;
  border-radius: var(--radius-box);
  vertical-align: middle;
  white-space: nowrap;
}
.scenario-stake-sep {
  margin: 0 2px;
  opacity: 0.55;
}
/* Per-scenario flag tag — Special-Scenarios spec §5. A compact box
   that sits in the scenario header beside the Replay marker. For major
   flags this is a real <button> that re-opens the briefing modal in
   review mode mid-hand; minor flags render as a static span. The tag
   carries the ⚠ glyph + the flag's short label (e.g. "Hidden cards"). */
.scenario-flag-tag {
  margin-left: 8px;
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--hero-soft);
  border: 1px solid rgba(76, 182, 255, 0.4);
  padding: 3px 7px;
  border-radius: var(--radius-box);
  vertical-align: middle;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-family: inherit;
  line-height: 1;
  cursor: pointer;
}
button.scenario-flag-tag {
  appearance: none;
}
@media (hover: hover) {
  button.scenario-flag-tag:hover {
    background: rgba(76, 182, 255, 0.28);
    border-color: var(--accent);
  }
}
button.scenario-flag-tag:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.scenario-flag-tag.scenario-flag-tag-static { cursor: default; }

/* ----- Scenario briefing modal (Special-Scenarios spec §4) -----------
   Fixed-position scrim covering the viewport, with a centered card
   above it. The hand behind is non-interactive by virtue of the scrim
   intercepting all pointer events. Gate mode is the default; the
   modal stays until the user taps Start hand. Review mode (re-opened
   via the header flag tag) also allows scrim-tap and Escape dismissal
   — those paths are wired in JS, the styles here are shared. */
.sb-scrim {
  position: fixed;
  inset: 0;
  background: rgba(5, 9, 13, 0.78);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 18px;
  z-index: 1000;
  animation: sb-scrim-in 140ms ease-out;
}
@keyframes sb-scrim-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.sb-modal {
  width: 100%;
  max-width: 320px;
  background: var(--bg-elev);
  border: 1px solid var(--border-interactive);
  border-radius: 16px;
  padding: 18px 17px 16px;
  box-shadow: var(--shadow-3), 0 20px 50px rgba(0, 0, 0, 0.7);
  text-align: center;
  animation: sb-modal-in 180ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
@keyframes sb-modal-in {
  from { opacity: 0; transform: translateY(8px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.sb-icon {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  margin: 0 auto 11px;
  background: var(--hero-soft);
  border: 1px solid rgba(76, 182, 255, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 19px;
  color: var(--accent);
}
.sb-eyebrow {
  font-size: 0.56rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  color: var(--accent);
  margin-bottom: 5px;
}
.sb-title {
  font-size: 1.06rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.25;
  margin: 0 0 8px;
  color: var(--text);
}
.sb-body {
  font-size: 0.78rem;
  color: #c4d2dd;
  line-height: 1.55;
  margin: 0 0 12px;
}
.sb-flags {
  display: flex;
  gap: 5px;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.sb-flag {
  font-size: 0.56rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  background: var(--hero-soft);
  color: var(--accent);
  border: 1px solid rgba(76, 182, 255, 0.32);
  border-radius: var(--radius-box);
  padding: 3px 7px;
}
.sb-btn {
  width: 100%;
  min-height: 46px;
  background: var(--accent);
  color: #061018;
  border: 0;
  border-radius: 999px;
  font-family: inherit;
  font-size: 0.94rem;
  font-weight: 800;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(76, 182, 255, 0.3);
  transition: transform 80ms ease, filter 120ms ease;
}
.sb-btn:active {
  transform: scale(0.97);
  filter: brightness(1.08);
}
.sb-btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}
.sb-foot {
  font-size: 0.6rem;
  color: #6a8092;
  margin-top: 9px;
}
@media (prefers-reduced-motion: reduce) {
  .sb-scrim,
  .sb-modal { animation: none; }
  .sb-btn { transition: none; }
}

/* Icon-only header buttons (used by solo + duel views). The text labels
   that used to live next to the icons were redundant with the title
   tooltips — and on narrow phones they kept pushing the title around.
   Icon + title tooltip is enough. */
.icon-btn,
.share-icon-btn {
  padding: 4px 8px;
  font-size: 1rem;
  flex: 0 0 auto;
  line-height: 1;
}
.share-icon-btn.is-copied { color: var(--ok); }

/* Inline fallback row that appears when the clipboard write is blocked
   (insecure origin, permission denied). Shows the URL so the user can
   copy by hand. Shared between solo + duel; either view appends one. */
.share-fallback {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px 10px;
  margin: 0 0 12px;
  font-size: 0.85rem;
}
/* `display: flex` above overrides the HTML `hidden` attribute (CSS
   display wins over the default `[hidden] { display: none }` user-
   agent rule), so without this the empty fallback container shows
   as an empty bubble between the header and the table. */
.share-fallback[hidden] { display: none; }
.share-fallback-input {
  flex: 1 1 auto;
  margin: 0;
  font-size: 0.82rem;
  padding: 6px 8px;
}

/* The duel view's share button sits in the existing .game-header strip,
   which already uses justify-content: space-between — no extra layout
   needed, just the button's own padding. */
.game-share { /* hook for future per-context tweaks */ }

/* Account bar (landing) */
.account-bar {
  margin-top: 28px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 0.85rem;
  text-align: center;
}
.link-btn {
  background: none;
  border: none;
  color: var(--accent);
  padding: 0;
  min-height: 0;
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}
@media (hover: hover) {
  .link-btn:hover { background: none; color: #6cc4ff; }
}

/* --- hand-spot wrapper -------------------------------------------------
   The container holding runout-strip + hero-strip + decide-prompt on
   the compact view (or the animated replay table on expanded view).
   Uses flex+gap to give consistent breathing room between the stacked
   pieces — was relying on each child's own margin which made the
   spacing inconsistent and bigger than needed. */
.hand-spot {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* --- Compact-view board-runout strip (spec §6.1 / mockup M3) ----------
   The flop/turn/river board dealt once as a compact strip — the compact
   layout's replacement for the oval table's felt board. */
.runout-strip {
  background: linear-gradient(180deg,
    rgba(45, 106, 74, 0.22) 0%, rgba(23, 59, 45, 0.22) 100%);
  border: 1px solid rgba(45, 106, 74, 0.5);
  border-radius: var(--radius);
  padding: 7px 10px;
}
.runout-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
}
.runout-label {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #9fd9bc;
}
/* Pot badge — a chip skeuomorph, so it keeps the round pill shape. */
.runout-pot {
  font-size: 0.78rem;
  color: var(--text);
  background: rgba(0, 0, 0, 0.45);
  padding: 3px 11px;
  border-radius: 999px;
  white-space: nowrap;
}
.runout-pot b { font-weight: 700; }
.runout-streets {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}
.runout-street {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.runout-street-tag {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.runout-street-cards { display: flex; gap: 3px; }
/* Thin vertical separator between FLOP / TURN / RIVER groups so the
   runout reads as three chunks rather than one long card run (mockup M3
   compressed-workflow pass). */
.runout-divider {
  flex: 0 0 auto;
  align-self: stretch;
  width: 1px;
  background: rgba(45, 106, 74, 0.55);
}

/* --- Street-progress dots in the scenario headline (mockup M3) --------
   Four dots = PRE / FLOP / TURN / RIVER. Streets before the decision
   street are dim (".is-done"); the decision street itself glows accent
   (".is-now"); unreached future streets keep the neutral border tint. */
.scenario-streets {
  display: inline-flex;
  gap: 5px;
  align-items: center;
  flex: 0 0 auto;
}
.scenario-street-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--border-interactive);
}
.scenario-street-dot.is-done { background: var(--text-dim); }
.scenario-street-dot.is-now {
  background: var(--accent);
  box-shadow: 0 0 4px rgba(76, 182, 255, 0.55);
}

/* --- Compact-view hero strip (spec §6.1 / mockup M3) ------------------
   One line — seat, hole cards, decision-point stack. The compact
   layout's replacement for the oval table's hero seat. A hero-blue
   left identity bar marks it as "you". */
.hero-strip {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 10px;
  background: transparent;
  border: 0;
  border-left: 3px solid var(--hero);
  border-radius: 0;
  /* Flattened — was a full bordered panel with rounded-rect bg.
     Now reads as an inline row anchored by the hero-blue left bar.
     Saves ~6px of vertical and removes the "second box inside a box"
     visual stacking that contributed to the giant-bubbles feel. */
}
.hero-strip-seat {
  font-size: 0.82rem;
  color: var(--text-dim);
  flex: 0 0 auto;
}
.hero-strip-seat b { color: var(--hero); font-weight: 700; }
/* Positional opposition — "vs BB" appended to the hero seat label.
   The hero stays the visual subject (full hero-blue on the seat letter
   and "You"); the villain seat is rendered in the project's villain
   accent so the opposition reads at a glance without ever competing
   with the hero label for weight. */
.hero-strip-vs {
  color: var(--text-dim);
  font-size: 0.78rem;
  margin-left: 4px;
  opacity: 0.85;
}
.hero-strip-vs b { color: var(--villain); font-weight: 700; opacity: 0.95; }
.hero-strip-cards { display: flex; gap: 3px; flex: 0 0 auto; }
.hero-strip-stack {
  margin-left: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex: 0 0 auto;
}
.hero-strip-stack-num {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.hero-strip-stack-label {
  font-size: 0.58rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
}

/* --- Compact decide prompt + context chips (mockup M3) ---------------
   On the DECIDE screen of the compact layout, the timeline is hidden;
   in its place we render a one-line "BB raises to 7 bb on the flop —
   your move" prompt plus a chip strip of Pot / To call / Pot odds. */
/* Decide-prompt — was a heavy bordered panel with backdrop tint.
   Tightened to a borderless inline block per the compact-view
   mockup: prompt text + chip strip read as one tight unit, not as
   their own panel. (Spec §6.1 "fit one viewport" — each visible
   panel costs vertical pixels.) */
.decide-prompt {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px 0 2px;
  /* border + bg removed — chips carry their own visual weight */
}
.decide-prompt-line {
  margin: 0;
  font-size: 14px;
  color: var(--text);
  line-height: 1.35;
}
.decide-prompt-actor {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 3px;
  /* Villain colors — the actor is always a villain per
     buildDecidePrompt's hero-actor guard. */
  background: var(--villain-soft);
  color: var(--villain);
  letter-spacing: 0.04em;
  font-weight: 700;
  font-size: 12px;
}
.decide-chips {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
/* Inline pills: "Pot 17.5bb" reads left-to-right in one tight line,
   not stacked. Was column-flex with min-width 64px and 4×12 padding,
   each chip its own large box. Now a true pill that scales to
   content. (Mockup M3 / GTO-Duel-Compressed-Workflow.) */
.decide-chip {
  display: inline-flex;
  flex-direction: row;
  align-items: baseline;
  gap: 4px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--bg-elev);
  border: 1px solid var(--border-interactive);
  white-space: nowrap;
}
.decide-chip-value {
  font-weight: 700;
  font-size: 12px;
  color: var(--text);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}
.decide-chip-label {
  font-size: 0.58rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 700;
}

/* --- "TAP TO GO DEEPER" reveal accordion (spec §8.6 / mockup M3) ------
   Collapsible sections under the above-fold reveal summary: Why this is
   the line / Your options compared / Villain's range + equity / Add
   your take. `<details>` gives free keyboard + a11y + tap behavior; we
   only style chrome here. */
.reveal-accordion {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 14px;
}
.reveal-accordion-heading {
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-align: center;
  margin: 4px 0 6px 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.reveal-accordion-heading::before,
.reveal-accordion-heading::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--border-interactive);
  opacity: 0.45;
}
.reveal-acc-item {
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-box);
  background: var(--bg-elev);
  overflow: hidden;
}
.reveal-acc-item[open] {
  background: var(--bg-elev-2);
}
.reveal-acc-summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 12px;
  cursor: pointer;
  list-style: none;
  user-select: none;
  min-height: 44px;
}
.reveal-acc-summary::-webkit-details-marker { display: none; }
.reveal-acc-summary::marker { content: ""; }
.reveal-acc-label {
  flex: 1 1 auto;
  font-weight: 600;
  font-size: 14px;
  color: var(--text);
}
.reveal-acc-hint {
  font-size: 11px;
}
.reveal-acc-chevron {
  flex: 0 0 auto;
  font-size: 14px;
  color: var(--text-dim);
  transition: transform 0.18s ease;
}
.reveal-acc-item[open] .reveal-acc-chevron {
  transform: rotate(180deg);
}
/* Body padding for each open section's content. The inner blocks each
   bring their own headings — inside the accordion those are visually
   redundant with the summary label, so hide them. */
.reveal-acc-item > :not(summary) {
  padding: 0 12px 12px 12px;
}
.reveal-acc-item .gto-explanation-label,
.reveal-acc-item .options-analysis-header {
  display: none;
}
@media (hover: hover) {
  .reveal-acc-summary:hover { background: rgba(76, 182, 255, 0.04); }
}

/* --- Poker-hand replay --- */
.replay { margin: 10px 0 6px 0; }
/* Header row above the table — format/depth chip on the left, optional
   context tag (tournament / short-stack) on the right. */
.replay-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.replay-gameinfo {
  font-size: 0.74rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.07em;
}
.replay-gameinfo.is-tournament {
  color: #ffd58a;
}
/* Context tag — shows only when the depth/format meaningfully changes
   strategy. Lights up vs the muted gameinfo so the user catches it
   during decide, not just in post-decision framing bullets. */
.replay-context-tag {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 3px 9px;
  border-radius: var(--radius-box);
  border: 1px solid transparent;
  background: rgba(255, 255, 255, 0.03);
}
.replay-context-tag.is-tournament {
  color: #ffd58a;
  border-color: rgba(255, 213, 138, 0.45);
  background: rgba(255, 213, 138, 0.08);
}
.replay-context-tag.is-shortstack {
  color: #c4dcff;
  border-color: rgba(120, 180, 255, 0.5);
  background: rgba(120, 180, 255, 0.08);
}
/* Poker felt — radial gradient base + a fine dot grain to break up the
   flat-color look. The grain dots are tiny + low opacity so it reads as
   "felt texture" not "polka dots". The rail is a dark-green outer border
   that mimics a table edge, plus an outer drop shadow so the table sits
   "on" the page instead of being a flat panel. The inner shadow keeps
   the edges of the felt slightly darker — adds depth without distracting. */
.replay-table {
  position: relative;
  height: 300px;
  background:
    /* Felt grain: tiny radial dots at low opacity over the base gradient */
    radial-gradient(circle at 1px 1px, rgba(255, 255, 255, 0.025) 1px, transparent 1.5px) 0 0 / 5px 5px,
    radial-gradient(ellipse at center, #2c6e52 0%, #1b4736 100%);
  border: 6px solid #0e2a1d;
  border-radius: 140px;
  box-shadow:
    /* Inner shadow — darkens the felt edge */
    inset 0 0 40px rgba(0, 0, 0, 0.45),
    /* Subtle highlight on the top inner edge — fakes a slight bevel */
    inset 0 1px 0 rgba(255, 255, 255, 0.05),
    /* Outer drop shadow — lifts the table off the page */
    0 6px 18px rgba(0, 0, 0, 0.45);
}

.rseat {
  position: absolute;
  width: 80px;
  padding: 4px;
  border-radius: 8px;
  text-align: center;
}

/* Betting ring — the dashed ellipse bet chips are pushed onto, the
   casino "betting line" convention. Sits behind the seats; bets land
   on its perimeter (.rbet-slot-N) and the dealer button rides just
   seat-side of it. */
.replay-ring {
  position: absolute;
  top: 24%;
  bottom: 24%;
  left: 12%;
  right: 12%;
  border: 2px dashed rgba(255, 255, 255, 0.13);
  /* Capsule, not an ellipse — mirrors the table's own rounded-
     rectangle outline (border-radius: 140px). A large radius caps
     to a full pill on this wide-short box. */
  border-radius: 999px;
  pointer-events: none;
}

/* Dealer button — small white "D" disc. Rendered as a CHILD of the BTN
   seat so it's unambiguously that seat's button, pinned to the seat
   corner that faces the table centre. It stays bright when the BTN
   folds because folded seats dim their content, not the seat box, and
   the disc is positioned (not flow) content the dim selectors skip. */
.rdealer {
  position: absolute;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: linear-gradient(180deg, #fafafa 0%, #d8d8d8 100%);
  color: #1a1a1a;
  font-size: 0.66rem;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(0, 0, 0, 0.4);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.6);
  line-height: 1;
  z-index: 4;
}
/* Per-slot: the disc hugs the seat corner pointing toward table centre
   (centre seats: just above/below; corner seats: the inward corner). */
.rdealer-slot-0 { top: -10px; left: 50%; transform: translateX(-50%); }
.rdealer-slot-3 { bottom: -10px; left: 50%; transform: translateX(-50%); }
.rdealer-slot-1 { top: -8px; right: -8px; }
.rdealer-slot-2 { bottom: -8px; right: -8px; }
.rdealer-slot-4 { bottom: -8px; left: -8px; }
.rdealer-slot-5 { top: -8px; left: -8px; }
/* Seat anchors. Slots 0/3 (hero centre + top centre) sit on the table's
   straight top/bottom edges. Slots 1/2/4/5 are the four "corner" seats —
   they CANNOT hug the table corners (left/right: 4px) because the table
   is a capsule with a 140px corner radius: the felt curves sharply away
   there and a 80px-wide seat would overhang the rounded end onto the page.
   The corner seats are inset ~46px from each edge so the seat box stays
   fully on the felt — this turns the six seats into a clean hexagon. */
.rslot-0 { bottom: 6px; left: 50%; transform: translateX(-50%); }
.rslot-1 { bottom: 46px; left: 46px; }
.rslot-2 { top: 46px; left: 46px; }
.rslot-3 { top: 8px; left: 50%; transform: translateX(-50%); }
.rslot-4 { top: 46px; right: 46px; }
.rslot-5 { bottom: 46px; right: 46px; }
.rseat-turn { background: rgba(76, 182, 255, 0.16); box-shadow: 0 0 0 2px var(--accent); }
.rseat-villain:not(.rseat-turn) { box-shadow: 0 0 0 1.5px rgba(232, 169, 63, 0.6); }
/* Folded seats keep a bounding box — a faint grey container + ring — so
   the seat's position on the table stays visible. The DIM is applied to
   the seat's content (cards, labels) rather than the whole seat, so the
   box stays crisp AND a child dealer disc isn't dragged dim with it. */
.rseat-folded {
  background: rgba(255, 255, 255, 0.05);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.16);
}
.rseat-folded .rseat-cards,
.rseat-folded .rseat-pos,
.rseat-folded .rseat-stack {
  opacity: 0.5;
}
.rseat-mucked {
  min-height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.62rem;
  font-style: italic;
  color: #9fb6ab;
}
.rseat-cards { display: flex; justify-content: center; gap: 3px; margin-bottom: 3px; }
.rseat-pos {
  font-size: 0.72rem;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
.rseat-hero .rseat-pos { color: var(--hero); }
.rseat-villain .rseat-pos { color: var(--villain); }
.rseat-stack {
  font-size: 0.72rem;
  color: #d5ece0;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

/* Action badge — floats above the seat naming the action that JUST
   landed there ("raises to 2.5bb", "calls 2.5bb", "checks", "bets 3bb",
   "folds"). Each render creates a fresh element so the entry animation
   re-fires on every step. Critical visual cue for checks (no chip
   moves) and reinforces bets/raises/calls beyond just the chip
   appearing. */
.rseat-acted-badge {
  position: absolute;
  top: -22px;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 3px 9px;
  border-radius: 999px;
  background: linear-gradient(180deg, #2b3849 0%, #1d2734 100%);
  color: #e8f4ff;
  border: 1px solid rgba(120, 180, 255, 0.45);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
  pointer-events: none;
  z-index: 4;
  animation: rseat-badge-in 320ms cubic-bezier(0.16, 0.84, 0.44, 1) both;
}
/* Bottom-row seats (hero centre + flanks) need the badge BELOW the
   seat instead of above — there's nothing above them, but plenty of
   table behind. */
.rslot-0 .rseat-acted-badge,
.rslot-1 .rseat-acted-badge,
.rslot-5 .rseat-acted-badge {
  top: auto;
  bottom: -22px;
}
@keyframes rseat-badge-in {
  0%   { opacity: 0; transform: translateX(-50%) translateY(-4px) scale(0.7); }
  60%  { opacity: 1; transform: translateX(-50%) translateY(0) scale(1.08); }
  100% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
/* Per-action color cues — subtle hue shift so a glance distinguishes
   aggression (raise/bet) from passive (call/check) from fold. */
.rseat-acted-raise,
.rseat-acted-bet {
  border-color: rgba(255, 184, 90, 0.7);
  color: #ffe7c4;
}
.rseat-acted-call {
  border-color: rgba(120, 200, 255, 0.6);
}
.rseat-acted-check {
  border-color: rgba(200, 215, 230, 0.55);
  color: #d8e5f1;
  background: linear-gradient(180deg, #354150 0%, #232c39 100%);
}
.rseat-acted-fold {
  border-color: rgba(200, 90, 90, 0.55);
  color: #f3c0c0;
}

/* Just-acted seat ring pulse — the seat that performed the latest
   action briefly glows. Re-runs on each render (each step) because
   the seat element is freshly built. */
.rseat-just-acted {
  animation: rseat-just-acted-pulse 700ms ease-out both;
}
@keyframes rseat-just-acted-pulse {
  0%   { box-shadow: 0 0 0 2px var(--accent), 0 0 0 0 rgba(120, 180, 255, 0.55); }
  60%  { box-shadow: 0 0 0 2px var(--accent), 0 0 0 10px rgba(120, 180, 255, 0); }
  100% { box-shadow: 0 0 0 2px var(--accent), 0 0 0 0 rgba(120, 180, 255, 0); }
}
/* Already-current-turn seats keep the steady ring AFTER the pulse —
   so .rseat-turn's box-shadow re-applies once the animation ends.
   (animation: both retains end state, but rseat-turn's static
   box-shadow takes over via CSS cascade since it's defined after.) */

/* Bet bubble — chip-like gradient pill positioned BETWEEN the seat and
   the pot center (not above/below the seat). Reads as "chips pushed
   forward" toward the pot instead of a label attached to the player.
   Sits in the .replay-table directly, addressed by slot for placement. */
.rbet {
  position: absolute;
  background: linear-gradient(180deg, #5cb7ff 0%, #3a98e0 100%);
  color: #061018;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 2px 9px;
  border-radius: 999px;
  white-space: nowrap;
  box-shadow:
    0 2px 3px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    inset 0 -1px 0 rgba(0, 0, 0, 0.2);
  z-index: 2; /* above the felt, below the pot pill which sits at center */
}
/* Per-slot positioning — each bet chip is centred on the point of the
   betting ring (.replay-ring) nearest its seat. Casino convention:
   chips pushed forward to the betting line. The ring spans 24%–76%
   vertically and 12%–88% horizontally; these six points are the
   perimeter positions in front of each seat. translate(-50%,-50%)
   centres the chip on the point. */
.rbet {
  transform: translate(-50%, -50%);
}
.rbet-slot-0 { top: 68%; left: 50%; }
.rbet-slot-1 { top: 63%; left: 26%; }
.rbet-slot-2 { top: 37%; left: 26%; }
.rbet-slot-3 { top: 32%; left: 50%; }
.rbet-slot-4 { top: 37%; left: 74%; }
.rbet-slot-5 { top: 63%; left: 74%; }

/* Animation: bet bubble fades + scales in when a player commits chips.
   Preserves the translate(-50%,-50%) centring through the keyframes. */
.rbet { animation: rbet-enter 280ms ease-out; }
@keyframes rbet-enter {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* Animation: board cards fade in (subtle — every render rebuilds the
   DOM, so a heavy animation would flicker on every prev/next click).
   Quick + opacity-only — feels like "dealt" without being distracting
   when the user is scrubbing through. */
.replay-board .pcard {
  animation: rcard-fade 180ms ease-out;
}
@keyframes rcard-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.replay-center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.replay-board { display: flex; gap: 4px; min-height: 36px; align-items: center; }
.replay-board-empty {
  font-size: 0.78rem;
  color: #cfe6da;
  font-style: italic;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
/* Pot pill — sits below the board cards. More prominent than before:
   slightly larger, softer dark fill, subtle ring + drop shadow so it
   reads as a chip stack rather than just a label. */
.replay-pot {
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 0.85rem;
  font-weight: 600;
  padding: 3px 14px;
  border-radius: 999px;
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.4),
    inset 0 0 0 1px rgba(255, 255, 255, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

/* Playing cards (CSS-drawn — no assets) */
.pcard {
  width: 30px;
  height: 42px;
  background: #fdfdfb;
  color: #1a1a1a;
  border: 1px solid #c8c8c0;
  border-radius: 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}
.pcard-sm { width: 26px; height: 36px; }
.pcard-red { color: #c0202a; }
/* Rank and suit each sit in a fixed flex-centred box, so the differing
   intrinsic metrics of the Unicode suit glyphs (♣♦♥♠) can't push them
   off-centre — the box centres the glyph regardless of its own ascent. */
.pcard-rank, .pcard-suit {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 1em;
  line-height: 1;
}
.pcard-rank { font-weight: 700; font-size: 0.78rem; }
.pcard-suit { font-size: 0.92rem; }
/* The suit is an inline <svg> (see suitSvg in replay.js), sized in `em`
   so it scales with .pcard-suit's font-size. `fill: currentColor` picks
   up the card's red/black colour. SVG is used instead of the Unicode
   ♠♥♦♣ glyphs because those render inconsistently across browsers. */
.pcard-suit-svg {
  width: 0.9em;
  height: 0.9em;
  display: block;
  fill: currentColor;
}
.pcard-sm .pcard-rank { font-size: 0.68rem; }
.pcard-sm .pcard-suit { font-size: 0.82rem; }
/* Inline card — flows inside GTO prose alongside text. Uses
   `vertical-align: middle` (not a fixed pixel offset) so it stays in
   sync with .tok-anysuit / .tok-modifier / .tok-suit-suffix — all four
   centre on the parent's text middle regardless of how many flex
   children they carry. */
.pcard-inline {
  display: inline-flex;
  width: 17px;
  height: 23px;
  border-radius: 3px;
  vertical-align: middle;
  margin: 0 1px;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
}
.pcard-inline .pcard-rank { font-size: 0.58rem; }
.pcard-inline .pcard-suit { font-size: 0.64rem; }
.pcard-back {
  background: repeating-linear-gradient(45deg, #2b5d8a, #2b5d8a 4px, #24507a 4px, #24507a 8px);
  border-color: #1c3e5e;
}

/* A run of 2+ inline cards (a board, a hand) rendered in prose — kept on
   one line as a single entity. white-space: nowrap suppresses the line
   break BETWEEN the cards, so the run wraps as a whole to the next line
   rather than splitting across two. */
.cardrun { white-space: nowrap; }

.replay-controls {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}
/* Tier-2 — playback controls. Bumped to a full 44px tap target with
   the standard secondary depth so they don't read as static glyphs. */
.replay-ctl {
  min-height: var(--tap-min);
  min-width: 44px;
  padding: 4px 8px;
  box-shadow: var(--shadow-1);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Control icons are inline SVG (see iconSvg in replay.js) — the Unicode
   media glyphs (⏮ ◀ ▶ ❚❚) render as inconsistent colour emoji. */
.replay-ctl-icon {
  width: 14px;
  height: 14px;
  display: block;
  fill: currentColor;
}
.replay-steplabel { font-size: 0.82rem; color: var(--text-dim); flex: 1 1 auto; }

/* .replay-history / .replay-log* removed — the standalone Action history
   collapsible was dropped. Its content (every fold + check + post)
   was noise; the spot-in-words prose now serves as the canonical
   textual narration. */

/* ----- Replay table — narrow / mobile widths ------------------------- */
/* The seat layout (80px seats, 46px corner insets) is tuned for the
   ~650px-wide desktop table. On a phone the table collapses toward
   square: 80px seats then overlap each other, the board, and the pot.
   At <=560px:
     - shrink the seats so six of them fit;
     - pull the four corner seats back out to the edges — the desktop
       inset (needed to clear the capsule ends) would, on a narrow table,
       crowd every seat into the centre over the board.
   The phone table is near-square, so a 140px corner radius drawn on the
   .replay-table box itself renders as a near-CIRCLE. To keep the
   distinctive horizontal capsule, the green felt is drawn instead by a
   ::before that is the full table width but much shorter — a true wide
   pill. The .replay-table box stays 300px tall so the seat layout is
   unchanged; the corner seats simply overhang the felt's rounded ends,
   which is fine. */
@media (max-width: 560px) {
  .rseat { width: 62px; padding: 3px; }
  .rseat-pos { font-size: 0.64rem; }
  .rseat-stack { font-size: 0.64rem; }
  .rslot-1 { bottom: 6px; left: 6px; }
  .rslot-2 { top: 8px; left: 6px; }
  .rslot-4 { top: 8px; right: 6px; }
  .rslot-5 { bottom: 6px; right: 6px; }
  /* Shrink the replay's small cards (seat hole cards + the community
     board) so the 5-card river board clears the side seats on narrow
     phones. */
  .replay .pcard-sm { width: 23px; height: 32px; }

  /* Move the felt off .replay-table (keep the box transparent but the
     same size — border kept transparent so seat geometry is unchanged)
     and onto a short, full-width ::before pill. */
  .replay-table {
    background: none;
    border-color: transparent;
    border-radius: 0;
    box-shadow: none;
  }
  .replay-table::before {
    content: "";
    position: absolute;
    top: 44px;
    bottom: 44px;
    left: 0;
    right: 0;
    background:
      radial-gradient(circle at 1px 1px, rgba(255, 255, 255, 0.025) 1px, transparent 1.5px) 0 0 / 5px 5px,
      radial-gradient(ellipse at center, #2c6e52 0%, #1b4736 100%);
    border: 6px solid #0e2a1d;
    border-radius: 999px;
    box-shadow:
      inset 0 0 40px rgba(0, 0, 0, 0.45),
      inset 0 1px 0 rgba(255, 255, 255, 0.05),
      0 6px 18px rgba(0, 0, 0, 0.45);
    pointer-events: none;
  }
}

/* Mobile */
@media (max-width: 480px) {
  .input-row { grid-template-columns: 1fr; }
  .stats-row { grid-template-columns: 1fr; }
  .stat-block.big { grid-column: span 1; }
  .app-title { font-size: 1.4rem; }
  .actions-row .action-btn { flex: 1 1 100%; }

  /* H-1: at narrow widths, give the icon-only header buttons a real tap
     target with a visible border (the icons alone are too easy to miss
     on a phone). Applies wherever icon-btn / share-icon-btn appears —
     solo header, duel header, etc. */
  .icon-btn,
  .share-icon-btn {
    padding: 6px 10px;
    font-size: 1.05rem;
    min-width: 40px;
    text-align: center;
    text-decoration: none;
    border: 1px solid var(--border-interactive);
    border-radius: 8px;
    color: var(--text-dim);
    line-height: 1;
  }
  @media (hover: hover) {
    .icon-btn:hover,
    .share-icon-btn:hover {
      background: var(--bg-elev);
      color: var(--text);
    }
  }
  .share-icon-btn.is-copied { color: var(--ok); border-color: var(--ok); }

  /* H-3 / M-1 (grid touch targets) intentionally NOT enforced here.
     The original fluid 13x13 villain grid and 4x13 hero/board grid
     stay as-is on mobile. On the audience's devices (iPhone 15 /
     iPhone 17 Pro Max), the original cells are tight but tappable;
     the horizontal-scroll workaround was worse than the problem. */
}

/* ===== Mobile-affordance base interaction patch ========================
   Universal press feedback, keyboard focus rings, and removal of the
   mobile tap-highlight flash. (touch-action: manipulation is already
   applied to every element at the top of this file — it kills the
   300ms double-tap delay.) Placed last in the file so the
   :focus-visible rings reliably win the cascade over any earlier
   :focus rule; all :hover rules elsewhere are gated under
   @media (hover: hover) so they never stick on a touch device. */
button, [role="button"], .rp-cell, .villain-range-card,
.spot-sum-action, .spot-sum-yourturn, .term-trigger {
  -webkit-tap-highlight-color: transparent;
}
/* Press feedback — a quick scale-down on every tappable element. */
button:active,
[role="button"]:active,
.villain-range-card.is-clickable:active,
.spot-summary.is-clickable .spot-sum-action:active,
.spot-summary.is-clickable .spot-sum-yourturn:active {
  transform: scale(0.97);
  filter: brightness(1.06);
}
/* Matrix cells are small — a deeper squash makes the press read. */
.rp-cell:active { transform: scale(0.88); }
/* Disabled controls must not appear to respond to a press. */
button:disabled:active { transform: none; filter: none; }
/* Keyboard focus — a visible ring on every interactive element. Never
   removed without a replacement (WCAG 2.2 SC 2.4.7). */
button:focus-visible,
[role="button"]:focus-visible,
.rp-cell:focus-visible,
.villain-range-card:focus-visible,
.spot-sum-action:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.rp-cell:focus-visible { outline-offset: -1px; }

/* Reduced-motion — honour the OS "reduce motion" setting app-wide.
   GTO Drill is a serious analysis tool; none of its motion is essential,
   so all of it collapses to near-instant when the user has asked for
   less. (A narrower rule above already covers the confidence pulse.) */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
