/* reversi.site – board, discs and the play page (PLAY package).
   Uses the design tokens and components of site.css (SPEC §4.11). Class prefixes: .board-*, .play-*.
   Board markup is built by public/js/app/board-view.js and reused by the daily challenge, learn and
   embed pages. No !important (site.css keeps the one for [hidden]). */

/* =========================================================================
   Board and discs (UI-18, UI-19, UI-20, UI-31, PSA-3, X-2)
   ========================================================================= */

.board-frame {
  --coord: 16px;
  --frame-pad: 10px;
  --board-suggest: #7dd3fc;
  --press-tint: rgba(255, 255, 255, 0.12);
  position: relative;
  display: grid;
  grid-template-columns: var(--coord) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) var(--coord);
  gap: 4px;
  width: 100%;
  aspect-ratio: 1;
  /* Equal horizontal and vertical totals keep the frame square (coordinates left and bottom). */
  padding: var(--frame-pad) calc(var(--frame-pad) + 4px) var(--frame-pad) calc(var(--frame-pad) - 4px);
  background: var(--board-frame);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  touch-action: manipulation; /* scroll and pinch-zoom still work; no double-tap zoom (PSA-V2) */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
.board-frame[data-coords='off'] {
  --coord: 0px;
  gap: 0;
  padding: var(--frame-pad);
}
.board-frame[data-coords='off'] :is(.board-ranks, .board-files) {
  visibility: hidden;
}

.board-ranks {
  grid-area: 1 / 1;
  display: grid;
  grid-template-rows: repeat(8, 1fr);
}
.board-files {
  grid-area: 2 / 2;
  display: grid;
  grid-template-columns: repeat(8, 1fr);
}
.board-ranks span,
.board-files span {
  display: grid;
  place-items: center;
  font: var(--fw-semibold) 11px/1 var(--font-sans);
  font-variant-numeric: tabular-nums;
  color: var(--board-coord);
}

.board-grid {
  grid-area: 1 / 2;
  position: relative;
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  grid-template-rows: repeat(8, minmax(0, 1fr));
  aspect-ratio: 1;
  min-width: 0;
  border-radius: var(--radius-xs);
  overflow: hidden;
  /* Star points on the grid intersections at c3, g3, c7 and g7. */
  background:
    radial-gradient(circle at 25% 25%, var(--board-star) 3px, transparent 3.5px),
    radial-gradient(circle at 75% 25%, var(--board-star) 3px, transparent 3.5px),
    radial-gradient(circle at 25% 75%, var(--board-star) 3px, transparent 3.5px),
    radial-gradient(circle at 75% 75%, var(--board-star) 3px, transparent 3.5px),
    radial-gradient(120% 90% at 30% 10%, var(--board-2), var(--board) 70%);
  box-shadow:
    inset 0 0 0 1px var(--board-line),
    inset 0 2px 8px rgba(0, 0, 0, 0.25);
}

/* Touch press preview: the row and column of the pressed square (one layer below the discs). */
.board-grid.is-pressing::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(var(--press-tint), var(--press-tint)) calc(var(--press-col, 0) * 100% / 7) 0 / 12.5% 100% no-repeat,
    linear-gradient(var(--press-tint), var(--press-tint)) 0 calc(var(--press-row, 0) * 100% / 7) / 100% 12.5% no-repeat;
}

/* Squares: 64 buttons with 1 px lines; the board is never dimmed on the opponent's turn. */
.board-cell {
  all: unset;
  box-sizing: border-box;
  position: relative;
  display: grid;
  place-items: center;
  min-width: 0;
  aspect-ratio: 1;
  perspective: 600px;
  cursor: default;
  box-shadow: inset -1px -1px 0 var(--board-line);
}
.board-cell[data-col='7'] {
  box-shadow: inset 0 -1px 0 var(--board-line);
}
.board-cell[data-row='7'] {
  box-shadow: inset -1px 0 0 var(--board-line);
}
.board-cell[data-row='7'][data-col='7'] {
  box-shadow: none;
}
.board-grid[data-interactive='true'] .board-cell.is-legal {
  cursor: pointer;
}
.board-cell:focus-visible {
  outline: 3px solid var(--cell-focus);
  outline-offset: -3px;
  z-index: 2;
}

/* Last move: yellow tint and ring. Suggested move (Hint) and the pressed target: rings. */
.board-cell:is(.is-last, .is-suggested, .is-press)::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.board-cell.is-last::before {
  background: var(--last-move);
  box-shadow: inset 0 0 0 2px var(--last-move-ring);
}
.board-cell.is-suggested::before {
  box-shadow: inset 0 0 0 3px var(--board-suggest);
  animation: board-suggest calc(var(--dur-slow) * 3) var(--ease-out) 2;
}
.board-cell.is-press::before {
  box-shadow: inset 0 0 0 3px var(--cell-focus);
  background: var(--press-tint);
}

/* Legal-move dot: static, about 4:1 on the felt (PSA-16); no endless pulse (UI-20). */
.board-cell.is-legal::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 24%;
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: var(--hint);
  box-shadow: 0 0 0 1.5px var(--hint-ring);
  pointer-events: none;
  transition:
    width var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast),
    box-shadow var(--dur-fast);
}
.board-grid:is([data-hints='off'], [data-interactive='false']) .board-cell.is-legal::after {
  display: none;
}

