:root {
  /* Cool navy / soft cyan / off-white palette. Lower-saturation than the
     previous #4f8bff scheme — text reads as true off-white and the accent
     is a desaturated light blue closer to a sky-cyan. */
  --bg: #0d141d;
  --surface: #131c28;
  --surface-hover: #1a2330;
  --row: transparent;
  --border: #1f2a3a;
  --border-strong: #2a3a52;

  --text: #f3f6fb;
  --muted: #8ea3bd;

  --accent: #6dc6e8;
  --accent-soft: rgba(109, 198, 232, 0.14);
  --accent-strong: rgba(109, 198, 232, 0.42);

  --btn: #a9dcef;
  --btn-hover: #c2e6f4;
  --btn-text: #0d141d;

  --danger: #f87171;
  --ok: #4ade80;

  /* Used by perspective-owner highlight on the replay felt + the
   * delete UI (per-round checkbox + bulk Delete + per-table Delete
   * buttons). Mirrors the statisticasino site's `--hero`. */
  --hero: #ef4444;
  --hero-soft: rgba(239, 68, 68, 0.18);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 14px;
  min-height: 100vh;
}

body {
  display: flex;
  flex-direction: column;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ------- Top bar (slim) ------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 20px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  min-height: 44px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  font-size: 14px;
  white-space: nowrap;
}

/* Two-card brand mark.
 *
 * Composition: a card-back tile tucked at the upper-left, with the
 * A-of-Hearts overlapping it from the lower-right. Both cards are
 * pre-rendered PNGs emitted by `scripts/build-icons.py` from the same
 * `assets/cards@2x.png` sprite that powers the toolbar icons, so the
 * brand mark and the toolbar icon stay in lock-step.
 *
 * Sizing: the parent's `--brand-card-h` controls the overall mark
 * height. The two child PNGs auto-scale to that height. We size the
 * outer box with extra width on the left so the back card can peek
 * out beyond the front card's silhouette. */
/* Two-card brand mark.
 *
 * Box layout: the container's width covers the FRONT card plus enough
 * extra room on the left for the rotated BACK card to sit fully inside
 * (no clipping, no overlap with adjacent topbar items). The back card
 * is anchored to the box's upper-left and rotated CCW; the front card
 * is anchored to the box's lower-right and overlaps the back. */
.brand-cards {
  --brand-card-h: 24px;
  /* Each card image height is slightly less than the full box height
   * so the rotated back card has vertical room to fan out. */
  --card-h: calc(var(--brand-card-h) * 0.86);
  /* Card aspect (width / height) of the rendered PNGs is ~0.74. */
  --card-w: calc(var(--card-h) * 0.74);
  position: relative;
  display: inline-block;
  vertical-align: middle;
  /* Width = front card + ~55% of card width worth of back-card peek
   * on the left. Tweak the 0.55 if you want more/less peek. */
  width: calc(var(--card-w) * 1.55);
  height: var(--brand-card-h);
  flex-shrink: 0;
}
.brand-cards .card {
  position: absolute;
  height: var(--card-h);
  width: auto;
  /* Subtle drop shadow so the cards read as physical objects against
   * the navy topbar background. */
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.45));
}
.brand-cards .card-back {
  /* Anchored upper-left, rotated CCW. transform-origin at the centre
   * so the rotation doesn't shove the card off the canvas in either
   * direction — the visual peek stays balanced. */
  top: 0;
  left: 0;
  transform: rotate(-10deg);
  transform-origin: 50% 50%;
}
.brand-cards .card-front {
  /* Anchored lower-right, drawn after .card-back so it z-stacks on
   * top. No rotation — the A-of-Hearts reads as the "primary" card. */
  bottom: 0;
  right: 0;
}

.top-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: 8px;
}

.nav-tab {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--muted);
  padding: 5px 12px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.2px;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.nav-tab:hover { color: var(--text); }
.nav-tab[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
}
.nav-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.topbar-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
}

.status-pill {
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid var(--accent-strong);
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.2px;
  white-space: nowrap;
}
.status-pill.muted {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--muted);
}
.status-pill:empty { display: none; }

/* ------- Main / sections ------- */

main {
  flex: 1;
  width: 100%;
  margin: 0;
  padding: 18px 24px 40px 24px;
  position: relative;
}

.section { display: none; }
.section.section-active { display: block; }

.section-heading {
  margin: 0 0 16px 0;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0.2px;
}

