/* ============ Checkers Arena ============ */

:root {
  --bg-1: #1a1410;
  --bg-2: #2b201a;
  --gold: #d4a24e;
  --gold-soft: #e8c98a;
  --sq-light: #e9d5ae;
  --sq-light-2: #dfc697;
  --sq-dark: #7c4a24;
  --sq-dark-2: #6b3d1c;
  --frame-1: #4a2c15;
  --frame-2: #2e1a0b;
  --red-1: #e05545;
  --red-2: #a52a1c;
  --red-3: #7c1a10;
  --dark-1: #4a4a52;
  --dark-2: #26262e;
  --dark-3: #131317;
  --ink: #f3ead9;
  --ink-dim: #b7a98c;
  --card: rgba(255, 255, 255, 0.045);
  --card-border: rgba(255, 255, 255, 0.09);
}

* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* fast taps everywhere: no double-tap-zoom hold-off on the board or the
   buttons (rapid capture chains felt laggy / zoomed on phones) */
button, input, .sq, .piece, .hint { touch-action: manipulation; }

/* generic hide utility — many elements are toggled with .hidden, and
   without this rule a plain element (e.g. the New Game button) stays
   visible online despite classList.add("hidden") */
.hidden { display: none !important; }

/* Same trap, other spelling. The browser hides [hidden] with a UA rule of the
   lowest possible weight, so ANY author `display:` beats it — the solver's
   piece palette is `display:flex` and stayed on screen in play mode with
   el.hidden = true set and no error anywhere. */
[hidden] { display: none !important; }

body {
  min-height: 100vh;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  color: var(--ink);
  background:
    radial-gradient(1200px 700px at 20% -10%, #3a2a1e 0%, transparent 60%),
    radial-gradient(1000px 600px at 110% 110%, #33241b 0%, transparent 55%),
    linear-gradient(160deg, var(--bg-2), var(--bg-1) 70%);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 22px 14px 40px;
}

/* ---------- Header ---------- */

.site-header { text-align: center; margin-bottom: 12px; }

.logo {
  font-size: clamp(1.2rem, 2.5vw, 1.6rem);
  font-weight: 800;
  letter-spacing: 0.5px;
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.logo-accent { color: var(--gold); }
/* the logo doubles as a "home" link on content pages */
.logo-link { color: var(--ink); text-decoration: none; }
.logo-link:hover { text-decoration: none; }
.logo-link .logo-accent { color: var(--gold); }

.logo-disc {
  width: 0.85em; height: 0.85em;
  border-radius: 50%;
  display: inline-block;
  box-shadow: inset 0 -3px 5px rgba(0,0,0,.45), inset 0 2px 3px rgba(255,255,255,.35), 0 3px 6px rgba(0,0,0,.5);
}
.logo-disc-red  { background: radial-gradient(circle at 35% 30%, var(--red-1), var(--red-2) 60%, var(--red-3)); }
.logo-disc-dark { background: radial-gradient(circle at 35% 30%, var(--dark-1), var(--dark-2) 60%, var(--dark-3)); }

.tagline {
  color: var(--ink-dim);
  font-size: 0.82rem;
  margin-top: 12px;
  font-style: italic;
  opacity: 0.85;
  text-align: center;
}

/* sound on/off — floats top-right so it costs no sidebar height */
.sound-toggle {
  position: absolute;
  top: 12px; right: 14px;
  width: 38px; height: 38px;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.18);
  background: rgba(255,255,255,.05);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease, opacity .15s ease;
}
.sound-toggle:hover { border-color: rgba(212,162,78,.6); background: rgba(212,162,78,.12); }
.sound-toggle.muted { opacity: .55; }

/* ---------- Layout ---------- */

.arena {
  display: flex;
  gap: 24px;
  align-items: stretch;      /* board & sidebar share the row height */
  justify-content: center;
  flex-wrap: wrap;
  width: 100%;
  max-width: 1340px; /* room for board + panel + affiliate column on wide screens */
}

/* ---------- Board ---------- */

.board-wrap { flex: 0 1 auto; display: flex; flex-direction: column; align-items: center; }

/* variant selector — a segmented control sitting above the board. Emerald
   active pill so it reads as the top-level "which game" choice, distinct from
   the gold action buttons and the teal take-back button. */
.variant-tabs {
  display: flex;
  flex-wrap: wrap;               /* four variants: wrap to 2×2 on narrow screens */
  gap: 4px;
  width: min(100%, 520px);
  margin: 0 0 8px;
  padding: 3px;
  background: rgba(0, 0, 0, .28);
  border: 1px solid rgba(255, 255, 255, .08);
  border-radius: 12px;
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, .4);
}
.variant-tabs.hidden { display: none; }
.variant-tab {
  flex: 1 1 calc(50% - 4px);     /* two per row by default (phones) */
  font: inherit;
  font-size: .8rem;
  font-weight: 700;
  color: var(--ink-dim);
  padding: 7px 6px;
  border: none;
  border-radius: 9px;
  background: transparent;
  cursor: pointer;
  transition: all .15s ease;
  white-space: nowrap;
}
.variant-tab:hover { color: var(--ink); }
.variant-tab.active {
  color: #eafff4;
  background: linear-gradient(160deg, #35c47c, #1f9d5b 58%, #12793f);
  box-shadow: 0 3px 12px rgba(31, 157, 91, .38);
}
@media (min-width: 620px) {
  .variant-tab { flex: 1 1 0; } /* all four variants in one row on wider screens */
}

.board-frame {
  position: relative;
  /* bind to the available viewport height (minus header/margins AND the
     variant tab bar above the board) so it never pushes the sidebar's online
     buttons below the fold */
  width: min(90vw, calc(100vh - 168px), 700px);
  /* dynamic viewport unit where supported: classic 100vh includes the
     mobile browser's retracted address bar and oversizes the board */
  width: min(90vw, calc(100dvh - 168px), 700px);
  aspect-ratio: 1;
  align-self: center;        /* centered under the variant tabs in the column */
  padding: clamp(12px, 2.4vw, 22px);
  border-radius: 14px;
  background:
    linear-gradient(135deg, rgba(255,255,255,.10), rgba(0,0,0,.25)),
    repeating-linear-gradient(100deg, var(--frame-1) 0 14px, #57351b 14px 22px, var(--frame-1) 22px 40px),
    var(--frame-1);
  box-shadow:
    0 24px 60px rgba(0,0,0,.6),
    0 6px 18px rgba(0,0,0,.4),
    inset 0 2px 3px rgba(255,255,255,.15),
    inset 0 -3px 6px rgba(0,0,0,.5);
}

.board, .pieces, .hints {
  position: absolute;
  inset: clamp(12px, 2.4vw, 22px);
  border-radius: 4px;
}

.board {
  display: grid;
  grid-template: repeat(8, 1fr) / repeat(8, 1fr);
  overflow: hidden;
  box-shadow: inset 0 0 0 2px rgba(0,0,0,.55), inset 0 4px 14px rgba(0,0,0,.35);
}

.sq { position: relative; }

/* keyboard focus ring — inset so it hugs the square edge around the disc,
   painted above the piece layer via a high z-index on the focused cell */
.sq:focus { outline: none; }
.sq:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 3px #f5e6b8, inset 0 0 12px 2px rgba(245, 230, 184, .55);
  z-index: 4;
}

/* take-back button (vs computer) — distinct teal so it clearly stands apart
   from the gold primary/small buttons and the red danger button.
   .btn-small.undo-btn (2 classes) outranks .btn-small's gold; .hidden stays
   !important so the mode-based show/hide keeps working. */
.undo-btn {
  display: block;
  width: 100%;
  margin-top: 8px;
}
.btn-small.undo-btn {
  color: #eafcf9;
  background: linear-gradient(160deg, #34a89d, #1d6f66 58%, #114b45);
  border: 1px solid rgba(120, 220, 208, .45);
  box-shadow: 0 2px 10px rgba(20, 120, 110, .28);
}
.btn-small.undo-btn:hover { filter: brightness(1.12); }
.undo-btn:disabled {
  opacity: .4;
  cursor: not-allowed;
  box-shadow: none;
}

/* visually hidden, but available to screen readers */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}

.sq.light {
  background:
    radial-gradient(120% 90% at 30% 20%, rgba(255,255,255,.28), transparent 55%),
    linear-gradient(145deg, var(--sq-light), var(--sq-light-2));
}

.sq.dark {
  background:
    radial-gradient(120% 90% at 70% 80%, rgba(0,0,0,.22), transparent 55%),
    linear-gradient(145deg, var(--sq-dark), var(--sq-dark-2));
}

/* last move tint */
.sq.last-from::after, .sq.last-to::after {
  content: "";
  position: absolute; inset: 0;
  background: rgba(212, 162, 78, 0.28);
}

/* take-back flash — the squares that rolled back pulse once. On a phone the
   board is often scrolled out of view when Undo is pressed, so the sound says
   "something happened" and this says "and here is what". Declared after the
   last-move tint so it wins at equal specificity. */
.sq.undone::after {
  content: "";
  position: absolute; inset: 0;
  background: rgba(212, 162, 78, 0.6);
  animation: undoFlash 0.9s ease-out forwards;
}
@keyframes undoFlash {
  0%   { opacity: 0; }
  12%  { opacity: 1; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .sq.undone::after { animation: none; opacity: 0.6; }
}

/* coordinates */
.sq[data-file]::before {
  content: attr(data-file);
  position: absolute; right: 4px; bottom: 2px;
  font-size: clamp(8px, 1.3vw, 11px);
  font-weight: 700;
  color: rgba(0,0,0,.35);
}
.sq.dark[data-file]::before { color: rgba(255,255,255,.3); }

.sq[data-rank] .rank-label {
  position: absolute; left: 4px; top: 2px;
  font-size: clamp(8px, 1.3vw, 11px);
  font-weight: 700;
  color: rgba(0,0,0,.35);
}
.sq.dark .rank-label { color: rgba(255,255,255,.3); }

/* ---------- Hints layer ---------- */

.hints { pointer-events: none; }

.hint {
  position: absolute;
  width: 12.5%; height: 12.5%;
  display: flex; align-items: center; justify-content: center;
}

.hint::after {
  content: "";
  width: 30%; height: 30%;
  border-radius: 50%;
  background: rgba(240, 225, 180, 0.85);
  box-shadow: 0 0 12px rgba(240, 225, 180, 0.65), 0 2px 4px rgba(0,0,0,.4);
  animation: pulse 1.4s ease-in-out infinite;
}

.hint.capture::after {
  width: 74%; height: 74%;
  background: transparent;
  border: 3px solid #ff8a5c;
  box-shadow: 0 0 14px rgba(255, 120, 70, 0.8), inset 0 0 10px rgba(255, 120, 70, 0.4);
}

@keyframes pulse {
  0%, 100% { transform: scale(1); opacity: 0.9; }
  50% { transform: scale(1.12); opacity: 1; }
}

/* ---------- Pieces ---------- */

.pieces { pointer-events: none; }

.piece {
  position: absolute;
  width: 12.5%; height: 12.5%;
  display: flex; align-items: center; justify-content: center;
  pointer-events: auto;
  cursor: pointer;
  transition: top 0.34s cubic-bezier(.4, 1.4, .4, 1), left 0.34s cubic-bezier(.4, 1.4, .4, 1);
  z-index: 2;
}

.piece .disc {
  position: relative;
  width: 78%; height: 78%;
  border-radius: 50%;
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}

.piece.red .disc {
  background: radial-gradient(circle at 35% 28%, var(--red-1) 0%, var(--red-2) 55%, var(--red-3) 100%);
  box-shadow:
    inset 0 5px 8px rgba(255,255,255,.32),
    inset 0 -6px 10px rgba(0,0,0,.5),
    0 5px 10px rgba(0,0,0,.55),
    0 1px 2px rgba(0,0,0,.6);
}

.piece.black .disc {
  background: radial-gradient(circle at 35% 28%, var(--dark-1) 0%, var(--dark-2) 55%, var(--dark-3) 100%);
  box-shadow:
    inset 0 5px 8px rgba(255,255,255,.18),
    inset 0 -6px 10px rgba(0,0,0,.65),
    0 5px 10px rgba(0,0,0,.55),
    0 1px 2px rgba(0,0,0,.6);
}

/* grooved ring on every piece */
.piece .disc::before {
  content: "";
  position: absolute; inset: 16%;
  border-radius: 50%;
  border: 2px solid rgba(0,0,0,.28);
  box-shadow: inset 0 1px 2px rgba(255,255,255,.18);
}
.piece.red .disc::before { border-color: rgba(90, 12, 5, .55); }

.piece:hover .disc { transform: scale(1.07); }

.piece.selected .disc {
  transform: scale(1.1);
  box-shadow:
    0 0 0 3px var(--gold),
    0 0 18px rgba(212, 162, 78, .85),
    inset 0 5px 8px rgba(255,255,255,.3),
    inset 0 -6px 10px rgba(0,0,0,.5);
}

/* pieces that MUST capture */
.piece.must .disc { animation: mustGlow 1.1s ease-in-out infinite; }

@keyframes mustGlow {
  0%, 100% { box-shadow: 0 0 0 2px rgba(255,138,92,.0), 0 5px 10px rgba(0,0,0,.55), inset 0 5px 8px rgba(255,255,255,.25), inset 0 -6px 10px rgba(0,0,0,.5); }
  50% { box-shadow: 0 0 0 3px rgba(255,138,92,.9), 0 0 16px rgba(255,120,70,.7), inset 0 5px 8px rgba(255,255,255,.25), inset 0 -6px 10px rgba(0,0,0,.5); }
}

/* king crown */
.piece.king .disc::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M24 62 L20 36 L34 47 L50 27 L66 47 L80 36 L76 62 Z' fill='%23f2c14e' stroke='%23a87b1e' stroke-width='3' stroke-linejoin='round'/%3E%3Crect x='24' y='64' width='52' height='9' rx='3' fill='%23f2c14e' stroke='%23a87b1e' stroke-width='3'/%3E%3Ccircle cx='20' cy='33' r='4' fill='%23f2c14e' stroke='%23a87b1e' stroke-width='2'/%3E%3Ccircle cx='50' cy='24' r='4' fill='%23f2c14e' stroke='%23a87b1e' stroke-width='2'/%3E%3Ccircle cx='80' cy='33' r='4' fill='%23f2c14e' stroke='%23a87b1e' stroke-width='2'/%3E%3C/svg%3E");
  background-size: 62% 62%;
  background-position: center 46%;
  background-repeat: no-repeat;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,.5));
}