/* Preview in the colour of the side to move: hover (mouse) and keyboard focus. */
.board-grid[data-turn='black'] .board-cell.is-legal:focus-visible::after {
  width: 80%;
  background: rgba(22, 24, 28, 0.55);
  box-shadow: none;
}
.board-grid[data-turn='white'] .board-cell.is-legal:focus-visible::after {
  width: 80%;
  background: rgba(255, 255, 255, 0.6);
  box-shadow: none;
}
@media (hover: hover) and (pointer: fine) {
  .board-grid[data-interactive='true'][data-hints='on'][data-turn='black'] .board-cell.is-legal:hover::after {
    width: 80%;
    background: rgba(22, 24, 28, 0.55);
    box-shadow: none;
  }
  .board-grid[data-interactive='true'][data-hints='on'][data-turn='white'] .board-cell.is-legal:hover::after {
    width: 80%;
    background: rgba(255, 255, 255, 0.6);
    box-shadow: none;
  }
}

/* Press preview: rings on the discs the move would flip. */
.board-cell.will-flip .board-disc {
  outline: 3px solid var(--board-suggest);
  outline-offset: 1px;
}

/* Illegal tap: the legal dots pulse once. */
.board-grid[data-hints='on'].flash-legal .board-cell.is-legal::after {
  animation: board-flash 600ms var(--ease-out);
}

/* Disc: two-sided 3D disc. Changing data-color turns it over (UI-19). Do not put opacity, filter or
   overflow on .board-disc – they flatten preserve-3d and the white face would render black. */
.board-disc {
  position: relative;
  width: 82%;
  aspect-ratio: 1;
  border-radius: 50%;
  transform-style: preserve-3d;
  transition: transform var(--dur-flip) var(--ease-in-out) var(--flip-delay, 0ms);
  pointer-events: none;
}
.board-disc::before,
.board-disc::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
.board-disc::before {
  background: radial-gradient(circle at 34% 28%, var(--disc-black-hi), var(--disc-black) 60%, var(--disc-black-edge));
  box-shadow:
    inset 0 -2px 2px rgba(255, 255, 255, 0.06),
    0 2px 2px rgba(0, 0, 0, 0.35),
    0 4px 8px rgba(0, 0, 0, 0.25);
}
.board-disc::after {
  transform: rotateY(180deg);
  background: radial-gradient(
    circle at 34% 28%,
    var(--disc-white-hi),
    var(--disc-white-mid) 55%,
    var(--disc-white) 85%,
    var(--disc-white-edge)
  );
  box-shadow:
    inset 0 -2px 3px rgba(0, 0, 0, 0.12),
    0 2px 2px rgba(0, 0, 0, 0.3),
    0 4px 8px rgba(0, 0, 0, 0.2);
}
.board-disc[data-color='white'] {
  transform: rotateY(180deg);
}
.board-disc.is-new {
  animation: board-disc-in var(--dur-base) var(--ease-out);
}
.board-frame.no-anim .board-disc {
  transition: none;
}

/* End of game: the losing side's discs are toned down (tokens, no filter). */
.board-grid[data-winner='black'] .board-disc {
  --disc-white-hi: #d9d9d2;
  --disc-white-mid: #c9c9c2;
  --disc-white: #b3b3ab;
  --disc-white-edge: #9d9d95;
}
.board-grid[data-winner='white'] .board-disc {
  --disc-black-hi: #5c6169;
  --disc-black: #34373d;
  --disc-black-edge: #26282c;
}

/* Content over the board: countdown, "Game starting" (used by the online module). */
.board-overlay {
  grid-area: 1 / 2;
  z-index: 5;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--space-2);
  padding: var(--space-5);
  text-align: center;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  border-radius: var(--radius-xs);
  animation: board-fade-in var(--dur-base) var(--ease-out);
}
.board-overlay[hidden] {
  display: none;
}
.board-overlay .count {
  font: var(--fw-black) 64px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
}
.play-countdown {
  display: grid;
  justify-items: center;
  gap: var(--space-2);
}
.play-countdown__title {
  font: var(--fw-bold) var(--text-2xl)/1.2 var(--font-display);
}
.play-countdown__note,
.play-countdown__color {
  font-size: var(--text-md);
}

@keyframes board-disc-in {
  from {
    scale: 0.4;
  }
}
@keyframes board-fade-in {
  from {
    opacity: 0;
  }
}
@keyframes board-flash {
  50% {
    width: 36%;
    background: #fff;
  }
}
@keyframes board-suggest {
  50% {
    box-shadow: inset 0 0 0 5px var(--board-suggest);
  }
}

/* Board themes (PROD-18). Classic uses the design tokens as they are. */
/* Wood: the legal-move dot is 3.7:1 / 4.4:1 on the two felt tones and the last-move ring 6.9:1 on
   the tinted square (WCAG 1.4.11 needs 3:1). */