/* ------- Session list (borderless hierarchy) -------
   The list is a plain indented tree. No cards, no rectangular borders;
   the only visual container in the whole replay panel is the oval felt
   itself. Each level uses a small caret + indent to show nesting:

       ▸ Table  Aquarium #4321   2 hands · 84 frames    14:02 · 6m
          ▸ Round 1   14:02   18 actions   FINISHED
               [oval felt]
          ▸ Round 2   ...

   Click on either row toggles its body (existing JS handlers continue
   to dispatch on `.session-row` / `.hand-row`). */

.session-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.session {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
}

/* Wraps the table-level checkbox + the toggle row so they sit on one
 * line. The toggle row keeps `flex: 1` so the rest of the row's grid
 * layout still works. The Delete table button was removed in favor of
 * the new tri-state selection + global Delete selected (per chat
 * 2026-05-21). */
.session-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.session-head > .session-row { flex: 1; min-width: 0; }
.session-select { flex-shrink: 0; }

.session-row {
  display: grid;
  grid-template-columns: 16px 1fr auto;
  gap: 12px;
  align-items: center;
  padding: 6px 4px;
  cursor: pointer;
  user-select: none;
  border-radius: 4px;
  transition: background 120ms ease;
}
.session-row:hover { background: rgba(109, 198, 232, 0.04); }
.session-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Caret as a CSS-only `▸` glyph that rotates 90° when expanded. */
.session-row::before,
.hand-row::before {
  content: "▸";
  display: inline-block;
  width: 16px;
  text-align: center;
  color: var(--muted);
  font-size: 11px;
  line-height: 1;
  transition: transform 160ms ease, color 120ms ease;
}
.session[aria-expanded="true"] .session-row::before,
.hand-item[aria-expanded="true"] .hand-row::before {
  transform: rotate(90deg);
  color: var(--accent);
}

.session-info {
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}

.session-title {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--text);
  min-width: 0;
}
.session-title .session-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.session-title .session-tableid {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}

.session-sub {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 12.5px;
  color: var(--muted);
}
.session-sub .dot-sep { color: var(--border-strong); }

.session-meta {
  text-align: right;
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12.5px;
  color: var(--muted);
  white-space: nowrap;
}
.session-meta .session-time {
  color: var(--text);
  font-weight: 500;
  font-size: 12.5px;
}

/* ------- Session body / hand list (indented, no border) ------- */

.session-body {
  display: none;
  padding: 2px 0 8px 28px;
}
.session[aria-expanded="true"] > .session-body {
  display: block;
}

.hand-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Wrap the per-round delete checkbox + clickable .hand-row on one line. */
.hand-item-row {
  display: grid;
  grid-template-columns: 22px 1fr;
  align-items: center;
}
.tree-select {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 4px 0;
}
.tree-select input[type="checkbox"] {
  accent-color: var(--hero, #ef4444);
  cursor: pointer;
}

.hand-row {
  display: grid;
  grid-template-columns: 16px 88px auto auto auto 1fr;
  gap: 12px;
  align-items: center;
  padding: 4px 4px;
  background: transparent;
  border: 0;
  border-radius: 4px;
  font-size: 12.5px;
  cursor: pointer;
  transition: background 120ms ease;
}
.hand-row:hover { background: rgba(109, 198, 232, 0.04); }
.hand-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.hand-index {
  color: var(--text);
  font-weight: 600;
  letter-spacing: 0.2px;
}
.hand-time, .hand-meta {
  color: var(--muted);
  white-space: nowrap;
}

.hand-status {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid transparent;
  white-space: nowrap;
  justify-self: start;
}
/* Round-state pill (lifecycle axis: finished / in-progress /
   incomplete). Distinct from the sync pill below. */
.hand-status.round-finished {
  color: var(--ok);
  background: rgba(74, 222, 128, 0.12);
  border-color: rgba(74, 222, 128, 0.35);
}
.hand-status.round-in-progress {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: var(--accent-strong);
}
.hand-status.round-incomplete {
  color: #f59e0b;
  background: rgba(245, 158, 11, 0.12);
  border-color: rgba(245, 158, 11, 0.4);
}

/* Per-hand flush badge — sits alongside `.hand-status`. Same shape as
   the status pill, but a shorter label and state-coded colors so the
   user can scan the History list and immediately see what's synced. */
.flush-badge {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid transparent;
  white-space: nowrap;
  justify-self: start;
}
.flush-badge.flush-flushed {
  color: var(--ok);
  background: rgba(74, 222, 128, 0.12);
  border-color: rgba(74, 222, 128, 0.35);
}
.flush-badge.flush-pending {
  color: var(--muted);
  background: rgba(148, 163, 184, 0.14);
  border-color: rgba(148, 163, 184, 0.35);
}
.flush-badge.flush-flushing {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: var(--accent-strong);
}
.flush-badge.flush-needs-reflush {
  color: #f59e0b;
  background: rgba(245, 158, 11, 0.14);
  border-color: rgba(245, 158, 11, 0.45);
  cursor: help;
}

.no-hands {
  margin: 4px 0 0 0;
  padding: 4px 0;
  color: var(--muted);
  font-size: 12.5px;
  font-style: italic;
}

/* ------- Empty state ------- */

.empty-state {
  margin-top: 80px;
  text-align: center;
  color: var(--muted);
}
.empty-state.hidden { display: none; }

/* Larger size of the two-card brand mark, used as the empty-state hero. */
.empty-cards {
  --brand-card-h: 96px;
  display: block;
  margin: 0 auto 24px;
}

.empty-title {
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 8px;
}

.empty-sub {
  font-size: 13.5px;
  max-width: 420px;
  margin: 0 auto;
  line-height: 1.5;
}

code {
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 1px 5px;
  border-radius: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
}

/* ------- Settings ------- */

.settings-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px 18px;
  margin-bottom: 14px;
}