.piece.crowned .disc { animation: crown 0.6s ease; }

@keyframes crown {
  0% { transform: scale(1); }
  40% { transform: scale(1.35) rotate(8deg); }
  100% { transform: scale(1); }
}

.piece.dying {
  z-index: 1;
  transition: none;
}
.piece.dying .disc { animation: die 0.4s ease forwards; }

@keyframes die {
  to { transform: scale(0.1) rotate(140deg); opacity: 0; }
}

/* ---------- Side panel ---------- */

.panel {
  display: flex;
  flex-direction: column;
  /* pack the cards from the top with a fixed gap — space-between used to
     stretch the gaps to whatever the tallest row neighbour was (the
     affiliate column), blowing the sidebar far past the board */
  justify-content: flex-start;
  gap: 8px;
  width: min(340px, 92vw);
}

.card {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: 14px;
  padding: 10px 14px;
  backdrop-filter: blur(6px);
  box-shadow: 0 8px 24px rgba(0,0,0,.35);
}

.turn-card {
  display: flex; align-items: center; gap: 12px;
  transition: box-shadow .3s ease, border-color .3s ease;
}

.turn-card.red-turn  { border-color: rgba(224, 85, 69, .5); box-shadow: 0 0 18px rgba(224, 85, 69, .25), 0 8px 24px rgba(0,0,0,.35); }
.turn-card.black-turn { border-color: rgba(200, 200, 215, .35); box-shadow: 0 0 18px rgba(160, 160, 180, .2), 0 8px 24px rgba(0,0,0,.35); }

.turn-disc {
  width: 34px; height: 34px;
  border-radius: 50%;
  box-shadow: inset 0 4px 6px rgba(255,255,255,.3), inset 0 -5px 8px rgba(0,0,0,.5), 0 4px 8px rgba(0,0,0,.5);
  transition: background .3s ease;
}
.turn-disc.red  { background: radial-gradient(circle at 35% 28%, var(--red-1), var(--red-2) 60%, var(--red-3)); }
.turn-disc.black { background: radial-gradient(circle at 35% 28%, var(--dark-1), var(--dark-2) 60%, var(--dark-3)); }

.turn-label { display: block; font-size: .7rem; text-transform: uppercase; letter-spacing: 1.5px; color: var(--ink-dim); }
.turn-player { font-size: 1.15rem; font-weight: 700; }

.turn-timer {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-size: 1.15rem;
  font-weight: 800;
  color: var(--gold-soft);
  background: rgba(0,0,0,.3);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 10px;
  padding: 4px 10px;
}
.turn-timer.hidden { display: none; }
.turn-timer.warn {
  color: #ff8a70;
  border-color: rgba(255,110,90,.55);
  animation: timerBlink 1s steps(2) infinite;
}
@keyframes timerBlink { 50% { background: rgba(255, 80, 60, .22); } }

.online-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.online-actions .hidden { display: none; }

/* ---------- Match (duel) card — who you're playing, online ---------- */

.match-card { display: flex; flex-direction: column; gap: 4px; }
.match-card.hidden { display: none; }

.match-side {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  border-radius: 10px;
  border: 1px solid transparent;
  transition: border-color .2s ease, background .2s ease, box-shadow .2s ease;
}
.match-side.active {
  border-color: rgba(212, 162, 78, .55);
  background: rgba(212, 162, 78, .12);
  box-shadow: 0 0 14px rgba(212, 162, 78, .22);
}

.match-disc {
  width: 26px; height: 26px;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: inset 0 3px 5px rgba(255,255,255,.3), inset 0 -4px 6px rgba(0,0,0,.5), 0 2px 5px rgba(0,0,0,.5);
}
.match-disc.red  { background: radial-gradient(circle at 35% 28%, var(--red-1), var(--red-2) 60%, var(--red-3)); }
.match-disc.black { background: radial-gradient(circle at 35% 28%, var(--dark-1), var(--dark-2) 60%, var(--dark-3)); }