.board-frame[data-board='wood'] {
  --board: #b5835a;
  --board-2: #c7976b;
  --board-frame: #5b3a22;
  --board-line: rgba(60, 30, 10, 0.45);
  --board-star: rgba(60, 30, 10, 0.6);
  --board-coord: rgba(255, 240, 220, 0.9);
  --hint: rgba(35, 20, 8, 0.75);
  --hint-ring: rgba(255, 255, 255, 0.45);
  --last-move: rgba(255, 236, 170, 0.4);
  --last-move-ring: #3b2412;
}
.board-frame[data-board='midnight'] {
  --board: #1e3a4c;
  --board-2: #28506a;
  --board-frame: #0b1a24;
  --board-line: rgba(0, 0, 0, 0.45);
  --board-star: rgba(0, 0, 0, 0.55);
  --board-coord: rgba(220, 235, 255, 0.85);
  --hint: rgba(255, 255, 255, 0.72);
  --hint-ring: rgba(0, 0, 0, 0.4);
}
.board-frame[data-board='contrast'] {
  --board: #0b6e2a;
  --board-2: #0b6e2a;
  --board-frame: #000;
  --board-line: #000;
  --board-star: #000;
  --board-coord: #fff;
  --hint: #fff;
  --hint-ring: #000;
  --last-move: transparent;
  --last-move-ring: #ffe500;
  --cell-focus: #ffe500;
  --board-suggest: #ffe500;
}
.board-frame[data-board='contrast'] .board-cell {
  box-shadow: inset -2px -2px 0 var(--board-line);
}
.board-frame[data-board='contrast'] .board-cell.is-last::before {
  box-shadow: inset 0 0 0 4px var(--last-move-ring);
}
.board-frame[data-board='contrast'] .board-disc::before {
  background: #000;
  box-shadow: inset 0 0 0 3px #fff;
}
.board-frame[data-board='contrast'] .board-disc::after {
  background: #fff;
  box-shadow: inset 0 0 0 3px #000;
}

/* Animations setting (PROD-18): Fast halves the durations, Off turns board animations off. Reduced
   motion always wins (site.css zeroes the --dur-* tokens and the rules below switch everything off). */
@media (prefers-reduced-motion: no-preference) {
  .board-frame[data-anim='fast'] {
    --dur-flip: 180ms;
    --dur-base: 100ms;
    --dur-fast: 60ms;
    --dur-slow: 160ms;
  }
}
.board-frame[data-anim='off'] .board-disc {
  transition: none;
}
.board-frame[data-anim='off'] :is(.board-disc.is-new, .board-overlay) {
  animation: none;
}
.board-frame[data-anim='off'] .board-cell.is-suggested::before {
  animation: none;
}
.board-frame[data-anim='off'] .board-grid.flash-legal .board-cell.is-legal::after {
  animation: none;
}
.board-frame[data-anim='off'] .board-cell.is-legal::after {
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .board-disc {
    transition: none;
  }
  .board-disc.is-new,
  .board-overlay,
  .board-cell.is-suggested::before {
    animation: none;
  }
  .board-grid.flash-legal .board-cell.is-legal::after {
    animation: none;
  }
  .board-cell.is-legal::after {
    transition: none;
  }
}

/* Windows contrast themes: discs stay distinguishable (X-2). */
@media (forced-colors: active) {
  .board-frame {
    forced-color-adjust: none;
    --board-line: CanvasText;
    --board-star: CanvasText;
    --board-coord: CanvasText;
    --hint: Highlight;
    --hint-ring: Canvas;
    --last-move: transparent;
    --last-move-ring: Highlight;
    --cell-focus: Highlight;
    --board-suggest: Highlight;
    --press-tint: transparent;
    background: Canvas;
    outline: 2px solid CanvasText;
  }
  .board-grid {
    background: Canvas;
  }
  .board-disc::before {
    background: CanvasText;
    box-shadow: none;
  }
  .board-disc::after {
    background: Canvas;
    box-shadow: inset 0 0 0 3px CanvasText;
  }
  .board-overlay {
    forced-color-adjust: none;
    background: Canvas;
    color: CanvasText;
    border: 2px solid CanvasText;
  }
}

/* =========================================================================
   Disc chips (cards, move list, colour choice)
   ========================================================================= */

.play-chip {
  display: inline-block;
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  vertical-align: middle;
}
.play-chip[data-color='black'],
.play-card[data-color='black'] .play-card__chip {
  background: radial-gradient(circle at 34% 28%, var(--disc-black-hi), var(--disc-black) 65%);
}
.play-chip[data-color='white'],
.play-card[data-color='white'] .play-card__chip {
  background: radial-gradient(circle at 34% 28%, var(--disc-white-hi), var(--disc-white));
  box-shadow:
    0 0 0 1px var(--border-strong),
    0 1px 2px rgba(0, 0, 0, 0.25);
}
.play-card[data-color='none'] .play-card__chip {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--border-strong);
}
.seg .play-chip {
  width: 14px;
  height: 14px;
}
@media (forced-colors: active) {
  .play-chip,
  .play-card__chip {
    forced-color-adjust: none;
  }
  .play-chip[data-color='black'],
  .play-card[data-color='black'] .play-card__chip {
    background: CanvasText;
  }
  .play-chip[data-color='white'],
  .play-card[data-color='white'] .play-card__chip {
    background: Canvas;
    box-shadow: inset 0 0 0 2px CanvasText;
  }
}

/* =========================================================================
   Play page layout
   Desktop: board first, one sticky side panel (UI-12, UX-26). The page scrolls normally, so the
   footer and the page text are always reachable (UI-13, UX-7, PSA-1). Heights use svh so nothing
   jumps when mobile browser bars move.
   ========================================================================= */