.settings-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}
.settings-card-head h3 {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.2px;
  color: var(--text);
}

.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
  font-size: 13px;
  color: var(--text);
}
.settings-row + .settings-row {
  border-top: 1px solid var(--border);
}
.settings-row label {
  color: var(--text);
  font-size: 13px;
}

.settings-help {
  margin: 6px 0 0 0;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.5;
}

.settings-control {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.settings-control input[type="number"] {
  width: 80px;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  padding: 5px 8px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.settings-control input[type="number"]:focus {
  outline: none;
  border-color: var(--accent);
}
.settings-control .unit {
  color: var(--muted);
  font-size: 12.5px;
}

.settings-value {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  color: var(--muted);
}

.settings-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 12px;
}

/* Light-blue button face (palette rule: light blue is buttons only). */
.btn-primary {
  appearance: none;
  background: var(--btn);
  color: var(--btn-text);
  border: 1px solid var(--btn);
  padding: 7px 16px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.2px;
  cursor: pointer;
  transition: background 120ms ease, transform 80ms ease;
}
.btn-primary:hover {
  background: var(--btn-hover);
  border-color: var(--btn-hover);
}
.btn-primary:active { transform: translateY(1px); }
.btn-primary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.btn-primary[disabled],
.btn-secondary[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

/* Quieter twin of `.btn-primary` — same dimensions, transparent face,
   subtle border. Used for the "Export all" action which is a peer of
   "Flush now" but shouldn't compete visually. */
.btn-secondary {
  appearance: none;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--accent-strong);
  padding: 7px 16px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.2px;
  cursor: pointer;
  transition: background 120ms ease, transform 80ms ease;
}
.btn-secondary:hover {
  background: var(--accent-soft);
}
.btn-secondary:active { transform: translateY(1px); }
.btn-secondary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Destructive button used for the delete-table button + the bulk
 * "Delete N rounds" toolbar action. */
.btn-danger {
  appearance: none;
  background: var(--hero, #ef4444);
  color: #fff;
  border: 1px solid var(--hero, #ef4444);
  padding: 7px 16px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.2px;
  cursor: pointer;
  transition: filter 120ms ease, transform 80ms ease;
}
.btn-danger:hover  { filter: brightness(1.08); }
.btn-danger:active { transform: translateY(1px); }
.btn-danger:focus-visible {
  outline: 2px solid var(--hero, #ef4444);
  outline-offset: 2px;
}

/* Compact button modifier — used on per-row "Delete table" buttons so
 * they don't crowd the table header. */
.btn-sm {
  padding: 3px 10px;
  font-size: 11.5px;
  border-radius: 6px;
}

/* ============================================================
   Sticky action toolbar (top of History page).

   Always visible once at least one table is captured. Buttons that
   don't apply (Delete / Export when nothing selected) get the
   disabled attribute and the standard btn-disabled look.
   ============================================================ */
.action-bar {
  position: sticky;
  /* Topbar is 44 px tall (see `.topbar`), so docking the toolbar at
   * top: 44px makes it sit immediately under it as the user scrolls. */
  top: 44px;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 10px;
  /* Extend to the section's padding edges so the bar reads as a
   * full-width chrome element rather than a floating pill. */
  margin: 0 0 12px;
  padding: 8px 4px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.action-bar[hidden] { display: none; }
.action-bar-count {
  color: var(--muted);
  font-size: 12.5px;
  margin-right: auto;
}
.action-bar-buttons {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.action-bar-buttons button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Sync-card layout: two buttons side by side instead of a single
   right-anchored primary. */
.sync-actions {
  justify-content: flex-end;
  gap: 8px;
}

/* Endpoint URL is long; let it shrink + ellipsize rather than spill. */
.endpoint-value {
  max-width: 60%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
}

/* Inline error banner under the status row. Only shown when
   manualFlush / manualExport surfaces a failure. */
.flush-error {
  margin-top: 8px;
  color: #f59e0b;
}

/* Toggle switch — uses --accent for the on-state track. */
.toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  user-select: none;
}
.toggle input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.toggle-slider {
  position: relative;
  width: 36px;
  height: 20px;
  background: rgba(143, 163, 189, 0.15);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  transition: background 120ms ease, border-color 120ms ease;
  flex-shrink: 0;
}
.toggle-slider::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  background: var(--muted);
  border-radius: 50%;
  transition: transform 140ms ease, background 120ms ease;
}
.toggle input:checked + .toggle-slider {
  background: var(--accent-soft);
  border-color: var(--accent);
}
.toggle input:checked + .toggle-slider::after {
  transform: translateX(16px);
  background: var(--accent);
}
.toggle input:focus-visible + .toggle-slider {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.toggle-label {
  color: var(--text);
  font-size: 13px;
  font-weight: 500;
}

/* Secondary toggle living below the primary help text inside a card
   (e.g. "Auto-delete after flush" under the Sync card's auto-flush
   header). Pulls in a top margin and a separator so it doesn't blend
   into the help paragraph above it. */
.settings-row-toggle {
  display: inline-flex;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  width: 100%;
  box-sizing: border-box;
}

/* Stacked label + input variant of `.settings-row`. Used by the
   admin flush-token field; the label sits above a full-width input
   instead of the usual key/value side-by-side layout. */
.settings-row-stack {
  display: block;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.settings-field-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 6px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.settings-text-input {
  width: 100%;
  box-sizing: border-box;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
}
.settings-text-input:focus {
  outline: none;
  border-color: var(--accent);
}

/* ============================================================
   Inline Replay panel (under a hand row)
   ============================================================ */

.hand-item { list-style: none; }
.hand-item[aria-expanded="true"] .hand-row .hand-index {
  color: var(--accent);
}

.hand-replay[hidden] { display: none; }
.hand-replay {
  margin: 6px 0 10px 28px;
}

/* The replay panel itself is just a transparent wrapper now — the only
   visual container left is the oval felt below. */
.replay-inline {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  display: grid;
  grid-template-rows: auto auto auto;
  gap: 10px;
}

.replay-empty {
  color: var(--muted);
  padding: 8px 0;
  font-style: italic;
}

.replay-topline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}
.replay-headline {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
.replay-title {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.2px;
}
.replay-subtitle {
  font-size: 12.5px;
  color: var(--muted);
}
.replay-step-label {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid var(--accent-strong);
  padding: 3px 10px;
  border-radius: 999px;
  white-space: nowrap;
}

/* Felt — navy ellipse with the Replay Poker hold'em watermark.
   The wrap fills the available width so the felt can stretch up to its
   ~1.4x larger cap (1500px). The seat layout uses percentage-based
   `--x`/`--y`, so widening the felt automatically spreads seats apart;
   meanwhile cards/plates/icons/pot/scrubber are sized in absolute px and
   stay the same physical size — exactly what's needed to fit a 9-seat
   layout without resizing the UI controls. */
.replay-felt-wrap {
  position: relative;
  padding: 8px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: visible;
}
.replay-felt {
  position: relative;
  width: min(100%, 1500px);
  aspect-ratio: 16 / 9;
  background:
    radial-gradient(ellipse at 50% 42%, #1b2a3e 0%, #131e2c 65%, #0d141d 100%);
  /* Bloated rounded-rectangle/oval hybrid — slightly bigger radii on the
     vertical axis to keep the long sides nearly straight. */
  border-radius: 32% / 50%;
  border: 1px solid var(--accent-strong);
  box-shadow:
    inset 0 0 0 6px rgba(109, 198, 232, 0.05),
    inset 0 0 80px rgba(0, 0, 0, 0.45),
    0 8px 30px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}

/* Replay Poker hold'em watermark. Lives at the dead centre of the felt,
   sized larger; it is allowed to sit underneath the community cards (cards
   live above on z-index: 2; watermark below on z-index: 1). */
.felt-watermark {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  opacity: 0.22;
  pointer-events: none;
  width: 26%;
  min-width: 160px;
  max-width: 280px;
  display: flex;
  justify-content: center;
  z-index: 1;
}
.felt-watermark img {
  width: 100%;
  height: auto;
  filter: drop-shadow(0 3px 8px rgba(0,0,0,0.6));
}

/* Pot — pill on the left with the numeric amount, zig-zag chip stack on
   the right. Placed directly ABOVE the community board (board sits at
   ~top: 52%, pot bottom now lands a few px above it). The container
   itself is just an anchor; visual styling lives on `.pot-pill`. */
/* Pot — anchored by its BOTTOM edge so the pill sits directly above
   the community cards regardless of chip-stack height. The board lives
   at top:52% (centred); the cards are ~14% felt-height tall, so their
   top edge is roughly at top:45%. We place the pot's bottom edge at
   top:44%, which leaves a tiny gap above the cards. */
.felt-pot {
  position: absolute;
  left: 50%;
  top: 44%;
  transform: translate(-50%, -100%);
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  gap: 8px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  z-index: 2;
}
.pot-pill {
  background: rgba(10, 20, 40, 0.7);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: 4px 14px;
}
.felt-pot .pot-label {
  color: var(--muted);
  font-size: 10.5px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  margin-right: 6px;
}

.felt-board {
  position: absolute;
  left: 50%;
  top: 52%;
  transform: translate(-50%, -50%);
  display: flex;
  gap: 8px;
  z-index: 2;
}

/* ---- Cards (CasinoCards renderer) ----
   All cards render at one canonical size (DEFAULT_WIDTH in cards.js) so
   the layout never shifts when hidden cards flip to faces. */
.card-wrap {
  display: inline-block;
  line-height: 0;
  vertical-align: top;
  border-radius: 7px;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.4));
  user-select: none;
  /* Pivot from horizontal centre for the sinusoidal flip animation. */
  transform-origin: 50% 50%;
  backface-visibility: hidden;
}
.card-wrap svg.card-svg {
  display: block;
  border-radius: 6px;
}
.card-wrap.empty { opacity: 0.5; }
/* All hidden cards are clickable: peekable -> flip; otherwise -> shake. */
.card-wrap.hidden { cursor: pointer; }
.card-wrap.hidden.peekable:hover svg.card-svg {
  outline: 2px solid var(--accent-strong);
  outline-offset: -1px;
}
.card-wrap.hidden:not(.peekable):hover svg.card-svg {
  outline: 2px dashed rgba(248, 113, 113, 0.5);
  outline-offset: -1px;
}
.card-fallback {
  display: inline-block;
  background: #f3f6fc;
  color: #0d141d;
  border: 1px solid rgba(0,0,0,0.15);
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 700;
  padding: 6px 8px;
  line-height: 1;
}

/* ---- Seats around the felt ----
   Each .felt-seat is a CSS grid using a single uniform 2x2 template
   (icon|bet on top, plate|cards on bottom). See the unified grid block
   below for details. The seat itself is anchored at (--x, --y) on the
   felt. The orient-* classes are still emitted on the element as a
   hook for any per-position styling (e.g. dealer-button placement). */
/* The seats container is a full-felt absolute overlay so each seat can
   anchor itself with (left, top) percentages relative to the felt. With
   z-index: 3 it stacks above the community board; without
   `pointer-events: none` here the empty stretches of the overlay (which
   visually cover the board area) would swallow every click on a board
   card. The actual seats restore `pointer-events: auto`. */
.felt-seats { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.felt-seat { pointer-events: auto; }
.felt-seat {
  position: absolute;
  display: grid;
  /* The anchor point is the centre of the seat; CSS positions us with
     `transform: translate(-50%, -50%)` so each seat is centred on its
     (--x, --y). */
  left: var(--x, 50%);
  top: var(--y, 50%);
  transform: translate(-50%, -50%);
}

.seat-icon {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: #131c28;
  border: 2px solid var(--border-strong);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  justify-self: center;
}
/* Per-state icon fills. The base rule lives in `.seat-icon` above
   (dark navy fill + grey ring); each state below colors the WHOLE icon
   ring + plate (see plate rules further down). Precedence is enforced
   by source order — the most specific / latest rule wins. Visual
   hierarchy from least to most important:
     1. base                (dark navy fill, grey ring)
     2. folded              (light grey)
     3. hero                (deep red)
     4. acting              (deep yellow)
     5. hero+acting         (deep orange)
     6. sit-out             (dark grey — overrides hero/acting)
     7. all-in              (deep red — overrides everything except winner/refund)
     8. seat-refund         (cyan — refunded chips coming back)
     9. seat-winner         (green — overrides everything) */
.felt-seat.folded      .seat-icon { background: #6b7280; border-color: #9ca3af; }
.felt-seat.hero        .seat-icon { background: #b91c1c; border-color: #f87171; }
.felt-seat.acting      .seat-icon { background: #ca8a04; border-color: #fbbf24; }
.felt-seat.hero.acting .seat-icon { background: #c2410c; border-color: #fb923c; }
.felt-seat.sit-out     .seat-icon { background: #374151; border-color: #4b5563; }
.felt-seat.all-in      .seat-icon { background: #7f1d1d; border-color: #b91c1c; }
.felt-seat.seat-refund .seat-icon { background: #155e75; border-color: #22d3ee; }
.felt-seat.seat-winner .seat-icon {
  background: #166534;
  border-color: var(--ok);
  box-shadow: 0 0 0 2px rgba(74, 222, 128, 0.35);
}
.seat-avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.seat-avatar-fallback {
  color: var(--muted);
  font-weight: 700;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 14px;
}

/* Hole cards stay upright. The grid places them in the correct cell for
   each orientation; no rotation/translate is required here. */
.seat-cards {
  display: flex;
  gap: 2px;
  align-self: end;
  justify-self: center;
}
.seat-cards.empty { visibility: hidden; }
.seat-cards:empty { display: none; }

/* ----------- Unified seat grid -----------
   Every seat — regardless of where it sits on the felt — uses the same
   internal 2x2 layout:
     icon  | bet (number + chip stack)
     plate | cards
   The icon sits upper-left of the plate; the plate's badge is below the
   icon. The bet block (numeric pill + zig-zag chip stack) lives on the
   upper-right, directly to the right of the icon. The hole cards sit on
   the lower-right, next to the plate. The orientation classes
   (orient-down / -up / -left / -right) are kept as a hook on the
   element for any future per-position tweaks but no longer alter the
   internal grid. */
.felt-seat {
  grid-template-columns: auto auto;
  grid-template-rows: auto auto;
  grid-template-areas:
    "icon  bet"
    "plate cards";
  column-gap: 8px;
  row-gap: 4px;
  align-items: start;
  justify-items: start;
}

/* Place the named slots into their grid areas. */
.felt-seat .seat-icon       { grid-area: icon;  align-self: end;   justify-self: center; }
.felt-seat .seat-name-plate { grid-area: plate; align-self: start; justify-self: center; }
/* Cards sit on the lower-right next to the plate, bottom-aligned with
   the plate so the cards' bottom edge sits on the same baseline as the
   plate. */
.felt-seat .seat-cards      { grid-area: cards; align-self: end;   justify-self: start;  }
/* Bet block sits on the upper-right next to the icon, bottom-aligned
   with the icon so the chip-stack baseline meets the icon's bottom. */
.felt-seat .seat-bet        { grid-area: bet;   align-self: end;   justify-self: start;  }

/* Fixed-size name plate. Same width and height regardless of pill state
   so the seat layout never jitters as the user scrubs through steps. */
.seat-name-plate {
  background: rgba(10, 20, 40, 0.92);
  border: 2px solid var(--border-strong);
  border-radius: 8px;
  padding: 4px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 140px;
  /* Three rows: name (12px) + stack (11px) + pill (18px) + gaps. Lock
     the height so the placeholder pill takes the same space as a real
     CALL 18 / RAISE 300 pill. */
  height: 62px;
  box-sizing: border-box;
  text-align: center;
  transition: background 140ms ease, border-color 140ms ease;
}
/* Plate colour rules — fill the WHOLE plate (matches the icon fill
   above). The plate and the icon are read as one "badge" by the user,
   so they get the same colour scheme on every state.

   Precedence (later rules win where the cascade overlaps):
     1. base               (dark navy fill, border-strong)
     2. folded             (light grey)
     3. hero               (deep red)
     4. acting             (deep yellow)
     5. hero+acting        (deep orange)
     6. sit-out            (dark grey — overrides hero/acting)
     7. all-in             (deep red — overrides hero/acting)
     8. seat-refund        (cyan — refunded chips coming back)
     9. seat-winner        (green — overrides everything) */
.felt-seat.folded .seat-name-plate {
  background: #6b7280;
  border-color: #9ca3af;
}
.felt-seat.hero .seat-name-plate {
  background: #b91c1c;
  border-color: #f87171;
}
.felt-seat.acting .seat-name-plate {
  background: #ca8a04;
  border-color: #fbbf24;
}
.felt-seat.hero.acting .seat-name-plate {
  background: #c2410c;
  border-color: #fb923c;
}
.felt-seat.sit-out .seat-name-plate {
  background: #374151;
  border-color: #4b5563;
}
.felt-seat.all-in .seat-name-plate {
  background: #7f1d1d;
  border-color: #b91c1c;
}
.felt-seat.seat-refund .seat-name-plate {
  background: #155e75;
  border-color: #22d3ee;
}
.felt-seat.seat-winner .seat-name-plate {
  background: #166534;
  border-color: var(--ok);
}

/* Keep all text legible on the coloured fills. */
.felt-seat.folded .seat-name,
.felt-seat.hero .seat-name,
.felt-seat.acting .seat-name,
.felt-seat.sit-out .seat-name,
.felt-seat.all-in .seat-name,
.felt-seat.seat-refund .seat-name,
.felt-seat.seat-winner .seat-name {
  color: #fff;
}
.felt-seat.folded .seat-stack,
.felt-seat.hero .seat-stack,
.felt-seat.acting .seat-stack,
.felt-seat.sit-out .seat-stack,
.felt-seat.all-in .seat-stack,
.felt-seat.seat-refund .seat-stack,
.felt-seat.seat-winner .seat-stack {
  color: rgba(255, 255, 255, 0.85);
}

.seat-name {
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 120px;
}
.seat-stack {
  font-size: 11px;
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
/* Pill height is locked so the plate height never shifts with/without an
   action. When there is no current action we still render a placeholder
   pill (visibility: hidden) that reserves the same vertical slot. */
.seat-action {
  margin-top: 3px;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid var(--accent-strong);
  border-radius: 999px;
  padding: 1px 8px;
  min-height: 16px;
  line-height: 14px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.seat-action.placeholder { visibility: hidden; }
.seat-action.action-fold { color: var(--muted); background: transparent; border-color: var(--border-strong); }
.seat-action.action-allIn { color: #fbbf24; background: rgba(251, 191, 36, 0.10); border-color: rgba(251, 191, 36, 0.35); }
.seat-action.action-show, .seat-action.action-muck { color: var(--ok); background: rgba(74, 222, 128, 0.10); border-color: rgba(74, 222, 128, 0.35); }
/* On any coloured plate fill (hero, acting, sit-out, all-in, refund,
   winner, folded), soften the action pill so it still reads as a chip
   but stops fighting the plate fill. */
.felt-seat.hero .seat-action,
.felt-seat.acting .seat-action,
.felt-seat.sit-out .seat-action,
.felt-seat.all-in .seat-action,
.felt-seat.seat-refund .seat-action,
.felt-seat.seat-winner .seat-action,
.felt-seat.folded .seat-action {
  color: #fff;
  background: rgba(0, 0, 0, 0.25);
  border-color: rgba(255, 255, 255, 0.4);
}

/* Bet block — pill (numeric amount) plus a zig-zag chip stack. Lives in
   the seat's `bet` grid cell (upper-right of the seat, right of the
   icon). Flex row so the pill and the chip stack sit shoulder-to-
   shoulder. */
.seat-bet {
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  gap: 6px;
  font-size: 11px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--text);
  white-space: nowrap;
  z-index: 4;
}
.seat-bet-amount {
  background: rgba(10, 20, 40, 0.7);
  border: 1px solid var(--accent-strong);
  border-radius: 999px;
  padding: 1px 8px;
}
/* Winnings/refund pills — same shape as the standing-bet pill, but
   re-tinted so the user can see chips flowing INTO a seat at the
   award step. The chip stack itself is unchanged (greedy denomination
   decomposition); only the numeric pill changes colour. */
.seat-bet-winnings .seat-bet-amount {
  background: rgba(22, 101, 52, 0.4);
  border-color: var(--ok);
  color: #fff;
}
.seat-bet-refund .seat-bet-amount {
  background: rgba(21, 94, 117, 0.4);
  border-color: #22d3ee;
  color: #fff;
}

/* ---- Chip stack visualisation ----
   A `.chip-stack` is a row of `.chip-col`s. Each column represents one
   denomination; same-denomination chips stack vertically inside the
   column. Chip image size and stack offsets live here so both the pot
   and the per-seat bet pick up the same visuals.
*/
.chip-stack {
  --chip-w: 36px;            /* visual width of a chip (2x previous) */
  --chip-h: 34px;            /* visual height (slight ellipse) */
  --chip-rise: 8px;          /* vertical offset between stacked chips */
  --chip-zig: 12px;          /* zig-zag baseline rise for odd columns */
  --chip-col-gap: -4px;      /* small overlap so adjacent columns hug */
  position: relative;
  display: inline-flex;
  flex-direction: row;
  align-items: flex-end;
  gap: var(--chip-col-gap);
  /* The stack itself only reserves space for ONE chip's worth — taller
     stacks overflow upward. Containers (.seat-bet, .felt-pot) align
     these flex-end so the bottom chip sits on the container baseline,
     and any tall stacks just rise visually above. */
  height: var(--chip-h);
  pointer-events: none;
  user-select: none;
}
.chip-col {
  position: relative;
  width: var(--chip-w);
  height: var(--chip-h);
  /* Allow individual chips (which are absolutely positioned) to render
     above the column box without being clipped. */
  overflow: visible;
  display: block;
}
/* Zig-zag: even-indexed columns sit on the low baseline, odd-indexed
   columns ride raised by --chip-zig. */
.chip-col-low  { transform: translateY(0); }
.chip-col-high { transform: translateY(calc(-1 * var(--chip-zig))); }

.chip-col .chip {
  position: absolute;
  left: 0;
  width: var(--chip-w);
  height: var(--chip-h);
  /* Stack from the bottom upward: chip with index k sits at
     k * --chip-rise above the column's bottom. */
  bottom: calc(var(--k, 0) * var(--chip-rise));
  display: block;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.45));
  image-rendering: -webkit-optimize-contrast;
}
/* Overflow badge sits at the top of an over-capacity column. */
.chip-stack-overflow {
  position: absolute;
  bottom: calc(var(--chip-h) + 7 * var(--chip-rise));
  left: 50%;
  transform: translateX(-50%);
  font-size: 9px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--text);
  background: rgba(10, 20, 40, 0.85);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: 0 4px;
  line-height: 12px;
  white-space: nowrap;
}

.seat-dealer {
  position: absolute;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--btn);
  color: var(--btn-text);
  font-weight: 700;
  font-size: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #0d141d;
  z-index: 4;
}
/* Pin the dealer chip to the side of the seat that faces the felt centre
   so it never collides with the cards. */
.felt-seat.orient-down  .seat-dealer { top: 0;     right: -10px; }
.felt-seat.orient-up    .seat-dealer { bottom: 0;  right: -10px; }
.felt-seat.orient-left  .seat-dealer { right: -10px; bottom: 8px; }
.felt-seat.orient-right .seat-dealer { left: -10px;  bottom: 8px; }

.seat-winner .seat-name-plate {
  border-color: var(--ok);
  box-shadow: 0 0 0 1px rgba(74, 222, 128, 0.45);
}

/* Scrubber */
.replay-controls {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 14px;
  align-items: center;
  padding-top: 4px;
}

.replay-step-btn {
  appearance: none;
  background: var(--btn);
  color: var(--btn-text);
  border: 1px solid var(--btn);
  padding: 6px 14px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.replay-step-btn:hover { background: var(--btn-hover); border-color: var(--btn-hover); }
.replay-step-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.replay-scrubber {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 26px;
  background: transparent;
  cursor: pointer;
}
.replay-scrubber::-webkit-slider-runnable-track {
  height: 6px;
  background: linear-gradient(to right, var(--accent-soft), var(--accent-soft));
  border: 1px solid var(--border-strong);
  border-radius: 999px;
}
.replay-scrubber::-moz-range-track {
  height: 6px;
  background: var(--accent-soft);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
}
.replay-scrubber::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -6px;
  background: var(--btn);
  border: 1px solid var(--btn-hover);
  border-radius: 50%;
  cursor: pointer;
}
.replay-scrubber::-moz-range-thumb {
  width: 16px;
  height: 16px;
  background: var(--btn);
  border: 1px solid var(--btn-hover);
  border-radius: 50%;
  cursor: pointer;
}

.replay-scrubber-wrap {
  position: relative;
  display: flex;
  flex-direction: column;
}

/* Milestone tick marks rendered as a separate strip directly above the
   slider. Big ticks = milestones, small ticks = every step. */
.replay-ticks {
  position: relative;
  height: 14px;
  pointer-events: none;
}
.replay-tick {
  position: absolute;
  top: 4px;
  width: 1px;
  height: 6px;
  background: var(--border-strong);
  transform: translateX(-0.5px);
}
.replay-tick.milestone {
  top: 0;
  height: 12px;
  width: 2px;
  background: var(--accent);
  border-radius: 1px;
}
.replay-tick-label {
  position: absolute;
  top: -16px;
  font-size: 10px;
  color: var(--muted);
  white-space: nowrap;
  transform: translateX(-50%);
}

/* ------- Responsive ------- */

@media (max-width: 600px) {
  .topbar { padding: 6px 12px; gap: 10px; }
  .brand-name { display: none; }
  main { padding: 14px 10px 32px 10px; }
  .session-row {
    grid-template-columns: 16px 1fr;
    gap: 10px;
  }
  .session-meta { display: none; }
  .hand-row {
    grid-template-columns: 16px 80px auto auto;
    gap: 10px;
  }
  .hand-row .hand-meta { display: none; }
  .session-body { padding-left: 18px; }
  .hand-replay { margin-left: 18px; }
}