.match-who { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.match-name { font-weight: 700; font-size: .95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.match-elo { font-size: .74rem; color: var(--ink-dim); }

.match-badge {
  font-size: .6rem;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--gold-soft);
  border: 1px solid rgba(212,162,78,.4);
  border-radius: 6px;
  padding: 1px 5px;
  flex-shrink: 0;
}

.match-clock {
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-size: 1.15rem;
  color: var(--gold-soft);
  flex-shrink: 0;
}
.match-clock.hidden { display: none; }
.match-clock.warn { color: #ff8a70; animation: timerBlink 1s steps(2) infinite; }

.match-vs {
  text-align: center;
  font-size: .68rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.draw-offer {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(212, 162, 78, .12);
  border: 1px solid rgba(212, 162, 78, .4);
  font-size: .9rem;
  color: var(--gold-soft);
}
.draw-offer.hidden { display: none; }
.draw-offer-btns { display: flex; gap: 8px; }
.draw-offer-btns .btn-small { flex: 1; }

/* the empty-lobby fallback: a quiet offer to play the computer while the
   search keeps running, and the loud banner for when a human does turn up */
.wait-bot {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(212, 162, 78, .07);
  border: 1px dashed rgba(212, 162, 78, .35);
  font-size: .84rem;
  line-height: 1.4;
  color: var(--ink-dim);
}
.wait-bot.hidden { display: none; }

.match-found {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(120, 205, 130, .14);
  border: 1px solid rgba(120, 205, 130, .5);
  font-size: .9rem;
  font-weight: 600;
  color: #b9e9be;
}
.match-found.hidden { display: none; }

.message-card { min-height: 44px; display: flex; align-items: center; }
.message-card p { font-size: .92rem; }
#message { font-size: .95rem; line-height: 1.4; color: var(--gold-soft); }
#message.alert { color: #ff9d78; font-weight: 600; }

.captures-card { display: flex; flex-direction: column; gap: 6px; }
.capture-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.capture-label { font-size: .75rem; color: var(--ink-dim); white-space: nowrap; }

.capture-tray { display: flex; flex-wrap: wrap; gap: 3px; min-height: 16px; justify-content: flex-end; }

.mini-disc {
  width: 15px; height: 15px;
  border-radius: 50%;
  box-shadow: inset 0 2px 3px rgba(255,255,255,.25), inset 0 -2px 3px rgba(0,0,0,.5), 0 2px 3px rgba(0,0,0,.4);
  animation: popIn .35s cubic-bezier(.3, 1.6, .5, 1);
}
.mini-disc.red  { background: radial-gradient(circle at 35% 30%, var(--red-1), var(--red-2) 60%, var(--red-3)); }
.mini-disc.black { background: radial-gradient(circle at 35% 30%, var(--dark-1), var(--dark-2) 60%, var(--dark-3)); }

@keyframes popIn {
  from { transform: scale(0); }
  to { transform: scale(1); }
}

/* ---------- Buttons ---------- */

.btn {
  font: inherit;
  font-weight: 700;
  font-size: .95rem;
  padding: 10px 18px;
  border: none;
  border-radius: 12px;
  cursor: pointer;
  transition: transform .12s ease, box-shadow .2s ease, filter .2s ease;
}

.btn-primary {
  color: #2b1a06;
  background: linear-gradient(160deg, var(--gold-soft), var(--gold) 60%, #b07f30);
  box-shadow: 0 6px 16px rgba(212, 162, 78, .3), inset 0 1px 2px rgba(255,255,255,.5);
}

.btn-primary:hover { filter: brightness(1.08); transform: translateY(-1px); }
.btn-primary:active { transform: translateY(1px); }

/* ---------- Modes ---------- */

.modes-card { display: flex; flex-direction: column; gap: 6px; }
.modes-label { font-size: .68rem; text-transform: uppercase; letter-spacing: 1.5px; color: var(--ink-dim); }

.mode-btns { display: flex; gap: 8px; }

.mode-btn, .diff-btn {
  flex: 1;
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  color: var(--ink-dim);
  padding: 7px 6px;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,.14);
  background: rgba(255,255,255,.04);
  cursor: pointer;
  transition: all .15s ease;
  white-space: nowrap;
}

.mode-btn:hover, .diff-btn:hover { border-color: rgba(212,162,78,.5); color: var(--ink); }

.mode-btn.active, .diff-btn.active {
  color: #2b1a06;
  background: linear-gradient(160deg, var(--gold-soft), var(--gold));
  border-color: transparent;
  box-shadow: 0 3px 10px rgba(212,162,78,.3);
}

.diff-row { display: flex; flex-direction: column; gap: 4px; margin-top: 0; }
.diff-row.hidden { display: none; }

/* online is American-only for now: greyed-out buttons + a note */
.mode-btn:disabled { opacity: .38; cursor: not-allowed; filter: grayscale(.5); }
.mode-btn:disabled:hover { border-color: rgba(255,255,255,.14); color: var(--ink-dim); }
.online-note { font-size: .72rem; color: var(--ink-dim); font-style: italic; line-height: 1.35; margin-top: 2px; }
.online-note.hidden { display: none; }

.online-sep { height: 1px; background: rgba(255,255,255,.1); margin: 2px 0; }

/* ---------- Install-as-an-app card ----------
   Only ever rendered on a browser that can really install the site, so it is
   styled to sit quietly UNDER the primary action rather than compete with it:
   the same card frame as everything else in the sidebar, no accent border. */

.install-card { display: flex; flex-direction: column; gap: 8px; }
.install-card.hidden { display: none; }
.install-note { font-size: .78rem; color: var(--ink-dim); line-height: 1.4; }

/* ---------- Online card ---------- */

.online-card { display: flex; flex-direction: column; gap: 10px; border-color: rgba(212,162,78,.35); }
.online-card.hidden { display: none; }
#onlineStatus { font-size: .92rem; color: var(--gold-soft); line-height: 1.4; }

.invite-row { display: flex; gap: 8px; }
.invite-row.hidden { display: none; }

#inviteLink {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: .8rem;
  color: var(--ink);
  background: rgba(0,0,0,.35);
  border: 1px solid rgba(255,255,255,.15);
  border-radius: 8px;
  padding: 8px 10px;
}

.btn-small {
  font: inherit;
  font-size: .82rem;
  font-weight: 700;
  padding: 8px 12px;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  color: #2b1a06;
  background: linear-gradient(160deg, var(--gold-soft), var(--gold));
  transition: filter .15s ease;
}
.btn-small:hover { filter: brightness(1.1); }

.btn-small.danger {
  color: var(--ink);
  background: rgba(255, 90, 70, .16);
  border: 1px solid rgba(255, 110, 90, .4);
}
.btn-small.danger:hover { background: rgba(255, 90, 70, .28); }

.overlay-btns { display: flex; gap: 10px; justify-content: center; }
.overlay-btns .hidden { display: none; }

.btn-ghost {
  color: var(--ink-dim);
  background: transparent;
  border: 1px solid rgba(255,255,255,.25);
}
.btn-ghost:hover { color: var(--ink); border-color: rgba(255,255,255,.5); }

/* pulsing dot while searching for an opponent */
.searching::before {
  content: "";
  display: inline-block;
  width: 9px; height: 9px;
  margin-right: 8px;
  border-radius: 50%;
  background: var(--gold);
  animation: pulse 1.2s ease-in-out infinite;
}

/* thinking dots */
.thinking-dots::after {
  content: "";
  animation: dots 1.2s steps(4) infinite;
}
@keyframes dots {
  0% { content: ""; }
  25% { content: "."; }
  50% { content: ".."; }
  75% { content: "..."; }
}

.mode-chip {
  padding: 9px 12px;
  border-radius: 10px;
  background: rgba(255,255,255,.04);
  border: 1px dashed rgba(255,255,255,.14);
  color: var(--ink-dim);
  font-size: .88rem;
}

/* ---------- Winner overlay ---------- */

.overlay {
  position: fixed; inset: 0;
  background: rgba(10, 6, 3, .78);
  backdrop-filter: blur(5px);
  display: flex; align-items: center; justify-content: center;
  z-index: 50;
  animation: fadeIn .35s ease;
}

.overlay.hidden { display: none; }

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.overlay-card {
  text-align: center;
  background: linear-gradient(165deg, #382718, #241709);
  border: 1px solid rgba(212, 162, 78, .4);
  border-radius: 20px;
  padding: 40px 56px;
  box-shadow: 0 30px 80px rgba(0,0,0,.7), 0 0 40px rgba(212,162,78,.15);
  animation: cardUp .45s cubic-bezier(.3, 1.4, .5, 1);
}

@keyframes cardUp {
  from { transform: translateY(30px) scale(.92); opacity: 0; }
  to { transform: none; opacity: 1; }
}

.overlay-trophy { font-size: 3.2rem; margin-bottom: 8px; }
.overlay-card h2 { font-size: 2rem; margin-bottom: 6px; color: var(--gold-soft); }
.overlay-card p { color: var(--ink-dim); margin-bottom: 22px; }

/* ---------- Player card ---------- */

.player-card { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

.player-info { display: flex; flex-direction: column; min-width: 0; }
.player-name {
  font-weight: 700;
  font-size: .98rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.player-elo { color: var(--gold-soft); font-weight: 800; font-size: .98rem; }
.player-sub { color: var(--ink-dim); font-size: .72rem; margin-top: 2px; }

/* ---- country flags ----
 * Round to echo the playing pieces, and always the same size whatever the
 * flag's own aspect ratio is, so a row of names never goes ragged. */
.flag {
  width: 18px; height: 18px;
  border-radius: 50%;
  flex-shrink: 0;
  object-fit: cover;
  box-shadow: 0 0 0 1px rgba(0,0,0,.45), 0 1px 3px rgba(0,0,0,.4);
  vertical-align: middle;
}
/* ---- strength badges ----
 * Round like the flags, but they sit right BEFORE the Elo number they
 * qualify — never in the flag position, or a row would read as two flags. */
.rank-badge {
  width: 15px; height: 15px;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: 0 1px 3px rgba(0,0,0,.4);
  vertical-align: -2px;
}
.lobby-row .rank-badge { margin-right: 1px; }
.lb-table td .rank-badge { margin-right: 5px; }
/* player card / duel card: name and Elo stack in a COLUMN, so a bare badge
 * img would become its own row under the name — this keeps badge + number
 * side by side as one line of that column */
.elo-line { display: flex; align-items: center; gap: 4px; min-width: 0; }

.flag-btn {
  background: none; border: 0; padding: 2px; cursor: pointer;
  border-radius: 50%; flex-shrink: 0; line-height: 0;
}
.flag-btn .flag { width: 22px; height: 22px; }
.flag-btn:hover .flag, .flag-btn:focus-visible .flag {
  box-shadow: 0 0 0 2px var(--gold-soft), 0 1px 3px rgba(0,0,0,.4);
}
.lb-table td .flag { margin-right: 7px; }
.lobby-row .flag { margin-right: 2px; }
/* the variant of an open challenge — only shown when it is not American, so it
   reads as "watch out, different game", not as decoration on every row */
.lobby-variant {
  font-size: .72rem;
  font-weight: 700;
  color: var(--gold-soft);
  background: rgba(212,162,78,.16);
  border: 1px solid rgba(212,162,78,.35);
  border-radius: 20px;
  padding: 2px 9px;
  white-space: nowrap;
  flex-shrink: 0;
}
.flag-field { display: flex; flex-direction: column; gap: 6px; margin: 14px 0 4px; }
.flag-field span { font-size: .8rem; color: var(--ink-dim); }
.flag-field select {
  padding: 9px 10px; border-radius: 8px; font-size: .92rem;
  background: rgba(0,0,0,.28); color: var(--ink);
  border: 1px solid rgba(255,255,255,.18);
}
.flag-actions { display: flex; justify-content: flex-end; margin-top: 12px; }

.player-actions { display: flex; flex-direction: column; gap: 4px; flex-shrink: 0; }
/* the stacked account/friends/top-25 buttons are the tallest part of the
 * card — keep them slim so the sidebar fits next to the board unscrolled */
.player-actions .btn-small { padding: 5px 10px; font-size: .76rem; }

.btn-small.ghosted {
  color: var(--ink-dim);
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.18);
}
.btn-small.ghosted:hover { color: var(--ink); border-color: rgba(212,162,78,.6); }

.overlay-rating {
  color: var(--gold-soft);
  font-weight: 700;
  font-size: 1.05rem;
  margin: -12px 0 20px;
}
.overlay-rating.hidden { display: none; }

/* ---------- Modals ---------- */

.modal {
  position: fixed; inset: 0;
  background: rgba(10, 6, 3, .72);
  backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  z-index: 60;
  padding: 16px;
  animation: fadeIn .25s ease;
}
.modal.hidden { display: none; }

.modal-card {
  position: relative;
  width: min(400px, 94vw);
  background: linear-gradient(165deg, #382718, #241709);
  border: 1px solid rgba(212, 162, 78, .35);
  border-radius: 18px;
  padding: 26px;
  box-shadow: 0 30px 80px rgba(0,0,0,.7);
  animation: cardUp .35s cubic-bezier(.3, 1.4, .5, 1);
}
.modal-card.modal-wide { width: min(560px, 94vw); max-height: 88vh; overflow-y: auto; }
.modal-card h3 { color: var(--gold-soft); margin-bottom: 6px; }

.modal-close {
  position: absolute; top: 12px; right: 12px;
  background: none; border: none;
  color: var(--ink-dim); font-size: 1.1rem;
  cursor: pointer;
}
.modal-close:hover { color: var(--ink); }

.modal-tabs { display: flex; gap: 8px; margin-bottom: 12px; }
.modal-tab {
  flex: 1; font: inherit; font-weight: 600; font-size: .9rem;
  padding: 9px; border-radius: 10px; cursor: pointer;
  color: var(--ink-dim);
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.15);
}
.modal-tab.active {
  color: #2b1a06;
  background: linear-gradient(160deg, var(--gold-soft), var(--gold));
  border-color: transparent;
}

.modal-hint { color: var(--ink-dim); font-size: .85rem; line-height: 1.5; margin-bottom: 14px; }
.modal-hint strong { color: #ff9d78; }

/* ---------- Discord button (sidebar + every footer) ---------- */

.discord-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 9px 16px; border-radius: 10px;
  font-weight: 700; font-size: .9rem; text-decoration: none;
  color: #fff; background: #5865f2;   /* Discord blurple — their brand, not ours */
  transition: filter .15s ease;
}
.discord-btn:hover { filter: brightness(1.08); text-decoration: none; }
.discord-btn svg { width: 20px; height: 20px; flex-shrink: 0; }
.footer-discord { margin: 10px 0 4px; }
/* full-width variant inside the game panel's "Play Online" section */
.discord-panel { width: 100%; margin-top: 10px; }

/* ---------- join nudge (guests, once a day, on arrival + after a game) ---------- */

.join-nudge-card {
  width: min(380px, 94vw);
  text-align: center;
  border-color: rgba(212, 162, 78, .55);
  box-shadow: 0 0 40px rgba(212, 162, 78, .18), 0 24px 60px rgba(0,0,0,.6);
}
.join-nudge-crown {
  font-size: 2.6rem;
  line-height: 1;
  margin-bottom: 10px;
  filter: drop-shadow(0 4px 10px rgba(212, 162, 78, .45));
}
.join-nudge-card h3 { font-size: 1.35rem; margin-bottom: 8px; }
.join-nudge-lead { color: var(--ink-dim); font-size: .92rem; line-height: 1.55; margin-bottom: 16px; }
/* the concrete promise, with the number the guest just earned in it */
.join-nudge-keep {
  color: var(--gold-soft);
  font-weight: 700;
  font-size: .95rem;
  background: rgba(212,162,78,.12);
  border: 1px solid rgba(212,162,78,.35);
  border-radius: 10px;
  padding: 9px 12px;
  margin-bottom: 16px;
}
.join-nudge-perks {
  list-style: none;
  text-align: left;
  margin: 0 auto 18px;
  display: flex; flex-direction: column; gap: 9px;
  width: fit-content;
}
.join-nudge-perks li { display: flex; align-items: center; gap: 10px; font-size: .92rem; }
.join-nudge-perks li span { font-size: 1.05rem; }
.join-nudge-cta { width: 100%; }
.join-nudge-later { width: 100%; margin-top: 8px; font-size: .85rem; }

@media (prefers-reduced-motion: reduce) {
  .join-nudge-card { animation: none; }
}

#accountForm { display: flex; flex-direction: column; gap: 10px; }
#accountForm input {
  font: inherit;
  color: var(--ink);
  background: rgba(0,0,0,.35);
  border: 1px solid rgba(255,255,255,.18);
  border-radius: 10px;
  padding: 11px 12px;
}
#accountForm input:focus { outline: none; border-color: var(--gold); }

.modal-error { color: #ff9d78; font-size: .85rem; }
.modal-error.hidden { display: none; }
.modal-submit { margin-top: 4px; }

/* ---------- Lobby / open challenges ---------- */

.lobby-list { display: flex; flex-direction: column; gap: 8px; min-height: 60px; }

.lobby-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.08);
}
.lobby-row .lobby-name { font-weight: 700; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lobby-row .lobby-elo { color: var(--gold-soft); font-weight: 700; font-size: .9rem; }
.lobby-empty { color: var(--ink-dim); padding: 18px 4px; text-align: center; }

/* a standing challenge: its owner has closed the page and gets notified */
.lobby-row .lobby-tag {
  display: block;
  font-weight: 500;
  font-size: .72rem;
  color: var(--ink-dim);
  letter-spacing: .2px;
}

/* Above the list, so it survives a busy room — see the note in index.html.
   The margin below it is now doing real work: it separates the action from
   the roll-call rather than trailing off the bottom of the card. */
.standing-box {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 14px;
  padding: 12px 14px;
  border-radius: 10px;
  background: rgba(212, 162, 78, .07);
  border: 1px dashed rgba(212, 162, 78, .35);
}
.standing-note { font-size: .82rem; line-height: 1.45; color: var(--ink-dim); margin: 0; }
.standing-box .btn { align-self: stretch; }
.standing-box .btn[disabled] { opacity: .55; cursor: default; }

/* ---------- Friends ---------- */

.friends-sub {
  margin: 16px 0 8px;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  color: var(--ink-dim);
}
#friendsModal .friends-sub:first-of-type { margin-top: 8px; }

/* green/gray presence dot in front of a friend's name */
.friend-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
  background: rgba(255,255,255,.22);
}
.friend-dot.online {
  background: #4cd964;
  box-shadow: 0 0 8px rgba(76, 217, 100, .7);
}

.friend-search {
  width: 100%;
  font: inherit;
  color: var(--ink);
  background: rgba(0,0,0,.35);
  border: 1px solid rgba(255,255,255,.18);
  border-radius: 10px;
  padding: 11px 12px;
  margin-bottom: 10px;
}
.friend-search:focus { outline: none; border-color: var(--gold); }

/* unread-requests count on the Friends button */
#friendsBtn { position: relative; }
.friends-badge {
  position: absolute;
  top: -6px; right: -6px;
  min-width: 18px; height: 18px;
  padding: 0 4px;
  border-radius: 9px;
  background: #e74c3c;
  color: #fff;
  font-size: .68rem;
  font-weight: 800;
  line-height: 18px;
  text-align: center;
}
.friends-badge.hidden { display: none; }

/* small round add-friend button in the duel card */
.match-add {
  width: 24px; height: 24px;
  flex-shrink: 0;
  border: 1px solid rgba(212,162,78,.5);
  border-radius: 50%;
  background: rgba(212,162,78,.12);
  color: var(--gold-soft);
  font-size: .95rem;
  line-height: 1;
  cursor: pointer;
  transition: background .15s ease;
}
.match-add:hover { background: rgba(212,162,78,.28); }
.match-add:disabled { cursor: default; opacity: .8; }
.match-add.hidden { display: none; }

/* in-game chat (floating box bottom-right; the challenge toast sits above) */
.chat-box {
  position: fixed;
  right: 18px; bottom: 18px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  width: min(300px, calc(100vw - 36px));
  border-radius: 14px;
  background: linear-gradient(165deg, #382718, #241709);
  border: 1px solid rgba(212, 162, 78, .4);
  box-shadow: 0 16px 40px rgba(0,0,0,.6);
}
.chat-box.hidden { display: none; }
.chat-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 12px;
  font-weight: 700;
  border-bottom: 1px solid rgba(255,255,255,.1);
}
.chat-head .modal-close { position: static; }
.chat-head-btns { display: flex; align-items: center; gap: 2px; }
.chat-mute {
  border: none; background: transparent; cursor: pointer;
  font-size: .95rem; line-height: 1; padding: 4px;
  opacity: .85; transition: opacity .15s ease;
}
.chat-mute:hover { opacity: 1; }
.chat-mute.muted { opacity: .45; }

/* unread count on the duel-card chat button */
#matchChatBtn { position: relative; }
.chat-badge {
  position: absolute;
  top: -5px; right: -5px;
  min-width: 15px; height: 15px;
  padding: 0 3px;
  border-radius: 8px;
  background: #e74c3c;
  color: #fff;
  font-size: .6rem;
  font-weight: 800;
  line-height: 15px;
  text-align: center;
}
.chat-badge.hidden { display: none; }
.chat-msgs {
  height: 180px;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 4px;
  padding: 10px 12px;
  font-size: .88rem;
}
.chat-line { max-width: 85%; padding: 4px 9px; border-radius: 9px; line-height: 1.4; overflow-wrap: break-word; }
.chat-line.mine { align-self: flex-end; background: rgba(212,162,78,.18); color: var(--ink); }
.chat-line.theirs { align-self: flex-start; background: rgba(255,255,255,.07); color: var(--ink); }
.chat-form { display: flex; gap: 6px; padding: 8px; border-top: 1px solid rgba(255,255,255,.1); }
.chat-form input {
  flex: 1; min-width: 0;
  font: inherit; font-size: .85rem;
  color: var(--ink);
  background: rgba(0,0,0,.35);
  border: 1px solid rgba(255,255,255,.18);
  border-radius: 8px;
  padding: 7px 10px;
}
.chat-form input:focus { outline: none; border-color: var(--gold); }

/* incoming friend-challenge toast (floats above everything) */
.challenge-toast {
  position: fixed;
  right: 18px; bottom: 18px;
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: min(320px, calc(100vw - 36px));
  padding: 14px 16px;
  border-radius: 14px;
  background: linear-gradient(165deg, #382718, #241709);
  border: 1px solid rgba(212, 162, 78, .5);
  box-shadow: 0 16px 40px rgba(0,0,0,.6), 0 0 24px rgba(212,162,78,.18);
  animation: cardUp .35s cubic-bezier(.3, 1.4, .5, 1);
}
.challenge-toast.hidden { display: none; }
/* if the chat box is open, the toast moves up instead of covering it */
.chat-box:not(.hidden) ~ .challenge-toast { bottom: 306px; }
.challenge-toast span { font-weight: 700; }
.challenge-btns { display: flex; gap: 8px; }
.challenge-btns .btn-small { flex: 1; }

/* ---------- Leaderboard ---------- */

.lb-table-wrap { overflow-x: auto; }
.lb-table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.lb-table th {
  text-align: left; color: var(--ink-dim); font-size: .75rem;
  text-transform: uppercase; letter-spacing: 1px;
  padding: 8px 10px; border-bottom: 1px solid rgba(255,255,255,.12);
}
.lb-table td { padding: 8px 10px; border-bottom: 1px solid rgba(255,255,255,.06); }
.lb-table tr:nth-child(1) td:first-child { color: #f2c14e; font-weight: 800; }
.lb-table tr:nth-child(2) td:first-child { color: #c8c8d0; font-weight: 800; }
.lb-table tr:nth-child(3) td:first-child { color: #c9804a; font-weight: 800; }
.lb-table .lb-elo { color: var(--gold-soft); font-weight: 700; }
.lb-empty { color: var(--ink-dim); padding: 14px 4px; }

.lb-history-title { margin-top: 20px; }
.history-list { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.history-row {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: .88rem;
  padding: 8px 10px;
  background: rgba(255,255,255,.04);
  border-radius: 8px;
}
.history-row .win { color: #8fd18a; font-weight: 700; }
.history-row .loss { color: #ff9d78; font-weight: 700; }
.history-row .draw { color: var(--gold-soft); font-weight: 700; }
.history-row .delta { color: var(--ink-dim); }
/* the opponent name is the flexible part — these two keep their own width so a
   long name never squeezes the rating out of the row */
.history-row .opp { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.history-row .delta.unrated { font-style: italic; opacity: .8; }
.history-row .history-variant {
  font-size: .74rem;
  color: var(--gold-soft);
  background: rgba(212,162,78,.14);
  border-radius: 20px;
  padding: 1px 8px;
  white-space: nowrap;
  align-self: center;
}

/* ---------- Affiliate column ---------- */

/* base = the "below the game" look (mobile + medium widths): a wide block
   with two cards side by side. On wide screens it becomes the LEFT column of
   a three-column layout (affiliate | board | panel) — see the min-width rule
   below. */
.affiliate {
  width: min(620px, 92vw);
  align-self: center;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.affiliate-label {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  color: var(--ink-dim);
}

/* minmax(0,1fr): columns may shrink below the image's intrinsic 240px —
   plain 1fr let the cards force horizontal scrolling on narrow phones */
/* Below the board (tablet / small laptop) the cards sit side by side. Three
   columns, not two — with three cards, two columns leave a lone card and a
   gap next to it. */
.affiliate-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }

.affiliate-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border-radius: 14px;
  background: var(--card);
  border: 1px solid var(--card-border);
  text-decoration: none;
  color: var(--ink);
  transition: transform .15s ease, box-shadow .2s ease, border-color .2s ease;
}
.affiliate-card:hover {
  transform: translateY(-2px);
  border-color: rgba(212, 162, 78, .55);
  box-shadow: 0 10px 24px rgba(0,0,0,.35);
}

.affiliate-img {
  background: #fff;
  border-radius: 10px;
  padding: 10px;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.affiliate-img img { max-width: 100%; max-height: 100%; object-fit: contain; }

.affiliate-name { font-weight: 600; font-size: .92rem; }

.affiliate-cta {
  align-self: flex-start;
  font-size: .8rem;
  font-weight: 700;
  color: #2b1a06;
  background: linear-gradient(160deg, var(--gold-soft), var(--gold));
  padding: 6px 12px;
  border-radius: 8px;
}

.affiliate-disclosure { font-size: .68rem; line-height: 1.45; color: var(--ink-dim); }

/* wide screens: a fixed three-column layout — affiliate LEFT of the board,
   board in the middle, panel on the right. Explicit grid-column placement
   puts the affiliate first regardless of its DOM order (it stays last in the
   markup so it falls BELOW the board on narrower screens). Cards stack
   vertically with a fixed small image height so the column stays compact. */
@media (min-width: 1160px) {
  .arena {
    display: grid;
    grid-template-columns: 190px minmax(0, 1fr) 322px;
    gap: 20px;
    align-items: start;
    max-width: 1480px;
  }
  /* pin all three to row 1 — the affiliate is last in the DOM, so without
     an explicit row the auto-placement drops it into a new row below */
  .affiliate  { grid-column: 1; grid-row: 1; width: auto; align-self: start; margin: 0; gap: 10px; }
  .board-wrap { grid-column: 2; grid-row: 1; justify-content: center; }
  .panel      { grid-column: 3; grid-row: 1; width: auto; }
  /* keep the checkers board SQUARE, but bind its width to the middle grid
     column (100%) — the base rule's 90vw would overflow the cell and force a
     horizontal scrollbar. The height term still caps it so the panel's online
     buttons never drop below the fold. */
  .board-frame {
    width: min(100%, calc(100vh - 168px), 700px);
    width: min(100%, calc(100dvh - 168px), 700px);
  }
  .affiliate-cards { grid-template-columns: 1fr; gap: 10px; }
  .affiliate-img { aspect-ratio: auto; height: 104px; padding: 8px; }
  .affiliate-card { padding: 10px; gap: 6px; }
}

/* ---------- Content / SEO section ---------- */

.content {
  width: 100%;
  margin-top: 56px;
  border-top: 1px solid rgba(255,255,255,.08);
  background: rgba(0,0,0,.18);
}

.content-inner {
  max-width: 980px;
  margin: 0 auto;
  padding: 48px 22px 30px;
}

/* h1 shares the h2 look: on the home page the first content heading IS the
   page's h1, and it must not jump to the browser's default 2em when promoted */
.content h1,
.content h2 {
  color: var(--gold-soft);
  font-size: 1.45rem;
  margin: 34px 0 12px;
}
.content h1:first-child,
.content h2:first-child { margin-top: 0; }

.content p {
  color: var(--ink-dim);
  line-height: 1.65;
  max-width: 75ch;
  margin: 0 0 14px; /* the global reset removes paragraph spacing */
}

.content strong { color: var(--ink); font-weight: 600; }

/* intro: text left, featured image right (stacks on small screens) */
.content-intro { display: flex; gap: 26px; align-items: flex-start; }
.content-intro-text { flex: 1; min-width: 0; }
.content-hero {
  width: min(300px, 34vw);
  height: auto;
  flex-shrink: 0;
  border-radius: 14px;
  border: 1px solid var(--card-border);
  box-shadow: 0 10px 28px rgba(0,0,0,.4);
}
@media (max-width: 720px) {
  .content-intro { flex-direction: column; align-items: center; }
  .content-hero { width: min(340px, 86vw); }
}

.content-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  margin: 20px 0 8px;
}

.content-card {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: 14px;
  padding: 18px;
}

.content-card h3 { color: var(--ink); font-size: 1.05rem; margin-bottom: 8px; }
.content-card p { font-size: .92rem; margin-bottom: 0; }

.faq dt {
  color: var(--ink);
  font-weight: 600;
  margin-top: 14px;
}
.faq dd {
  color: var(--ink-dim);
  line-height: 1.55;
  margin: 4px 0 0;
}

.site-footer {
  width: 100%;
  padding: 22px 16px 6px;
  text-align: center;
  color: var(--ink-dim);
  font-size: .85rem;
  border-top: 1px solid rgba(255,255,255,.06);
}
.site-footer a { color: var(--gold-soft); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }
.footer-links { margin-top: 6px; }
.footer-links a { margin: 0 6px; white-space: nowrap; }

/* ---------- Legal / content pages (how-to-play) ---------- */

/* width:100% is load-bearing, not tidying. <body> is `display:flex;
   flex-direction:column; align-items:center`, and align-items:center sizes
   each item to its FIT-CONTENT width instead of stretching it. So <main> took
   the width of its widest child: .legal-table carries min-width:520px, giving
   520 + 44 padding = 564px on a 360px phone, and the whole document scrolled
   sideways. Every page with a table was affected, checkers-openings.html
   included — this was not new.
   min-width:0 does NOT fix it (tried first, measured, no change): nothing here
   is failing to shrink on the main axis. Giving the item an explicit width
   makes it fill the column instead of hugging its content, and the
   overflow-x:auto on .legal-table-wrap finally gets to be the thing that
   scrolls. Measured at 360px: 574 -> 360. */
.legal-page { max-width: 860px; width: 100%; margin: 0 auto; padding: 40px 22px 20px; }
/* the "· Updated <date>" half of the subtitle — present, but never louder than
   what the page is actually about */
.upd-date { color: var(--ink-dim); opacity: .75; font-size: .92em; white-space: nowrap; }

/* "this page in your language" pointer — the hreflang tag talks to Google, this
   line talks to the reader who already landed on the wrong one */
.lang-note {
  font-size: .82rem;
  color: var(--ink-dim);
  margin: 0 0 14px;
  padding: 7px 11px;
  border-left: 3px solid var(--gold-soft);
  background: rgba(255,255,255,.04);
  border-radius: 0 6px 6px 0;
}
.lang-note a { color: var(--gold-soft); }

/* breadcrumb bar above the h1 — generated from each page's BreadcrumbList
   schema, so the two can never disagree */
.crumbs {
  font-size: .78rem;
  color: var(--ink-dim);
  margin: 0 0 10px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.crumbs a { color: var(--gold-soft); text-decoration: none; }
.crumbs a:hover, .crumbs a:focus-visible { text-decoration: underline; }
.crumb-sep { opacity: .5; }
.crumbs [aria-current="page"] { color: var(--ink-dim); }

.legal-page h1 { color: var(--gold-soft); font-size: clamp(1.5rem, 4vw, 1.9rem); margin: 18px 0 4px; }
.legal-page .updated { color: var(--ink-dim); font-size: .82rem; margin-bottom: 26px; }
.legal-page h2 { color: var(--gold-soft); font-size: 1.2rem; margin: 30px 0 10px; }
.legal-page h3 { color: var(--ink); font-size: 1.02rem; margin: 20px 0 8px; }
.legal-page p { color: var(--ink-dim); line-height: 1.7; margin: 0 0 14px; }
.legal-page ul { color: var(--ink-dim); line-height: 1.7; margin: 0 0 14px; padding-left: 22px; }
.legal-page li { margin-bottom: 7px; }
.legal-page strong { color: var(--ink); font-weight: 600; }
.legal-page a { color: var(--gold-soft); }
.legal-page .lead { color: var(--ink); background: var(--card); border: 1px solid var(--card-border);
  border-radius: 12px; padding: 14px 16px; margin-bottom: 22px; }

.legal-table-wrap { overflow-x: auto; margin: 6px 0 20px; }
.legal-table { width: 100%; border-collapse: collapse; font-size: .88rem; min-width: 520px; }
.legal-table th, .legal-table td { text-align: left; padding: 10px 12px;
  border-bottom: 1px solid var(--card-border); color: var(--ink-dim); vertical-align: top; }
.legal-table th { color: var(--ink); font-weight: 600; background: rgba(255,255,255,.03); }
.legal-table code { color: var(--gold-soft); font-size: .85em; }

.back-home { display: inline-block; margin: 26px 0 0; color: var(--gold-soft); text-decoration: none; }
.back-home:hover { text-decoration: underline; }

/* ---------- How-to-play guide ---------- */
.guide-toc { background: var(--card); border: 1px solid var(--card-border);
  border-radius: 12px; padding: 16px 20px; margin: 0 0 30px; }
.guide-toc-title { margin: 0 0 10px; font-size: .95rem; color: var(--ink);
  text-transform: uppercase; letter-spacing: 1px; }
.guide-toc ol { margin: 0; padding-left: 20px; color: var(--ink-dim);
  columns: 2; column-gap: 30px; }
.guide-toc li { margin-bottom: 6px; }
.guide-toc a { color: var(--gold-soft); text-decoration: none; }
.guide-toc a:hover { text-decoration: underline; }

.legal-page h2[id] { scroll-margin-top: 16px; }

.guide-tip { border-left: 3px solid var(--gold); background: rgba(212,162,78,.10);
  border-radius: 0 10px 10px 0; padding: 12px 16px; margin: 4px 0 20px; color: var(--ink); }
.guide-tip strong { color: var(--gold-soft); }

.guide-cta { display: inline-block; margin: 4px 0 8px; padding: 12px 22px;
  font-weight: 800; font-size: 1.02rem; text-decoration: none;
  background: linear-gradient(160deg, var(--gold-soft), var(--gold)); border-radius: 10px;
  box-shadow: 0 4px 14px rgba(212,162,78,.3); }
/* higher specificity than ".legal-page a" (gold-soft), which otherwise
   tinted the label almost the same colour as the gold button */
.legal-page a.guide-cta { color: #2b1a06; }
.guide-cta:hover { filter: brightness(1.05); text-decoration: none; }

.guide-figure { margin: 6px 0 22px; }
.guide-figure img { display: block; width: 100%; height: auto; border-radius: 12px;
  border: 1px solid var(--card-border); box-shadow: 0 12px 30px rgba(0,0,0,.4); }
.guide-figure figcaption { margin-top: 8px; font-size: .82rem; color: var(--ink-dim);
  text-align: center; font-style: italic; }

@media (max-width: 640px) { .guide-toc ol { columns: 1; } }

/* ---------- Responsive ---------- */

@media (max-width: 900px) {
  .arena { flex-direction: column; align-items: center; }
  .panel { width: min(620px, 92vw); }
}

/* touch devices: comfortable targets + no iOS focus-zoom.
   (The sidebar sits BELOW the board here, so taller buttons cost nothing.) */
@media (pointer: coarse) {
  /* iOS zooms the whole page into any focused input below 16px */
  #accountForm input, .friend-search, .chat-form input, #inviteLink {
    font-size: 16px;
  }
  .btn-small { padding: 9px 12px; font-size: .85rem; }
  .player-actions .btn-small { padding: 9px 12px; font-size: .85rem; }
  .mode-btn, .diff-btn { padding: 10px 6px; font-size: .88rem; }
  .match-add { width: 34px; height: 34px; font-size: 1.05rem; }
  .sound-toggle { width: 42px; height: 42px; }
  .modal-close { font-size: 1.15rem; padding: 4px 8px; }
}

/* small screens: reclaim a little vertical room above the board */
@media (max-width: 640px) {
  body { padding: 12px 10px 32px; }
  .site-header { margin-bottom: 8px; }
  .sound-toggle { top: 8px; right: 10px; }
  .board-frame {
    width: min(94vw, calc(100vh - 148px), 700px);
    width: min(94vw, calc(100dvh - 148px), 700px);
  }
  /* One affiliate card is enough on a phone — more than that just pushes the
     page down. Shown is the LAST one (the chess & checkers set): on a checkers
     site it is the most relevant of the three, and relevance is most of what an
     affiliate link has going for it.
     Written as "hide everything but the last" rather than naming a position, so
     adding a fourth card later cannot silently leave two showing — which is
     exactly what the old :nth-child(2) rule would have done here. */
  .affiliate-cards { grid-template-columns: 1fr; }
  .affiliate-card:not(:last-child) { display: none; }
}

/* ---------- Daily puzzle page ---------- */

/* The board markup is the game's, so squares, discs and crowns come for free.
   Only the frame is retuned: on an article page it must sit inside the column
   like a figure, not claim the viewport height the way the playable board
   does. */
/* Sized against its COLUMN, not the viewport. The game board uses vw/dvh
   because it owns the screen; here the same rule pushed the board past the
   panel padding and the body margin on a phone, and the whole page scrolled
   sideways. Percent cannot overflow its parent. */
.puzzle-page .pz-frame {
  width: min(100%, 520px);
  margin: 0 auto;
}
/* No hover-grow on puzzle pieces: the game grows the piece under the cursor as
   an affordance for dragging attention, but here every disc is clickable only
   when it is red and has a move, and the wobble on the others reads as a lie. */
.puzzle-page .piece:hover .disc { transform: none; }
.puzzle-page .piece.red { cursor: pointer; }
.puzzle-page .piece.black { cursor: default; }

.pz-panel {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  padding: 18px 16px 16px;
  margin: 22px 0 30px;
  text-align: center;
}
.pz-heading {
  font-size: .78rem;
  text-transform: uppercase;
  letter-spacing: 1.6px;
  color: var(--ink-dim);
  margin: 0 0 4px;
  font-weight: 700;
}
/* Two modes over one board. Styled like the game's variant tabs rather than
   like buttons — this is a view switch, not an action. */
.pz-modes {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin: 0 0 12px;
}
.pz-mode {
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  color: var(--ink-dim);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 5px 15px;
  cursor: pointer;
}
.pz-mode:hover { color: var(--gold-soft); border-color: var(--gold-soft); }
.pz-mode.on {
  color: #1a1410;
  background: var(--gold-soft);
  border-color: var(--gold-soft);
}
/* The number this whole mode exists for, so it sits above the board rather
   than under it. Tabular figures: a rating that shifts sideways as it changes
   reads as a glitch. */
.pz-rating {
  font-size: .9rem;
  font-weight: 600;
  color: var(--gold-soft);
  margin: 0 0 8px;
  font-variant-numeric: tabular-nums;
}
.pz-rating.pz-up { color: var(--green, #63bd82); }
.pz-rating.pz-down { color: #d9736a; }
.pz-task {
  font-size: 1.12rem;
  color: var(--gold-soft);
  font-weight: 600;
  margin: 0 0 14px;
}
/* Scoped to the panel ON PURPOSE. `.legal-page p` is one specificity step
   above a bare `.pz-status`, so it won the margin and reset `auto` to 0 —
   the verdict sat against the left edge while every rule said "centred". */
.pz-panel .pz-status {
  /* Reserve the height of two lines. The verdict appears and disappears on
     every attempt, and without this the whole page jumps a line each time. */
  min-height: 2.8em;
  display: flex; align-items: center; justify-content: center;
  margin: 14px auto 0;
  max-width: 42ch;
  font-size: .95rem;
  line-height: 1.4;
  text-align: center;
  color: var(--ink-dim);
}
.pz-status.pz-good { color: #7ac77a; }
.pz-status.pz-bad { color: #e07a6a; }
.pz-streak { margin: 2px 0 0; font-size: .85rem; color: var(--gold-soft); min-height: 1.2em; }

/* the solve moment. The board is the reward, so the panel lights up around it
   rather than covering it with a dialog — a puzzle you just solved is a thing
   you want to look at, not dismiss. */
.pz-result {
  margin: 6px 0 0; font-size: 1.25rem; letter-spacing: .5px;
  color: var(--gold-soft); font-weight: 700;
}
.pz-panel.pz-won {
  border-color: rgba(212, 162, 78, .55);
  box-shadow: 0 0 0 1px rgba(212,162,78,.25), 0 0 34px rgba(212,162,78,.16);
  animation: pzWon .7s ease-out;
}
@keyframes pzWon {
  0%   { box-shadow: 0 0 0 1px rgba(212,162,78,0), 0 0 0 rgba(212,162,78,0); }
  40%  { box-shadow: 0 0 0 2px rgba(212,162,78,.75), 0 0 46px rgba(212,162,78,.4); }
  100% { box-shadow: 0 0 0 1px rgba(212,162,78,.25), 0 0 34px rgba(212,162,78,.16); }
}

/* a refused move: the square it was aimed at flashes and nothing else moves,
   so the answer lands where the player was already looking */
.sq.pz-wrong::after {
  content: ""; position: absolute; inset: 0;
  background: rgba(224, 122, 106, .55);
  animation: pzWrong .7s ease-out forwards;
}
@keyframes pzWrong {
  0% { opacity: 0; } 12% { opacity: 1; } 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .pz-panel.pz-won { animation: none; }
  .sq.pz-wrong::after { animation: none; opacity: .55; }
}

.pz-actions {
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  margin-top: 14px;
}
.pz-btn {
  background: rgba(255,255,255,.08);
  border: 1px solid rgba(255,255,255,.16);
  color: var(--ink);
  border-radius: 8px;
  padding: 8px 15px;
  font: inherit;
  font-size: .88rem;
  cursor: pointer;
}
.pz-btn:hover { background: rgba(255,255,255,.14); }
.pz-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.pz-btn-alt {
  background: linear-gradient(160deg, var(--gold-soft), var(--gold) 60%, #b07f30);
  border-color: transparent;
  color: #2b1a06;
  font-weight: 600;
}
.pz-btn-alt:hover { filter: brightness(1.05); background: linear-gradient(160deg, var(--gold-soft), var(--gold) 60%, #b07f30); }

.pz-archive {
  list-style: none;
  padding: 0;
  margin: 12px 0 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 4px 18px;
}
.pz-archive li { font-size: .92rem; }
.pz-archive a { color: var(--gold-soft); text-decoration: none; }
.pz-archive a:hover, .pz-archive a:focus-visible { text-decoration: underline; }
.pz-archive .pz-done { color: #7ac77a; }
.pz-archive .pz-none { color: var(--ink-dim); }

@media (max-width: 640px) {
  .pz-panel { padding: 14px 8px 12px; }
  .pz-actions { gap: 6px; }
  .pz-btn { padding: 8px 12px; font-size: .84rem; }
}

/* ---------- top-level navigation ---------- */

/* Four items, not a mega-menu: the site has four areas and will not have
   fifteen. It sits INSIDE the header row rather than above it — on the game
   page the board has to stay reachable without scrolling, which is also why
   there is no heading above the board. */
.site-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin-left: auto;
}
.site-nav a {
  padding: 6px 12px;
  border-radius: 7px;
  color: var(--ink-dim);
  text-decoration: none;
  font-size: .88rem;
  font-weight: 600;
  white-space: nowrap;
}
.site-nav a:hover { color: var(--ink); background: rgba(255,255,255,.06); }
.site-nav a:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.site-nav a[aria-current="page"] { color: var(--gold-soft); background: rgba(212,162,78,.12); }

/* ---- grouped entries ----
 * Two of the four areas now hold two destinations each, and a label that hid
 * one of them is why this exists: "Analysis" reached the analysis board only,
 * so the endgame database — the one thing here that nothing else on the web
 * has — could not be reached from the menu at all. "Puzzle" reached the daily
 * tactic only, so the sixty endgame exercises were invisible to anyone
 * thinking "I want to solve something", which is exactly who they are for.
 *
 * A <details> keeps the row at FOUR entries: the summary is one word wide and
 * a closed panel costs no height. Measured before building — the board ends at
 * 530px of an 844px phone and 501px of a 640px one, so the constraint noted
 * above (board reachable without scrolling) survives with 139px to spare.
 *
 * No JavaScript: the disclosure widget is the browser's own, so it works with
 * a tap, with a keyboard, and with scripting off. */
.site-nav summary {
  display: block;                 /* also kills the native triangle in Safari */
  padding: 6px 12px;
  border-radius: 7px;
  color: var(--ink-dim);
  font-size: .88rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
}
.site-nav summary::-webkit-details-marker { display: none; }
.site-nav summary:hover { color: var(--ink); background: rgba(255,255,255,.06); }
.site-nav summary:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.nav-drop { position: relative; }
.nav-drop[data-here] > summary { color: var(--gold-soft); background: rgba(212,162,78,.12); }
.nav-drop > summary::after {
  content: "\25BE";               /* escaped: no encoding accident can eat it */
  margin-left: 5px;
  font-size: .7em;
  opacity: .7;
}
/* A closed <details> hides its children through content-visibility, which does
 * NOT lift an absolutely positioned panel out of the layout. The sister project
 * shipped exactly this and the invisible links sat on top of the board,
 * swallowing clicks meant for the pieces. Hide the panel explicitly rather than
 * trusting the element to do it. */
.nav-drop:not([open]) .nav-panel { display: none; }
.nav-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  min-width: 200px;
  padding: 6px;
  background: #241a13;
  border: 1px solid var(--card-border);
  border-radius: 10px;
  box-shadow: 0 14px 34px rgba(0,0,0,.5);
}
.nav-panel a {
  display: block;
  padding: 8px 12px;
  font-size: .84rem;
  white-space: nowrap;
}
/* The last group's panel would hang off the right edge on a narrow screen;
   anchoring it to the right keeps it on the page. */
.nav-drop:last-child .nav-panel { left: auto; right: 0; transform: none; }
.site-header { display: flex; align-items: center; gap: 12px; }
/* the game page keeps its sound button pinned to the corner, so the nav must
   not run underneath it */
.arena-nav { margin-right: 44px; }

/* On a phone the logo and four items do not share a line, and letting them
   try produced a three-line right-aligned stack that reads as broken markup.
   The nav gets its own full-width row instead — one line, evenly spread, and
   the board still starts above the fold. */
@media (max-width: 560px) {
  /* The logo is the only thing on its row here (the nav wraps below it), and
     left-aligned it reads as stuck against the edge rather than placed. Centred
     it becomes an app bar: title in the middle, the sound button pinned in its
     corner, navigation across the full width underneath.
     The NAV deliberately stays edge-to-edge rather than centred with it —
     spread out, the four tap targets are bigger, which on a phone beats
     symmetry. */
  .site-header { flex-wrap: wrap; row-gap: 4px; justify-content: center; }
  .site-nav {
    width: 100%;
    margin-left: 0;
    justify-content: space-between;
    gap: 0;
  }
  /* the sound button is absolutely positioned in the corner, so once the nav
     sits on its own row there is nothing left to dodge */
  .arena-nav { margin-right: 0; }
  .site-nav a { padding: 5px 8px; font-size: .8rem; }
}

/* ---------- analysis board (/checkers-solver/) ---------- */

.sv-panel {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  padding: 16px 14px;
  margin: 22px 0 30px;
  text-align: center;
}
.solver-page .sv-frame { width: min(100%, 520px); margin: 0 auto; }
.solver-page .piece:hover .disc { transform: none; }
/* the board is inert while the engine thinks — say so rather than swallowing
   clicks silently */
.sv-panel[data-busy] .board-frame { opacity: .72; cursor: progress; }

.sv-modes { display: flex; justify-content: center; gap: 4px; margin-bottom: 10px; }
.sv-mode {
  background: rgba(0,0,0,.28); border: 1px solid transparent; color: var(--ink-dim);
  border-radius: 8px; padding: 7px 14px; font: inherit; font-size: .85rem;
  font-weight: 600; cursor: pointer;
}
.sv-mode.active { background: rgba(212,162,78,.16); color: var(--gold-soft); border-color: rgba(212,162,78,.4); }

.sv-palette { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin-bottom: 10px; }
.sv-paint {
  display: inline-flex; align-items: center; gap: 6px;
  background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.14);
  color: var(--ink); border-radius: 8px; padding: 5px 10px; font: inherit;
  font-size: .8rem; cursor: pointer;
}
.sv-paint.active { border-color: var(--gold); background: rgba(212,162,78,.16); }
/* On a phone these were 66x29 — under the 44px both Apple and Google ask for,
   and a paint you miss looks exactly like a paint that does not work, because
   the board is scrolled away from the palette by the time you find out. */
@media (max-width: 640px) {
  .sv-paint { padding: 12px; font-size: .85rem; min-height: 44px; }
  /* The palette was fixed on its own and the controls around it were missed.
     Measured on the live site at 390x844: mode tabs 34px, side-to-move 32px,
     and every action button — Analyse included — 35px. The primary action of
     the whole page was the one thing still under the floor. */
  .sv-mode, .sv-turn, .sv-btn {
    min-height: 44px; padding-top: 10px; padding-bottom: 10px;
  }
}
.sv-chip { width: 15px; height: 15px; border-radius: 50%; display: inline-block; }
.sv-chip-r { background: radial-gradient(circle at 35% 30%, #e05545, #a52a1c 60%, #7c1a10); }
.sv-chip-b { background: radial-gradient(circle at 35% 30%, #4a4a52, #26262e 60%, #131317); }
.sv-chip-k { outline: 2px solid #f2c14e; outline-offset: -3px; }

.sv-turn {
  display: inline-block; margin: 8px auto 0; padding: 6px 14px;
  background: rgba(0,0,0,.28); border: 1px solid rgba(255,255,255,.14);
  border-radius: 999px; font: inherit; font-size: .85rem; font-weight: 700;
  cursor: pointer; color: var(--ink);
}
.sv-turn.sv-red { color: #e8907f; }
.sv-turn.sv-black { color: #b9b9c6; }
.sv-turn:hover { border-color: var(--gold); }

/* The min-height reserves the wrap so the buttons do not hop when the message
   changes — worth keeping. But it reserved two lines while every message is
   written to fit one at this width, so most of the time it was blank space
   between the board and the buttons. One line on a wide screen, two on a
   narrow one where they really do wrap. */
.sv-panel .sv-status {
  min-height: 1.5em; display: flex; align-items: center; justify-content: center;
  margin: 5px auto 0; max-width: 76ch; font-size: .88rem; line-height: 1.4;
  text-align: center; color: var(--ink-dim);
}
/* Two lines is what the longest message actually needs at this width —
   measured, not padded. */
@media (max-width: 760px) { .sv-panel .sv-status { min-height: 2.6em; } }
.sv-status.sv-bad { color: #e07a6a; }
.sv-status.sv-good { color: #7ac77a; }

.sv-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 6px; }
.sv-btn {
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.16);
  color: var(--ink); border-radius: 8px; padding: 8px 14px; font: inherit;
  font-size: .86rem; cursor: pointer;
}
.sv-btn:hover { background: rgba(255,255,255,.14); }
.sv-btn:disabled { opacity: .6; cursor: progress; }
.sv-btn-main {
  background: linear-gradient(160deg, var(--gold-soft), var(--gold) 60%, #b07f30);
  border-color: transparent; color: #2b1a06; font-weight: 700;
}
.sv-btn-main:hover { filter: brightness(1.05); background: linear-gradient(160deg, var(--gold-soft), var(--gold) 60%, #b07f30); }

.sv-result {
  margin: 14px auto 0; max-width: 52ch; text-align: left;
  background: rgba(0,0,0,.22); border: 1px solid rgba(212,162,78,.28);
  border-radius: 10px; padding: 12px 14px;
}
.solver-page .sv-result p { margin: 0 0 8px; font-size: .92rem; line-height: 1.5; }
.solver-page .sv-result p:last-child { margin-bottom: 0; }
.sv-label { display: block; font-size: .68rem; text-transform: uppercase; letter-spacing: 1.4px; color: var(--ink-dim); }
.sv-best { font-size: 1.15rem !important; font-weight: 700; color: var(--gold-soft); }
.sv-verdict { color: var(--ink); }
.sv-line { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; color: var(--gold-soft); }
.sv-note { font-size: .8rem; color: var(--ink-dim); font-weight: 400; }
.sv-depth { color: var(--ink-dim); font-size: .8rem !important; }
/* A proved answer should not look like the same sentence as a guess. */
.sv-exact { color: var(--ok, #2e7d4f); font-weight: 600; }

@media (max-width: 640px) {
  .sv-panel { padding: 12px 8px; }
  .sv-btn { padding: 8px 11px; font-size: .82rem; }
}

/* ---------- the answer belongs next to the board, not under it ----------
 *
 * The result box sat below the buttons, which on a laptop put it off the
 * bottom of the screen: you pressed Analyse, the page appeared not to react,
 * and the answer was waiting somewhere you had to go looking for it. Beside
 * the board it lands in the same glance as the position it is about.
 *
 * The panel breaks out past the 860px text column to pay for it — there is
 * nothing in those margins. The PROSE deliberately does not follow: the
 * how-to steps and the article below stay at their reading width, because a
 * 1180px line of body text is harder to read, not easier.
 *
 * Only once there IS a result (.sv-has-result, set from solver.js). Holding
 * the second column open from the start would leave half the card empty on
 * every visit before the first analysis.
 */
@media (min-width: 1040px) {
  .solver-page .sv-panel {
    /* wider than the container, centred on it; no transform, so nothing blurs */
    --sv-wide: min(1120px, calc(100vw - 48px));
    width: var(--sv-wide);
    margin-left: calc((100% - var(--sv-wide)) / 2);
    display: grid;
    /* The left column is sized by the BUTTON ROW, not by the board. At 1fr it
       grew to ~730px and left the answer drifting away from the position it
       describes; cut to the board's 556px it made the four buttons wrap onto a
       second line, which put back the exact vertical space this was meant to
       save. 620px is what holds them in one row. */
    grid-template-columns: minmax(0, 620px) minmax(300px, 430px);
    justify-content: center;
    align-items: start;
    gap: 0 24px;
    padding: 18px 20px;
  }
  /* everything keeps its place in the left column... */
  .solver-page .sv-panel > * { grid-column: 1; }
  /* ...and the answer takes the right one, level with the top of the board.
     The column is held open from the first paint, empty box and all. Opening
     it only once an answer arrived moved the board sideways at the exact
     moment you were reading it. The placeholder does a second job besides
     standing still: it says where the answer will appear before you ask. */
  .solver-page .sv-panel .sv-result {
    grid-column: 2;
    /* `1 / -1` is wrong here and silently does nothing useful: -1 counts back
       from the end of the EXPLICIT grid, and only columns are declared, so it
       resolves to row 1 and the box spans a single row. Hence `span`, which
       counts implicit rows. Empty trailing rows have no height. */
    grid-row: 1 / span 30;
    align-self: start;
    margin: 0;
    max-width: none;
  }
}

/* The waiting state: dashed and dim, so it reads as space reserved for
   something rather than as a result that came back blank. */
.sv-result.sv-empty {
  border-style: dashed;
  border-color: rgba(255,255,255,.13);
  background: rgba(0,0,0,.12);
}
.solver-page .sv-result.sv-empty p { color: var(--ink-dim); font-size: .86rem; }
/* Below the two-column threshold there is no column to hold open, and an empty
   box under the buttons would be clutter with nothing to say. */
@media (max-width: 1039px) { .sv-result.sv-empty { display: none; } }

/* ---------- the solver's how-to steps ---------- */

/* Numbered, with a picture each. This section used to be three paragraphs
   sitting FIFTH on the page, below an essay — someone who did not know how to
   work the board had to scroll past two sections to find out. Instructions get
   scanned, not read, so they are steps now and they come first. */
.sv-steps { list-style: none; counter-reset: step; padding: 0; margin: 14px 0 0; }
.sv-steps > li {
  counter-increment: step;
  position: relative;
  padding-left: 46px;
  margin-bottom: 26px;
}
.sv-steps > li::before {
  content: counter(step);
  position: absolute; left: 0; top: 0;
  width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: linear-gradient(160deg, var(--gold-soft), var(--gold) 60%, #b07f30);
  color: #2b1a06; font-weight: 800; font-size: 1rem;
}
.legal-page .sv-steps h3 { margin: 4px 0 6px; }
.sv-figure { margin: 12px 0 0; }
.sv-figure img {
  display: block;
  width: 100%; max-width: 560px; height: auto;
  border-radius: 10px;
  border: 1px solid var(--card-border);
}
.legal-page .sv-figure figcaption {
  margin-top: 6px;
  font-size: .82rem;
  color: var(--ink-dim);
  max-width: 560px;
}

@media (max-width: 640px) {
  .sv-steps > li { padding-left: 38px; }
  .sv-steps > li::before { width: 27px; height: 27px; font-size: .9rem; }
}

/* explicit share targets, shown next to the native sheet after a solve */
.pz-share-links { margin: 10px 0 0; font-size: .85rem; color: var(--ink-dim); }
.pz-share-links a { color: var(--gold-soft); text-decoration: none; font-weight: 600; }
.pz-share-links a:hover, .pz-share-links a:focus-visible { text-decoration: underline; }

/* the measured-methodology table — the one thing on the puzzle page no other
   puzzle site can print, so it gets to look like a result and not like prose */
.pz-table {
  width: 100%;
  border-collapse: collapse;
  margin: 16px 0 18px;
  font-size: .92rem;
}
.pz-table caption {
  caption-side: top;
  text-align: left;
  font-size: .78rem;
  text-transform: uppercase;
  letter-spacing: 1.3px;
  color: var(--ink-dim);
  padding-bottom: 8px;
}
.pz-table th, .pz-table td {
  padding: 9px 10px;
  border-bottom: 1px solid var(--card-border);
  text-align: left;
  font-weight: 400;
}
.pz-table thead th {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--ink-dim);
  font-weight: 700;
}
.pz-table tbody th { color: var(--ink); }
.pz-table td { color: var(--gold-soft); white-space: nowrap; text-align: right; }
/* The nowrap above is right for a column of percentages and wrong for one of
   prose: the before/after tables added in 2026-08 carry phrases like "the game,
   in 3-4 moves", and holding those on one line pushed the whole PAGE 30px
   sideways on a 390px screen. Measured, not guessed — the same failure this
   stylesheet already carries a note about further up. */
.pz-table.pz-wide td { white-space: normal; text-align: left; }
.pz-table tbody tr:first-child td { font-size: 1.15rem; }

/* the solver's share targets — always on, because the position always exists */
.sv-share { margin: 10px 0 0; font-size: .85rem; color: var(--ink-dim); }
.sv-share a { color: var(--gold-soft); text-decoration: none; font-weight: 600; }
.sv-share a:hover, .sv-share a:focus-visible { text-decoration: underline; }
/* Measured on a real phone (390x844, live site): 18px tall, and the X link
   8px WIDE, because its whole label is one character. That is not a small
   target, it is an unhittable one — and a link you keep missing reads as a
   link that does not work. Same 44px floor the palette already got. */
@media (max-width: 640px) {
  .sv-share a {
    display: inline-block; min-width: 44px; min-height: 44px; line-height: 44px;
    padding: 0 8px; text-align: center; vertical-align: middle;
  }
}

/* American on a lobby row: labelled, but quiet. Every waiting row now carries
   its variant — otherwise a bare row means both "American" and "no
   information" and the reader cannot tell which — and the unusual ones still
   have to be the ones that catch the eye. */
.lobby-variant-plain {
  color: var(--ink-dim);
  background: rgba(255,255,255,.05);
  border-color: rgba(255,255,255,.12);
  font-weight: 600;
}

/* Three states, one dot. Green is "answers now", amber is "connected but the
   tab is buried", grey is "not here at all" — the same order the rows are
   sorted in, so the eye and the list agree. Never colour ALONE: every row also
   says it in words, because a colour difference is not information to a reader
   who cannot see it. */
.lobby-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: 0 0 auto;
  margin-right: 2px;
}
.lobby-dot.active { background: #63bd82; box-shadow: 0 0 0 2px rgba(99, 189, 130, .18); }
.lobby-dot.idle   { background: #d8a24a; }
.lobby-dot.away   { background: #6b5c46; }

/* "3 players online · 1 waiting for an opponent" — the first line of the
   lobby, above the sections it counts. Tabular figures so the numbers do not
   shift sideways as the room fills and empties on the 5s poll. */
.lobby-pulse {
  margin: -4px 0 12px;
  font-size: .85rem;
  font-weight: 600;
  color: var(--gold-soft);
  font-variant-numeric: tabular-nums;
}

/* what variant an outgoing challenge will use — said once, above the list */
.lobby-note {
  margin: 2px 0 8px;
  font-size: .78rem;
  color: var(--ink-dim);
}

/* the invite row's share button — gold like the other primary action, so the
   eye lands on "send it" rather than on "copy it" */
.invite-row .btn-share {
  background: linear-gradient(160deg, var(--gold-soft), var(--gold) 60%, #b07f30);
  border-color: transparent;
  color: #2b1a06;
  font-weight: 700;
  white-space: nowrap;
}
.invite-row .btn-share:hover { filter: brightness(1.05); }
/* three items on one line get tight on a phone: the link may shrink, the two
   buttons may not */
@media (max-width: 420px) {
  .invite-row { flex-wrap: wrap; }
  #inviteLink { flex: 1 1 100%; }
}

/* the share control is an <a>, not a <button> — it has to look identical */
a.btn-share {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
a.btn-share:hover { text-decoration: none; }

/* ---------- endgame database cards (/checkers-endgame/) ----------
 *
 * The boards here are illustrations you can play, not the game board, so the
 * frame must NOT keep .board-frame's viewport binding: that rule sizes itself
 * from 90vw/100dvh because it owns the screen, and inside a text column it
 * pushes the page sideways. Same trap the puzzle page hit — size from the
 * column instead. */
.endgame-page .eg-frame {
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
  padding: clamp(8px, 1.6vw, 14px);
}
.endgame-page .eg-frame .board,
.endgame-page .eg-frame .pieces { inset: clamp(8px, 1.6vw, 14px); }

/* No hover-grow: nothing on these boards is clickable, and a disc that swells
   under the cursor promises an interaction that is not there. */
.endgame-page .piece:hover .disc { transform: none; }

.eg-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: 20px;
  margin: 18px 0 26px;
}

.eg-card {
  background: rgba(255,255,255,.035);
  border: 1px solid var(--card-border);
  border-radius: 14px;
  padding: 16px 16px 14px;
}
.eg-card-wide { margin: 18px 0 26px; }

/* ---- the endgame quiz ----
   Same shell as a card so the two read as one family, but it is a single
   widget rather than a grid: the exercises come from puzzles.json and rotate
   in place, which keeps sixty positions off the page weight. */
.eg-quiz {
  background: rgba(255,255,255,.035);
  border: 1px solid var(--card-border);
  border-radius: 14px;
  padding: 16px 16px 14px;
  margin: 18px 0 22px;
  max-width: 520px;
}
.eg-quiz-bands { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.eg-quiz-band.is-on {
  background: var(--gold);
  border-color: var(--gold);
  color: #1a1410;
}
.eg-quiz-task { margin: 0 0 12px; font-size: .93rem; color: var(--ink-dim); }
.eg-quiz-task strong { color: var(--gold-soft); }
.eg-quiz-status { margin: 12px 0 0; font-size: .9rem; min-height: 1.3em; }
.eg-quiz-status.eg-good { color: var(--gold-soft); font-weight: 600; }
.eg-quiz-status.eg-bad { color: #e07a6a; }
/* the picked-up piece, matching the game board's own selection ring */
.eg-quiz .sq.sel::after {
  content: ""; position: absolute; inset: 0;
  box-shadow: inset 0 0 0 3px var(--gold);
  border-radius: 2px;
}
/* a refused move flashes the square it was aimed at — same idea, and the same
   reason, as .sq.pz-wrong on the daily puzzle: the answer belongs where the
   player was already looking */
.eg-quiz .sq.eg-wrong::after {
  content: ""; position: absolute; inset: 0;
  background: rgba(224, 122, 106, .55);
  animation: pzWrong .7s ease-out forwards;
}

.eg-title { margin: 0 0 4px; font-size: 1.05rem; }
.eg-verdict {
  margin: 0 0 12px;
  font-size: .9rem;
  font-weight: 600;
  color: var(--gold-soft);
}
.eg-verdict.eg-win { color: var(--gold-soft); }

.eg-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 12px;
}
.eg-btn {
  font: inherit;
  font-size: .85rem;
  padding: 7px 12px;
  border-radius: 9px;
  cursor: pointer;
  color: var(--ink);
  background: rgba(255,255,255,.08);
  border: 1px solid var(--card-border);
}
.eg-btn:hover { background: rgba(255,255,255,.14); }
.eg-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.eg-btn-main {
  flex: 1 1 auto;
  min-width: 140px;
  font-weight: 600;
  color: #241a08;
  background: linear-gradient(160deg, var(--gold-soft), var(--gold) 60%, #b07f30);
  border-color: rgba(240, 214, 150, .5);
}
.eg-btn-main:hover { filter: brightness(1.06); background: linear-gradient(160deg, var(--gold-soft), var(--gold) 60%, #b07f30); }
.eg-step { min-width: 42px; font-size: 1.05rem; line-height: 1; }

/* Reserves its line so the card does not jump when playback starts — the
   status text appears and disappears, the layout must not move with it. */
.eg-progress {
  margin: 10px 0 0;
  min-height: 1.3em;
  font-size: .85rem;
  color: var(--ink-dim);
}
.eg-note { margin: 10px 0 0; font-size: .86rem; color: var(--ink-dim); }
.eg-links { margin: 8px 0 0; font-size: .82rem; }
.eg-links a { color: var(--gold-soft); text-decoration: none; }
.eg-links a:hover, .eg-links a:focus-visible { text-decoration: underline; }