body.page-play {
  overscroll-behavior-y: none;
}
:root:has(body.page-play) {
  overscroll-behavior-y: none;
}

.play {
  --panel-w: 340px;
  --board-max: 760px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--panel-w);
  gap: var(--space-6);
  align-items: start;
  /* At most 1200 px, centred, 16 px from the edges and clear of a notch or rounded corners in
     landscape (viewport-fit=cover makes env(safe-area-inset-*) non-zero there). */
  width: auto;
  margin-block: var(--space-5) var(--space-7);
  margin-inline: max(16px, env(safe-area-inset-left), (100% - 1200px) / 2)
    max(16px, env(safe-area-inset-right), (100% - 1200px) / 2);
}

.play-board {
  position: relative;
  justify-self: center;
  width: min(100%, calc(100vh - var(--header-h, 56px) - 64px), var(--board-max));
  width: min(100%, calc(100svh - var(--header-h, 56px) - 64px), var(--board-max));
  aspect-ratio: 1;
}
.play-noscript {
  display: grid;
  place-content: center;
  height: 100%;
  margin: 0;
  padding: var(--space-5);
  text-align: center;
  color: #fff;
  background: var(--board-frame);
  border-radius: var(--radius-lg);
}

.play-panel {
  position: sticky;
  top: calc(var(--header-h, 56px) + 16px);
  display: grid;
  gap: var(--space-4);
  max-height: calc(100svh - var(--header-h, 56px) - 32px);
  overflow: auto;
  overscroll-behavior: contain;
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.play-title {
  margin: 0;
  font: var(--fw-bold) var(--text-xl)/var(--lh-xl) var(--font-display);
  letter-spacing: var(--tracking-tight);
}

/* Setup: mode, level, colour, online panel. Visibility follows data-mode (no inline styles). */
.play-setup {
  display: grid;
  gap: var(--space-3);
  container: play-setup / inline-size;
}
.play-modes [role='radio'] {
  gap: 6px;
  padding-inline: var(--space-2);
}
/* The segmented controls fit narrow panels (desktop panel at 860–960 px, the phone sheet at 320 px):
   no mode icons and smaller level labels when the setup is narrow; wrapping instead of overflowing
   as the last resort (large text settings). */
.play-setup .seg {
  flex-wrap: wrap;
}
@container play-setup (max-width: 300px) {
  .play-modes .icon {
    display: none;
  }
  .play-setup .play-levels > [role='radio'] {
    padding-inline: 3px;
    font-size: 0.8125rem;
  }
}
.play-opt {
  display: none;
  gap: var(--space-3);
}
.play-setup[data-mode='computer'] .play-opt--computer,
.play-setup[data-mode='local'] .play-opt--local,
.play-setup[data-mode='online'] .play-opt--online {
  display: grid;
}
.play-field {
  display: grid;
  gap: var(--space-1);
  margin: 0;
}
.play-field__label {
  font-size: var(--text-sm);
  font-weight: var(--fw-semibold);
}
.play-field .help,
.play-opt .help {
  margin: 0;
}
.play-levels [role='radio'] {
  padding-inline: 4px;
}
.play-level-desc {
  min-height: var(--lh-sm, 1.25rem);
}
.play-seg-wrap {
  flex-wrap: wrap;
}
.play-online {
  display: grid;
  gap: var(--space-3);
}
.play-online__loading {
  margin: 0;
}

/* Player cards (UI-21, UX-14): opponent above, you below; colour, name, turn, score, online clock. */
.play-cards {
  display: grid;
  gap: var(--space-2);
}
.play-card {
  position: relative;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  grid-auto-columns: auto; /* clock (online only) and score */
  grid-auto-flow: column;
  align-items: center;
  gap: var(--space-3);
  min-height: 56px;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition:
    border-color var(--dur-base),
    box-shadow var(--dur-base);
}
.play-card.is-turn {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.play-card__chip {
  justify-self: center;
}
.play-card__who {
  display: grid;
  min-width: 0;
}
.play-card__name,
.play-card__status {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.play-card__name {
  font-weight: var(--fw-semibold);
  line-height: var(--lh-sm, 1.25rem);
}
.play-card__status {
  min-height: 1rem;
  font-size: var(--text-xs);
  line-height: 1rem;
  color: var(--text-muted);
}
.play-card.is-turn .play-card__status {
  color: var(--accent-text);
  font-weight: var(--fw-semibold);
}
.play-card__score {
  position: relative;
  min-width: 2ch;
  text-align: right;
  font: var(--fw-bold) var(--text-3xl)/1 var(--font-sans);
  font-variant-numeric: tabular-nums;
}
/* "+N" after a move (the placed disc and the flipped ones), next to the mover's score (PROD-7). */
.play-card__gain {
  position: absolute;
  top: 50%;
  right: calc(100% + 6px);
  translate: 0 -50%;
  padding: 1px 6px;
  font: var(--fw-bold) var(--text-sm)/1.2 var(--font-sans);
  color: var(--accent-text);
  white-space: nowrap;
  background: var(--accent-soft);
  border-radius: var(--radius-full);
  opacity: 0;
  pointer-events: none;
}
.play-card__gain.is-shown {
  opacity: 1;
  animation: play-gain 1100ms var(--ease-out) both;
}
.play[data-anim='fast'] .play-card__gain.is-shown {
  animation-duration: 700ms;
}
.play[data-anim='off'] .play-card__gain.is-shown {
  animation: none;
}
.play-card__clock {
  display: none;
  font: var(--fw-semibold) var(--text-sm)/1 var(--font-sans);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.play-card.has-clock .play-card__clock {
  display: block;
}
.play-card.has-clock.is-turn .play-card__clock {
  color: var(--text);
}
.play-card.is-low .play-card__clock {
  color: var(--warning);
}
.play-card.is-critical .play-card__clock {
  color: var(--danger);
}
.play-card__bar {
  position: absolute;
  inset: auto 12px 4px 12px;
  display: none;
  height: 3px;
  overflow: hidden;
  background: var(--surface-3);
  border-radius: 2px;
}
.play-card.has-clock .play-card__bar {
  display: block;
}
.play-card__bar > i {
  display: block;
  height: 100%;
  background: var(--accent);
  transform-origin: left center;
}
.play-card.is-low .play-card__bar > i {
  background: var(--warning);
}
.play-card.is-critical .play-card__bar > i {
  background: var(--danger);
}
.play-card__reaction {
  position: absolute;
  top: -14px;
  right: 56px;
  z-index: 2;
  padding: 2px 10px;
  font-size: var(--text-xl);
  line-height: 1.4;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-md);
}
.play-card__reaction.is-shown {
  animation: play-pop var(--dur-slow) var(--ease-out);
}

/* Replay controls after a game or for a shared game link. */
.play-review {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}
.play-review__nav {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}
.play-review__pos {
  min-width: 9ch;
  text-align: center;
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}
/* Evaluation line of a reviewed game (PROD-17): above the middle line you are ahead. */
.play-review__graph {
  display: grid;
  flex: 1 1 100%;
  gap: 2px;
  min-width: 0;
}
.play-graph {
  display: block;
  width: 100%;
  height: 56px;
  cursor: pointer;
  background: var(--surface-2);
  border-radius: var(--radius-sm);
}
.play-graph__area {
  fill: var(--accent-soft);
}
.play-graph__zero {
  stroke: var(--border-strong);
  stroke-width: 1;
}
.play-graph__line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
}
.play-graph__marker {
  stroke: var(--text-muted);
  stroke-width: 1.5;
  stroke-dasharray: 3 2;
}
.play-review__caption {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--text-muted);
}
@media (forced-colors: active) {
  .play-graph__area {
    fill: none;
  }
  .play-graph__line {
    stroke: CanvasText;
  }
  .play-graph__zero,
  .play-graph__marker {
    stroke: GrayText;
  }
}

/* Move list with coordinates (PROD-12). */
.play-moves {
  display: grid;
  gap: var(--space-2);
  min-width: 0;
}
.play-moves__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}
.play-moves__title {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--fw-semibold);
}
.play-assisted {
  font-size: var(--text-xs);
}
.play-moves__scroll {
  max-height: 180px;
  overflow: auto;
  padding: var(--space-2) var(--space-3);
  background: var(--surface-2);
  border-radius: var(--radius-sm);
}
.play-moves__empty {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--text-muted);
}
.play-moves__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  gap: 2px var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}
.play-moves__item {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 1px 4px;
  border-radius: 4px;
}
.play-moves__item .play-chip {
  width: 10px;
  height: 10px;
  box-shadow: 0 0 0 1px var(--border-strong);
}
.play-moves__no {
  min-width: 2ch;
  text-align: right;
  color: var(--text-subtle);
  font-size: var(--text-xs);
}
.play-moves__sq {
  font-weight: var(--fw-semibold);
}
.play:is([data-over='true'], [data-kind='local'][data-reviewing='true']) .play-moves__item {
  cursor: pointer;
}
.play:is([data-over='true'], [data-kind='local'][data-reviewing='true']) .play-moves__item:hover {
  background: var(--surface-3);
}
.play-moves__item.is-current {
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px var(--accent);
}
/* Move labels after a finished game (PROD-17): ?! inaccuracy, ? mistake, ?? blunder. */
.play-moves__mark {
  font-weight: var(--fw-bold);
  color: var(--warning);
}
.play-moves__item[data-label='blunder'] .play-moves__mark {
  color: var(--danger);
}
.play-moves__legend {
  margin: 0;
}

/* First-visit tip (links to the Learn page; hidden in focus mode and online). */
.play-tip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-1) var(--space-1) var(--space-1) var(--space-3);
  font-size: var(--text-sm);
  background: var(--info-soft);
  border-radius: var(--radius-md);
}
.play-tip__link {
  display: inline-flex;
  flex: 1;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  font-weight: var(--fw-semibold);
}
.play-tip__link .icon {
  flex: none;
  color: var(--info);
}
.play-tip__close {
  flex: none;
}
body.play-focus .play-tip {
  display: none;
}

/* Actions (UX-21: Undo is a neutral ghost button, disabled until you have moved). */
.play-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
.play-action {
  gap: 6px;
}
.play-actions__tools {
  display: flex;
  gap: var(--space-1);
  margin-left: auto;
}
.play-action--mobile {
  display: none;
}

/* SEO text below the game (h1 is in the panel, UI-12, PSA-22). */
.play-about {
  margin-top: var(--space-5);
}

/* =========================================================================
   Narrow landscape and short screens (X-1): keep board and panel side by side
   ========================================================================= */
@media (max-width: 1100px) {
  .play {
    --panel-w: clamp(260px, 34vw, 340px);
    gap: var(--space-5);
  }
}
@media (orientation: landscape) and (max-height: 600px) {
  .play {
    margin-block: var(--space-2) var(--space-6);
    gap: var(--space-4);
  }
  .play-board {
    width: min(100%, calc(100svh - var(--header-h, 56px) - 24px));
  }
  .play-panel {
    top: calc(var(--header-h, 56px) + 8px);
    max-height: calc(100svh - var(--header-h, 56px) - 16px);
    gap: var(--space-3);
    padding: var(--space-3);
  }
  .play-card {
    min-height: 48px;
    padding-block: 4px;
  }
  .play-card__score {
    font-size: var(--text-2xl);
  }
  .play-moves__scroll {
    max-height: 96px;
  }
}

/* =========================================================================
   Phones and narrow portrait screens (UI-29, UX-22, PSA-15): opponent card, board, your card,
   fixed action bar; the setup opens as a bottom sheet. Fits 390×844 without scrolling.
   ========================================================================= */
@media (max-width: 859px) and (orientation: portrait), (max-width: 599px) {
  /* The fixed action bar: page padding below it, toasts above it (site.css reads --toast-offset). */
  body.page-play {
    padding-bottom: calc(64px + env(safe-area-inset-bottom));
    --toast-offset: 76px;
  }
  .play {
    --card-h: 52px;
    --bar-h: 64px;
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'top' 'board' 'bottom' 'tip' 'review' 'moves';
    gap: var(--space-2);
    width: 100%;
    margin: 0;
    padding: var(--space-2) max(8px, env(safe-area-inset-right)) var(--space-4) max(8px, env(safe-area-inset-left));
  }
  .play-tip {
    grid-area: tip;
  }
  .play-panel,
  .play-cards {
    display: contents;
  }
  .play-title {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
  .play-setup-slot {
    display: none;
  }
  .play-card[data-seat='top'] {
    grid-area: top;
  }
  .play-card[data-seat='bottom'] {
    grid-area: bottom;
  }
  .play-review {
    grid-area: review;
  }
  .play-moves {
    grid-area: moves;
  }
  .play-card {
    min-height: var(--card-h);
    padding: 6px 10px;
  }
  .play-card__score {
    font-size: var(--text-2xl);
  }
  .play-board {
    grid-area: board;
    width: min(100%, calc(100vh - var(--header-h, 48px) - 2 * var(--card-h) - var(--bar-h) - 48px));
    width: min(
      100%,
      calc(100svh - var(--header-h, 48px) - 2 * var(--card-h) - var(--bar-h) - env(safe-area-inset-bottom) - 48px)
    );
  }
  /* A thin frame, so the squares are as big as the width allows: the frame and coordinates take
     18 px, which gives 40.75 px squares at 360 px and 44.5 px at 390 px (Auto confirm starts below
     40 px, i.e. on 320–350 px phones). */
  .board-frame {
    --coord: 10px;
    --frame-pad: 4px;
    gap: 0;
    border-radius: var(--radius-md);
  }
  .board-ranks span,
  .board-files span {
    font-size: 9px;
  }
  .board-disc {
    width: 84%;
  }

  /* Fixed action bar with the thumbs' reach. */
  .play-actions {
    position: fixed;
    inset: auto 0 0 0;
    z-index: var(--z-mobile-bar, 40);
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 2px;
    height: calc(var(--bar-h) + env(safe-area-inset-bottom));
    padding: 4px max(4px, env(safe-area-inset-right)) env(safe-area-inset-bottom) max(4px, env(safe-area-inset-left));
    background: var(--surface);
    border-top: 1px solid var(--border);
  }
  .play-action,
  .play-action--mobile {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
    min-width: 0;
    height: 56px;
    padding: 0 2px;
    font-size: 11px;
    line-height: 1.2;
    background: transparent;
    border-color: transparent;
    box-shadow: none;
  }
  .play-action span {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .play-action .icon {
    width: 22px;
    height: 22px;
  }
  .play-actions__tools {
    display: none;
  }
  /* Online lobby: the search panel is in the Mode sheet, so the bar offers the search itself. */
  .play-action--primary {
    grid-column: span 2;
    flex-direction: row;
    gap: 8px;
    height: 48px;
    margin: 4px 2px;
    border-color: var(--accent);
    border-radius: var(--radius-md);
    background: var(--accent);
    color: var(--on-accent);
    font-size: 14px;
    font-weight: 700;
  }

  /* First visit: the cookie banner (site.css, height in --consent-banner-h on :root) sits at the
     bottom. The bar moves above it and the board shrinks, so the cards and the board stay visible. */
  .has-consent-banner body.page-play {
    padding-bottom: calc(64px + var(--consent-banner-h, 0px));
  }
  .has-consent-banner body.page-play .toast-region {
    bottom: calc(var(--consent-banner-h, 0px) + 76px);
  }
  .has-consent-banner .play-actions {
    bottom: var(--consent-banner-h, 0px);
    height: var(--bar-h);
    padding-bottom: 0;
  }
  .has-consent-banner .play-board {
    width: min(
      100%,
      calc(100svh - var(--header-h, 48px) - 2 * var(--card-h) - var(--bar-h) - var(--consent-banner-h, 0px) - 48px)
    );
  }
}
/* From 520 px the banner is a card 16 px above the bottom edge (site.css). */
@media (max-width: 859px) and (orientation: portrait) and (min-width: 520px), (min-width: 520px) and (max-width: 599px) {
  .has-consent-banner .play-actions {
    bottom: calc(var(--consent-banner-h, 0px) + var(--space-4) + env(safe-area-inset-bottom));
  }
  .has-consent-banner body.page-play {
    padding-bottom: calc(64px + var(--consent-banner-h, 0px) + var(--space-4) + env(safe-area-inset-bottom));
  }
  .has-consent-banner .play-board {
    width: min(
      100%,
      calc(
        100svh - var(--header-h, 48px) - 2 * var(--card-h) - var(--bar-h) - var(--consent-banner-h, 0px) -
          env(safe-area-inset-bottom) - 64px
      )
    );
  }
}

/* =========================================================================
   Focus mode (followup/mobile.md §3a): header, footer, page text and setup hidden; the page does
   not scroll; board and bar fill a 100svh screen; no scrolling, zooming or pull-to-refresh from the
   board. Also used with the Fullscreen API on large screens.
   ========================================================================= */
body.play-focus {
  overflow: hidden;
  overscroll-behavior: none;
}
body.play-focus .site-header,
body.play-focus .site-footer,
body.play-focus .skip-link,
body.play-focus .play-about {
  display: none;
}
body.play-focus .play {
  position: fixed;
  inset: 0;
  z-index: 30;
  width: auto;
  height: 100vh;
  height: 100svh;
  margin: 0;
  overflow: hidden;
  padding: max(8px, env(safe-area-inset-top)) max(8px, env(safe-area-inset-right)) max(8px, env(safe-area-inset-bottom))
    max(8px, env(safe-area-inset-left));
  background: var(--bg);
  align-content: center;
}
body.play-focus .board-grid {
  touch-action: none;
}
body.play-focus .play-board {
  width: min(100%, calc(100svh - 32px), var(--board-max));
}
body.play-focus .play-panel {
  max-height: calc(100svh - 16px);
  top: 8px;
}
/* In focus mode the setup (mode, level, colour, online panel) is in the sheet (play.js placeSetup). */
body.play-focus .play-setup-slot,
body.play-focus .play-title,
body.play-focus .play-about {
  display: none;
}

/* Landscape phones and full screen (side panel layout): Mode opens the setup sheet and Cancel stops a
   search, as on portrait phones; the panel's own tools replace More. */
@media (min-width: 600px) and (orientation: landscape), (min-width: 860px) {
  body.play-focus .play-action--mobile {
    display: inline-flex;
  }
  body.play-focus .play-action--mobile[data-play-action='more'] {
    display: none;
  }
}

@media (max-width: 859px) and (orientation: portrait), (max-width: 599px) {
  body.play-focus {
    padding-bottom: 0;
  }
  body.play-focus .play {
    --gap: 8px;
    grid-template-rows: 1fr auto auto auto auto;
    grid-template-areas: '.' 'top' 'board' 'bottom' 'bar';
    align-content: stretch;
    gap: var(--gap);
    padding-bottom: 0;
  }
  body.play-focus .play-board {
    width: min(100%, calc(100vh - 2 * var(--card-h) - var(--bar-h) - 48px));
    width: min(
      100%,
      calc(100svh - 2 * var(--card-h) - var(--bar-h) - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 48px)
    );
  }
  body.play-focus .play-moves,
  body.play-focus .play-review {
    display: none;
  }
  body.play-focus .play-actions {
    position: static;
    grid-area: bar;
    margin-inline: calc(-1 * max(8px, env(safe-area-inset-left)));
    padding-bottom: env(safe-area-inset-bottom);
  }
  /* The cookie banner (first visit) keeps its place at the bottom; the game fits above it. */
  .has-consent-banner body.play-focus .play {
    padding-bottom: var(--consent-banner-h, 0px);
  }
  .has-consent-banner body.play-focus .play-actions {
    padding-bottom: 0;
  }
  .has-consent-banner body.play-focus .play-board {
    width: min(
      100%,
      calc(100svh - 2 * var(--card-h) - var(--bar-h) - env(safe-area-inset-top) - var(--consent-banner-h, 0px) - 48px)
    );
  }
}
@media (max-width: 859px) and (orientation: portrait) and (min-width: 520px), (min-width: 520px) and (max-width: 599px) {
  .has-consent-banner body.play-focus .play {
    padding-bottom: calc(var(--consent-banner-h, 0px) + var(--space-4) + env(safe-area-inset-bottom));
  }
  .has-consent-banner body.play-focus .play-board {
    width: min(
      100%,
      calc(
        100svh - 2 * var(--card-h) - var(--bar-h) - env(safe-area-inset-top) - env(safe-area-inset-bottom) -
          var(--consent-banner-h, 0px) - 64px
      )
    );
  }
}
@media (max-width: 360px) {
  .play {
    --card-h: 46px;
    --bar-h: 56px;
  }
  .play-modes .icon {
    display: none;
  }
}

/* =========================================================================
   Dialogs: setup sheet, end card, settings, stats, more
   ========================================================================= */
.play-sheet .play-setup {
  gap: var(--space-4);
}
.play-dialog .dialog__body {
  display: grid;
  gap: var(--space-3);
}

.play-end {
  display: grid;
  gap: var(--space-2);
}
.play-end__score {
  margin: 0;
  font: var(--fw-black) var(--text-4xl)/var(--lh-4xl, 1.1) var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-tight);
}
.play-end__score--win {
  color: var(--accent-text);
}
.play-end__sub,
.play-end__reason,
.play-end__note {
  margin: 0;
  color: var(--text-muted);
}
.play-end__reason {
  color: var(--text);
}
.play-end__stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
}
.play-end__ach {
  display: grid;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
}
.play-end__ach li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.play-end__ach .icon {
  color: var(--accent-text);
  flex: none;
}
.play-end__moments {
  display: grid;
  gap: var(--space-1);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
}
.play-end__h {
  margin: 0;
  font-size: var(--text-md);
}
.play-end__moments p {
  margin: 0;
}
.play-end__moments-list {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding-left: 1.25rem;
  font-size: var(--text-sm);
}
.play-end__tip {
  display: block;
  color: var(--text-muted);
}
.play-end__more {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
}
.play-end__promo {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-weight: var(--fw-semibold);
}

.play-settings {
  display: grid;
  gap: var(--space-4);
}
.play-settings__group {
  display: grid;
  gap: var(--space-2);
}
.play-settings__title {
  margin: 0;
  font-size: var(--text-md);
}
.play-switch {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1) var(--space-3);
  margin: 0;
}
.play-switch .help {
  flex-basis: 100%;
  margin: 0;
}
.play-switch__label {
  font-weight: var(--fw-medium);
}

.play-stats {
  display: grid;
  gap: var(--space-3);
}
.play-stats__summary {
  display: grid;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}
.play-stats__table {
  width: 100%;
  font-variant-numeric: tabular-nums;
}
.play-stats__table :is(td, th[scope='col']) {
  text-align: right;
}
.play-stats__h {
  margin: 0;
  font-size: var(--text-md);
}
.play-ach {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.play-ach__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-2);
  color: var(--text-subtle);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
}
.play-ach__item strong {
  display: block;
  color: var(--text-muted);
}
.play-ach__desc {
  display: block;
  font-size: var(--text-xs);
}
.play-ach__item.is-unlocked {
  color: var(--text);
  border-style: solid;
  border-color: var(--accent);
  background: var(--accent-soft);
}
.play-ach__item.is-unlocked strong {
  color: var(--text);
}
.play-ach__item.is-unlocked .icon {
  color: var(--accent-text);
}

.play-more {
  display: grid;
  gap: var(--space-1);
}
.play-more__item {
  justify-content: flex-start;
  gap: var(--space-3);
}
.play-more__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
}
.play-more__links a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 44px;
}

@keyframes play-pop {
  from {
    scale: 0.8;
    opacity: 0;
  }
}
@keyframes play-gain {
  from {
    opacity: 0;
    translate: 0 -20%;
  }
  15%,
  70% {
    opacity: 1;
    translate: 0 -50%;
  }
  to {
    opacity: 0;
    translate: 0 -110%;
  }
}
.play[data-anim='off'] :is(.play-card, .play-card__reaction.is-shown) {
  transition: none;
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  .play-card,
  .play-card__reaction.is-shown,
  .play-card__gain.is-shown {
    transition: none;
    animation: none;
  }
}

/* =========================================================================
   Offline page and touch diagnostics
   ========================================================================= */
.play-offline {
  display: grid;
  justify-items: start;
  gap: var(--space-4);
  padding-block: var(--space-8);
}
.play-offline h1 {
  margin: 0;
}
.play-offline .lead {
  margin: 0;
}
.play-offline__board {
  display: grid;
  grid-template-columns: repeat(3, 40px);
  width: max-content;
  padding: 8px;
  background: radial-gradient(120% 90% at 30% 10%, var(--board-2), var(--board) 70%);
  border: 8px solid var(--board-frame);
  border-radius: var(--radius-md);
}
.play-offline__board span {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  box-shadow: inset -1px -1px 0 var(--board-line);
}
.play-offline__board [data-c]::after {
  content: '';
  width: 76%;
  aspect-ratio: 1;
  border-radius: 50%;
}
.play-offline__board [data-c='b']::after {
  background: radial-gradient(circle at 34% 28%, var(--disc-black-hi), var(--disc-black) 65%);
}
.play-offline__board [data-c='w']::after {
  background: radial-gradient(circle at 34% 28%, var(--disc-white-hi), var(--disc-white));
}

.play-debug {
  position: fixed;
  left: 8px;
  bottom: calc(72px + env(safe-area-inset-bottom));
  z-index: 90;
  max-width: min(320px, 90vw);
  padding: 6px 8px;
  font:
    11px/1.35 ui-monospace,
    SFMono-Regular,
    Menlo,
    Consolas,
    monospace;
  color: #fff;
  background: rgba(0, 0, 0, 0.78);
  border-radius: 6px;
  pointer-events: none;
}
.play-debug__log {
  margin: 4px 0 0;
  padding-left: 0;
  list-style: none;
}
