/* ===========================================================================
   Quoridor — chess.com-inspired theme
   --------------------------------------------------------------------------- */
:root {
  /* ---- design tokens -------------------------------------------------------
     The prompt's named tokens (--color-*, --radius-*, --shadow-card, etc.) are
     the source of truth. The older short aliases (--bg, --panel, --accent…) are
     kept and point at the same values so the ~900 lines that already reference
     them stay consistent without a mass rename. */

  /* surfaces */
  --color-bg:             #1e1e1e;            /* main page background */
  --color-surface:        #272727;            /* cards, panels */
  --color-surface-raised: #2f2f2f;            /* hover / elevated surfaces */
  --color-border:         rgba(255,255,255,0.08);
  --color-border-focus:   rgba(100,180,80,0.6);

  /* accent + text */
  --color-accent:         #5a9e3f;            /* primary green */
  --color-accent-hover:   #6ab84a;
  --color-text-primary:   #e8e8e0;
  --color-text-muted:     #888880;

  /* players */
  --color-cream:          #e8e5c8;            /* player 1 pawn */
  --color-blue:           #4a90d9;            /* player 2 pawn */
  --color-gold:           #d4a017;            /* P1 walls / trophy */

  /* radii / shadows / motion */
  --radius-sm:            8px;
  --radius-md:            12px;
  --radius-lg:            16px;
  --shadow-card:          0 4px 24px rgba(0,0,0,0.4);
  --font-ui:              'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --transition-fast:      all 0.15s ease;
  --transition-med:       all 0.25s ease;

  /* ---- legacy aliases (mapped onto the tokens above) ---- */
  --bg:            var(--color-bg);
  --panel:         var(--color-surface);
  --panel-2:       var(--color-surface-raised);
  --line:          var(--color-border);
  --text:          var(--color-text-primary);
  --text-dim:      var(--color-text-muted);

  --cell-light:    #ebecd0;
  --cell-dark:     #739552;
  --board-edge:    #4a6b34;

  --p1:            var(--color-cream);  /* warm cream */
  --p1-ring:       #2b2b2b;
  --p2:            var(--color-blue);   /* bold blue */
  --p2-ring:       #143a63;

  --wall1:         var(--color-gold);   /* P1 walls: warm gold */
  --wall2:         var(--color-blue);   /* P2 walls: blue */

  --dot:           rgba(20, 20, 20, 0.28);
  --legal:         #b6d36b;
  --illegal:       #d65a4a;
  --last:          rgba(255, 233, 110, 0.45);

  --accent:        var(--color-accent);
  --accent-strong: var(--color-accent-hover);

  --radius:        var(--radius-md);
  --shadow:        var(--shadow-card);

  /* board geometry: 9 cells + 8 gaps. gap is a fraction of a cell. */
  --gap-ratio: 0.16;
}

* { box-sizing: border-box; }
/* Global hide utility — applies to any element given the `hidden` class. */
.hidden { display: none !important; }

/* Visible keyboard focus for every interactive control (mouse clicks, which use
   :focus not :focus-visible, stay ring-free). */
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 4px; }

/* Screen-reader-only text (e.g. the live move announcer). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- board themes -------------------------------------------------------
   Selectable board skins. Each retunes the cell + edge colours (and goal-row
   tints) via a `data-theme` attribute on <body>. The default (Forest) is the
   :root values; the named themes below override them. */
:root { --goal-light: #d6d7b4; --goal-dark: #5f7e42; }

body[data-theme="walnut"] {
  --cell-light: #f0dab5; --cell-dark: #b58863; --board-edge: #7a5436;
  --goal-light: #e6caa0; --goal-dark: #9d7150;
}
body[data-theme="slate"] {
  --cell-light: #d7dbe0; --cell-dark: #7d8794; --board-edge: #4d5560;
  --goal-light: #c4cad2; --goal-dark: #6a7480;
}
body[data-theme="ocean"] {
  --cell-light: #cfe3ee; --cell-dark: #5b91b5; --board-edge: #356180;
  --goal-light: #b9d4e4; --goal-dark: #4d7e9f;
}

/* ---- piece styles -------------------------------------------------------
   `data-pieces` on <body> selects the pawn look. Default (classic) is the
   glossy radial sphere defined under `.pawn`; flat and ring are overrides. */
body[data-pieces="flat"] .pawn { box-shadow: none; }
body[data-pieces="flat"] .pawn.p1 { background: var(--color-cream); }
body[data-pieces="flat"] .pawn.p2 { background: var(--color-blue); }
body[data-pieces="ring"] .pawn {
  background: var(--color-bg) !important;
}
body[data-pieces="ring"] .pawn.p1 { border-color: var(--color-cream); border-width: 5px; }
body[data-pieces="ring"] .pawn.p2 { border-color: var(--color-blue); border-width: 5px; }

html, body {
  margin: 0; padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  -webkit-font-smoothing: antialiased;
  /* Base topbar height; the fixed bar grows by the top safe-area inset on
     notched devices, and .app / .side-rail reserve the same amount. */
  --topbar-h: 52px;
}
/* Put the scroll container on the ROOT only. Setting overflow on BOTH html and
   body makes body a second, competing scroller sized to its content — wheel
   gestures over in-page content then scroll the (already-fitted) body and do
   nothing, which reads as "can't scroll". Body stays overflow:visible so the
   single document scroller on <html> handles everything. */
html {
  overflow-x: hidden;   /* no sideways scroll; the board scales to fit instead */
  overflow-y: auto;     /* vertical scroll so the footer is always reachable */
}

/* min-height (not height) so content can grow past the viewport and scroll,
   keeping the footer reachable instead of trapping it off-screen. */
/* The topbar is fixed (see below), so reserve its 52px height with padding so
   the page content starts just beneath it instead of under it. */
.app { min-height: 100vh; display: flex; flex-direction: column; padding-top: calc(var(--topbar-h) + env(safe-area-inset-top)); }

/* ---- top bar ---- */
/* Pinned to the top of the viewport so the header + left rail stay in place
   while only the board / panel / footer scroll. */
.topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between;
  height: calc(var(--topbar-h) + env(safe-area-inset-top));
  /* Honour notch/safe-area insets (viewport-fit=cover): push content below the
     status bar and clear of rounded/left/right intrusions. */
  padding: env(safe-area-inset-top) max(20px, env(safe-area-inset-right)) 0 max(20px, env(safe-area-inset-left));
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}
.brand {
  display: flex; align-items: center; gap: 9px;
  font-weight: 700; font-size: 18px; letter-spacing: .2px;
  color: var(--color-text-primary); text-decoration: none;
}
.brand:hover { color: var(--color-text-primary); }
.brand-mark { display: inline-flex; color: var(--color-accent); }
.topbar-controls { display: flex; align-items: center; gap: 12px; }

/* Mobile in-game "Menu" chip. Hidden by default; revealed only on small screens
   while a game is on (see the in-game mobile block). It re-opens the collapsed
   nav rail so the board can own the screen during play. */
.nav-toggle {
  display: none; align-items: center; gap: 7px;
  margin-right: auto; margin-left: 14px;            /* sit just right of the brand */
  height: 38px; padding: 0 14px;
  border: 1px solid var(--color-border); background: var(--color-surface-raised);
  color: var(--color-text-primary); border-radius: 999px;
  font: inherit; font-weight: 600; font-size: 14px; cursor: pointer;
  transition: var(--transition-fast);
}
.nav-toggle:hover { border-color: var(--color-accent); }
.nav-toggle .ic { width: 18px; height: 18px; }

.seg { display: inline-flex; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 3px; }
.seg-btn {
  border: 0; background: transparent; color: var(--text-dim);
  font: inherit; font-weight: 600; font-size: 14px;
  padding: 7px 14px; border-radius: 8px; cursor: pointer; transition: all .15s;
}
.seg-btn.is-active { background: var(--accent); color: #16210d; }
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border: 1px solid transparent; background: transparent;
  color: var(--color-text-primary);
  border-radius: var(--radius-sm); cursor: pointer;
  transition: var(--transition-fast);
}
.icon-btn:hover { background: var(--color-surface-raised); border-color: var(--color-border); }
.icon-btn.is-on { background: rgba(90, 158, 63, .15); border-color: var(--color-accent); color: var(--color-accent); }
.icon-btn .ic { width: 19px; height: 19px; }

/* ---- header: account ---- */
/* (.topbar is position:fixed above, which is also a containing block for the
   absolutely-positioned account dropdown.) */
.topbar-account { display: flex; align-items: center; gap: 10px; }
/* Sign In: a real pill CTA — a trust signal that it leads somewhere. */
.signin-btn {
  padding: 8px 20px; border-radius: 20px;
  background: var(--color-accent); color: #fff;
  border: 1px solid transparent; font-weight: 600;
  box-shadow: 0 2px 8px rgba(90, 158, 63, .3);
  transition: var(--transition-fast);
}
.signin-btn:hover {
  background: var(--color-accent-hover);
  box-shadow: 0 4px 12px rgba(90, 158, 63, .4);
}

.account-chip { position: relative; }
.account-summary {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  border: 1px solid var(--color-border); background: var(--color-surface-raised); color: var(--color-text-primary);
  border-radius: 999px; padding: 5px 12px 5px 5px; font: inherit; font-weight: 600;
  transition: var(--transition-fast);
}
.account-summary:hover { border-color: var(--color-accent); }
.account-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--color-accent); color: #fff;
  font-size: 13px; font-weight: 700; flex: none;
  overflow: hidden;
}

/* Avatar image fills its (circular or rounded) container; shape via inherit. */
.avatar { overflow: hidden; }
.avatar.has-img { background: transparent; }
.avatar-img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }

/* Small avatar circle for live-game player panels. */
.seat-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%; flex: none; overflow: hidden;
  background: var(--color-accent); color: #fff; font-size: 12px; font-weight: 800;
}
/* The topbar account chip stays compact: just the avatar + the ELO pill. The
   username (which can be up to 20 chars and ballooned the chip) and the rank
   label are dropped here — both still appear in-game and on the profile. */
.account-name { display: none; }
.account-rank { display: none; }
.account-elo {
  background: rgba(90, 158, 63, .15); color: var(--color-accent); border-radius: 999px;
  padding: 2px 9px; font-size: 13px; font-weight: 700;
  flex: none; white-space: nowrap;
}
.account-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 30;
  display: flex; flex-direction: column; min-width: 150px; padding: 6px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .35);
}
.menu-item {
  border: 0; background: transparent; color: var(--text); text-align: left;
  font: inherit; font-size: 14px; padding: 9px 10px; border-radius: 7px; cursor: pointer;
}
.menu-item:hover { background: var(--panel-2); }

/* ---- left-side action rail: a polished, app-style sidebar nav ---------------
   On wide screens this is a fixed card pinned to the left edge, spanning from
   just below the topbar to the bottom of the viewport. It does NOT consume
   vertical space above the board. On narrow screens it falls back to normal
   flow and stacks above the board (see the responsive block). */
.side-rail {
  display: flex; flex-direction: column; align-items: stretch; gap: 6px;
  padding: 12px 16px;
  position: fixed;
  left: 0;
  top: calc(var(--topbar-h) + env(safe-area-inset-top) + 1px);  /* just below topbar + border */
  bottom: 0;
  width: 190px;
  z-index: 20;
  background: var(--color-surface);
  border-right: 1px solid var(--color-border);
}
.play-menu-wrap { position: relative; }

/* Shared inline-icon sizing inside nav buttons. */
.side-rail .ic { width: 18px; height: 18px; flex: none; }
.nav-label { display: flex; align-items: center; gap: 6px; }

/* Every nav control in the rail shares one cohesive button style. */
.side-rail .play-btn,
.side-rail .profile-link {
  display: flex; align-items: center; gap: 6px;
  width: 100%;
  height: 44px;
  padding: 0 12px;
  border-radius: var(--radius-md);
  font-size: 14px; font-weight: 500;
  text-align: left;
  line-height: 1.2;
  border: 1px solid transparent;
  background: transparent;
  color: var(--color-text-primary);
  cursor: pointer;
  transition: var(--transition-fast);
}
.side-rail .profile-link:hover {
  background: var(--color-surface-raised);
  border-color: var(--color-border);
  transform: translateY(-1px);
}
.side-rail .play-btn:active,
.side-rail .profile-link:active { transform: scale(.97); }
/* Active/selected nav item (set via .is-active by the page scripts). */
.side-rail .profile-link.is-active {
  background: rgba(90, 158, 63, .15);
  border-color: var(--color-accent);
  color: var(--color-accent);
}

/* Icons sit in a fixed-width gutter so every label starts at the same x. Each
   nav item carries its own distinct colour (kept on hover/active); the label
   brightens and the row gets a subtle background on hover. */
.side-rail .profile-link { gap: 11px; }
.side-rail .profile-link .ic { color: var(--color-text-muted); transition: color .15s ease; }
.side-rail .profile-link:hover { color: var(--color-text-primary); }
#daily-link .ic        { color: #5a9e3f; }  /* green  */
#puzzles-link .ic      { color: #a987d6; }  /* violet */
#profile-link .ic      { color: #4a90d9; }  /* blue   */
#leaderboard-link .ic  { color: #d4a017; }  /* gold   */
#tournaments-link .ic  { color: #e0864a; }  /* orange */
#rank-link .ic         { color: #00e5a8; }  /* emerald (echoes the rank ladder) */

/* "Coming soon" nav item: looks present but disabled; reveals a tooltip on
   hover/focus. Keep pointer-events so the tooltip still shows over a disabled
   button (native disabled buttons swallow hover on some engines). */
.side-rail .profile-link.is-soon {
  position: relative;
  opacity: .5;
  cursor: not-allowed;
  pointer-events: auto;
}
.side-rail .profile-link.is-soon:hover,
.side-rail .profile-link.is-soon:focus-visible {
  background: transparent;
  border-color: transparent;
  transform: none;
}
.side-rail .profile-link.is-soon::after {
  content: attr(data-soon);
  position: absolute;
  left: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  padding: 4px 8px;
  border-radius: var(--radius-sm, 6px);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  color: var(--color-text-primary);
  font-size: 12px; font-weight: 600;
  box-shadow: 0 4px 12px rgba(0, 0, 0, .35);
  opacity: 0;
  pointer-events: none;
  transition: opacity .15s ease;
  z-index: 50;
}
.side-rail .profile-link.is-soon:hover::after,
.side-rail .profile-link.is-soon:focus-visible::after { opacity: 1; }

/* Play: the primary green CTA, same shape/size as the rest. */
.side-rail .play-btn {
  justify-content: space-between;
  font-weight: 700;
  background: var(--color-accent);
  color: #fff;
  margin-bottom: 2px;
  box-shadow: 0 2px 8px rgba(90, 158, 63, .25);
}
.side-rail .play-btn:hover {
  background: var(--color-accent-hover);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(90, 158, 63, .35);
}
/* Chevron rotates to point up when the menu is open. */
.side-rail .play-btn .chevron { transition: transform .2s ease; }
.side-rail .play-btn[aria-expanded="true"] .chevron { transform: rotate(180deg); }

/* Play dropdown: a polished dark card with an entrance animation. */
.play-menu {
  position: absolute; left: 0; top: calc(100% + 6px); z-index: 30;
  display: flex; flex-direction: column; min-width: 180px; padding: 6px;
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  animation: menu-in .16s ease-out;
}
.play-menu .menu-item { padding: 12px 12px; border-radius: var(--radius-sm); }
@keyframes menu-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Grouped option blocks (difficulty / colour) under a labelled divider. */
.side-group { display: flex; flex-direction: column; }
.side-divider { border: none; border-top: 1px solid var(--color-border); margin: 12px 0; }
.side-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--color-text-muted); font-weight: 600; margin-bottom: 8px;
}
/* Side segmented controls fill the rail width with equal-width buttons so all
   options (Easy/Medium/Hard) fit inside the 190px panel. */
.side-seg { align-self: stretch; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; }
.side-seg .seg-btn { padding: 7px 4px; text-align: center; }
/* Settings button pinned to the bottom of the rail. */
.side-rail-settings { margin-top: auto; }

.matchmaking-fill { width: 30%; animation: mm-indeterminate 1.3s ease-in-out infinite; }
@keyframes mm-indeterminate {
  0% { margin-left: 0%; width: 15%; }
  50% { width: 55%; }
  100% { margin-left: 85%; width: 15%; }
}

/* ---- layout ---- */
.layout {
  flex: 1;
  display: grid;
  /* Three columns: a left spacer that mirrors the 320px side panel, the board
     in the middle, and the panel on the right. The matching left/right columns
     keep the BOARD itself centred in the viewport regardless of whether the
     panel is visible (pre-game vs in-game). The fixed side-rail floats over the
     left spacer. */
  /* Col 1 reserves the fixed 190px rail. The board (auto) and panel (auto) sit
     side by side and are centred together by the two flexible 1fr spacer
     columns, so the panel hugs the board instead of floating off to the right.
     align-items:center vertically centres the shorter panel against the board. */
  grid-template-columns: 190px 1fr auto auto 1fr;
  gap: 20px;
  padding: 12px 28px;
  align-items: center;
}
/* Board + panel form a centred cluster (cols 3 and 4). */
.layout > .board-wrap { grid-column: 3; }
.layout > .panel,
.layout > .review-panel { grid-column: 4; }

/* ---- board ---- */
.board-wrap {
  display: flex; flex-direction: column; align-items: center; gap: 8px; width: 100%;
  /* One source of truth for the board's edge length, shared by the board frame
     and the player bars above/below it (so they line up). The height term keeps
     the board + BOTH bars (+ the online emote bar) within the viewport so they
     never need scrolling — the site footer is allowed to fall just below the
     fold. ~210px reserves: topbar 52 + layout padding 24 + two bars ~70 + gaps
     + emote bar ~44. The dvh line overrides the vh fallback where supported. */
  --board-size: min(72vw, 880px, calc(100vh - 216px));
  --board-size: min(72vw, 880px, calc(100dvh - 216px));
}
/* The frame is a sized square (labels overlay its gutters). Giving it an
   explicit, definite width breaks the circular sizing dependency the board
   alone would otherwise have. */
.board-frame {
  position: relative;
  /* Use viewport-relative terms (vh/vw) rather than 100%, so the frame has a
     definite size even inside the flex .board-row (a parent-percentage there
     collapses to ~0).
     The height term subtracts the chrome above/below the board (topbar +
     layout padding + a little breathing room ≈ 190px) so the square always
     fits the viewport height without forcing a scroll. dvh tracks mobile
     browser UI; vh is the fallback for browsers without dvh.
     The right-hand side panel needs ~320px, so on wider screens also cap by
     the available width so the board never crowds it off the page. */
  /* Subtract the real vertical chrome so the board + topbar + footer fit the
     viewport exactly (no tiny perpetual overflow): topbar 52 + footer ~69 +
     layout padding 56 ≈ 180px, plus a little breathing room. */
  width: var(--board-size);
  flex: none;
  aspect-ratio: 1 / 1;
  /* reserve 22px gutters on the left (ranks) and bottom (files) */
  padding: 0 0 22px 22px;
  /* No text/element selection on the playing surface: dragging a finger to aim a
     wall must never paint the browser's blue selection highlight or pop the
     iOS callout menu. */
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
/* Belt-and-suspenders: also kill selection on every board layer + the labels. */
.board, .board > div, .labels { -webkit-user-select: none; user-select: none; }
.board {
  position: relative;
  width: 100%;
  height: 100%;
  background: var(--board-edge);
  border-radius: 10px;
  /* Finished frame: a thin dark edge ring + a faint inner highlight, over a
     deep ambient shadow so the board reads as a solid object lifted off page. */
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, .5),
    0 12px 44px rgba(0, 0, 0, .55),
    inset 0 0 0 1px rgba(255, 255, 255, .05);
  padding: 0;
  /* The board must NOT restrict scrolling — it fills most of the screen, so any
     restriction here (even pan-y) lets Chromium swallow wheel/scroll gestures
     that start over it, trapping the page. Default (auto) lets wheel + touch
     scroll pass straight through to the page. Pawns opt into `none` for drag,
     and the board locks to `none` only WHILE a pawn is being dragged. */
  touch-action: auto;
}
.board.is-dragging { touch-action: none; }
.board > div { position: absolute; inset: 0; }

.cells {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-template-rows: repeat(9, 1fr);
  gap: calc(var(--gap-ratio) * (100% / 9.6));
  padding: calc(var(--gap-ratio) * (100% / 9.6));
}
.cell {
  border-radius: 4px;
  position: relative;
}
.cell.light { background: var(--cell-light); }
.cell.dark  { background: var(--cell-dark); }
/* Goal rows (top + bottom): a touch darker so the objective edges stand out. */
.cell.goal-row.light { background: var(--goal-light); }
.cell.goal-row.dark  { background: var(--goal-dark); }
.cell.last-from, .cell.last-to { box-shadow: inset 0 0 0 9999px var(--last); }

.walls-layer, .overlay-layer, .pawns-layer, .preview-layer { pointer-events: none; }

/* Idle-state overlay: a single "Play now" CTA centered on the empty board. */
.board-cta { display: flex; align-items: center; justify-content: center; z-index: 5; padding: 14px; }
.play-now-btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; height: 52px; padding: 0 24px;
  border-radius: var(--radius-md);
  font-size: 16px; font-weight: 700; letter-spacing: .01em;
  box-shadow: 0 4px 16px rgba(90, 158, 63, .35);
}
.play-now-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(90, 158, 63, .45); }
.play-now-btn:active { transform: scale(.98); }

/* Idle landing card: explains the game for first-time / logged-out visitors. */
.landing-card {
  max-width: min(420px, 92%); text-align: center; padding: 40px 36px;
  background: rgba(30, 30, 30, .96);
  border: 1px solid var(--color-border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card); backdrop-filter: blur(8px);
}
.landing-logo { display: inline-flex; color: var(--color-accent); line-height: 0; }
.landing-title { margin: 12px 0 10px; font-size: 2rem; font-weight: 800; letter-spacing: -.02em; }
.landing-text { color: var(--color-text-muted); font-size: 15px; line-height: 1.6; margin: 0 0 22px; }
.landing-links { margin-top: 16px; display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap; }
.landing-dot { color: var(--color-text-muted); }

/* Tutorial coach banner (one move + one wall). */
.coach {
  position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: 95;
  display: flex; align-items: center; gap: 12px; max-width: 92vw;
  background: var(--accent); color: #16210d; font-weight: 700; font-size: 14px;
  padding: 12px 16px; border-radius: 12px; box-shadow: var(--shadow);
}
.coach.hidden { display: none; }
.coach-step { background: rgba(0, 0, 0, .18); border-radius: 999px; padding: 2px 9px; font-size: 12px; font-weight: 800; }
.coach-text { line-height: 1.4; }
.coach-skip { border: 0; background: rgba(0, 0, 0, .16); color: #16210d; font: inherit; font-weight: 700; padding: 6px 12px; border-radius: 8px; cursor: pointer; }
.coach-skip:hover { background: rgba(0, 0, 0, .26); }
.coach-next { border: 0; background: #16210d; color: #fff; font: inherit; font-weight: 700; padding: 6px 14px; border-radius: 8px; cursor: pointer; }
.coach-next:hover { background: #000; }
.coach-next.hidden { display: none; }

/* legal-move dots / rings drawn into overlay-layer */
.move-dot {
  position: absolute;
  border-radius: 50%;
  /* Bright fill with a dark contrast ring so it pops on both light and dark
     cells; a soft glow gently pulses to draw the eye. */
  background: rgba(255, 255, 255, .78);
  transform: translate(-50%, -50%);
  transition: transform .1s;
  animation: dot-glow 1.5s ease-in-out infinite;
}
.move-dot.jump {
  background: transparent;
  border: 4px solid rgba(255, 255, 255, .85);
  box-sizing: border-box;
}
.move-dot:hover { transform: translate(-50%, -50%) scale(1.15); }
@keyframes dot-glow {
  0%, 100% { box-shadow: 0 0 0 2px rgba(20, 36, 12, .5), 0 0 6px 1px rgba(255, 255, 255, .35); }
  50%      { box-shadow: 0 0 0 2px rgba(20, 36, 12, .55), 0 0 13px 3px rgba(255, 255, 255, .6); }
}
/* On touch/coarse pointers, expand the tappable area well beyond the visible
   dot (generated content is hit-tested as part of the element) so move targets
   are comfortably finger-sized without enlarging the dot graphic itself. */
@media (pointer: coarse) {
  .move-dot::after {
    content: ""; position: absolute; inset: -14px; border-radius: 50%;
  }
  /* Touch users get the Rotate/Place/Cancel bar at any viewport width. */
  .touch-controls { display: flex; }
}

/* pawns */
.pawn {
  position: absolute;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: left .22s cubic-bezier(.22,.61,.36,1), top .22s cubic-bezier(.22,.61,.36,1);
  box-shadow: inset 0 2px 3px rgba(255,255,255,.35);
  /* Drop-shadow (vs box-shadow) hugs the round silhouette so the piece reads as
     lifted off the board. */
  filter: drop-shadow(0 2px 4px rgba(0,0,0,.5));
  cursor: default;
  z-index: 5;
}
.pawn.p1 { background: radial-gradient(circle at 35% 30%, #ffffff, var(--p1)); border: 3px solid var(--p1-ring); }
.pawn.p2 { background: radial-gradient(circle at 35% 30%, #7eaef0, var(--p2)); border: 3px solid var(--p2-ring); }
/* Pawns capture the touch gesture for dragging (the board itself stays pan-y so
   the page can scroll from anywhere else on the board). */
.pawn.movable { cursor: grab; touch-action: none; }
.pawn.movable:active { cursor: grabbing; }
.pawn.dragging { transition: none; z-index: 20; box-shadow: 0 10px 22px rgba(0,0,0,.5); }
.pawn.selected { outline: 3px solid var(--legal); outline-offset: 2px; }

/* walls */
.wall {
  position: absolute;
  border-radius: 5px;
  /* Raised, slightly bevelled slab: drop shadow for lift + a top highlight and
     bottom shade so it reads as a solid wall sitting over the cells. */
  box-shadow:
    0 4px 8px rgba(0, 0, 0, .5),
    inset 0 1px 0 rgba(255, 255, 255, .35),
    inset 0 -2px 3px rgba(0, 0, 0, .28);
  transform: translate(-50%, -50%);
}
.wall.owner1 { background: linear-gradient(180deg, #f3d488, var(--wall1)); }
.wall.owner2 { background: linear-gradient(180deg, #6ba0e6, var(--wall2)); }
.wall.last-wall { outline: 3px solid var(--last); }
/* The most-recently placed wall drops into place. */
@keyframes wall-drop {
  from { transform: translate(-50%, -50%) scale(.55); opacity: .2; }
  to   { transform: translate(-50%, -50%) scale(1);   opacity: 1; }
}
.wall.last-wall { animation: wall-drop .22s cubic-bezier(.22,.61,.36,1); }

/* wall preview */
.wall-preview {
  position: absolute;
  border-radius: 5px;
  transform: translate(-50%, -50%);
  opacity: .92;
  z-index: 15;
}
.wall-preview.legal { animation: wall-preview-pulse 1.1s ease-in-out infinite; }
/* High-contrast preview: the wall colour with a dark outline + coloured glow so
   it stands out clearly against the cells while hovering a groove. */
.wall-preview.legal.owner1   { background: var(--wall1); box-shadow: 0 0 0 2px rgba(0,0,0,.4), 0 0 12px 2px rgba(212,160,23,.7); }
.wall-preview.legal.owner2   { background: var(--wall2); box-shadow: 0 0 0 2px rgba(0,0,0,.4), 0 0 12px 2px rgba(74,144,217,.7); }
.wall-preview.illegal { background: var(--illegal); opacity: .6; box-shadow: 0 0 0 2px rgba(0,0,0,.4); animation: none; }

/* ARMED (touch): a wall that's been tapped but NOT placed yet — a clearly
   "pending" ghost so it's obvious it's only showing where the wall *could* go.
   We translucent the fill, add a dashed bright outline, and breathe a soft glow
   so it reads as provisional rather than a solid, committed wall. The dashed
   ring is the key tell: real/placed walls are never dashed. */
.wall-preview.armed.legal {
  background: transparent;
  border: 2px dashed rgba(255, 255, 255, .9);
  animation: wall-armed-pulse 1.1s ease-in-out infinite;
}
.wall-preview.armed.legal.owner1 {
  background: color-mix(in srgb, var(--wall1) 45%, transparent);
  box-shadow: 0 0 0 2px rgba(0,0,0,.45), 0 0 14px 3px rgba(212,160,23,.65);
}
.wall-preview.armed.legal.owner2 {
  background: color-mix(in srgb, var(--wall2) 45%, transparent);
  box-shadow: 0 0 0 2px rgba(0,0,0,.45), 0 0 14px 3px rgba(74,144,217,.65);
}
.wall-preview.armed.illegal {
  background: transparent; opacity: 1;
  border: 2px dashed var(--illegal);
  box-shadow: 0 0 0 2px rgba(0,0,0,.4);
}
@keyframes wall-preview-pulse {
  0%, 100% { opacity: .7; }
  50%      { opacity: 1; }
}
@keyframes wall-armed-pulse {
  0%, 100% { opacity: .78; }
  50%      { opacity: 1; }
}
@keyframes status-pulse {
  0%, 100% { transform: scale(1);   opacity: 1;  }
  50%      { transform: scale(1.5); opacity: .5; }
}

/* ---- skeleton / shimmer loading placeholders ---- */
.skeleton {
  position: relative; overflow: hidden;
  background: var(--color-surface-raised); border-radius: 6px;
}
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.06), transparent);
  transform: translateX(-100%); animation: shimmer 1.3s infinite;
}
@keyframes shimmer { 100% { transform: translateX(100%); } }
/* A leaderboard skeleton row mirrors the real .lb-row layout. */
.lb-skel-row { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
.lb-skel-rank { width: 22px; height: 22px; border-radius: 50%; }
.lb-skel-name { height: 14px; flex: 1; }
.lb-skel-elo  { width: 44px; height: 14px; }
.lb-skel-wl   { width: 96px; height: 12px; }

/* labels */
.labels { position: absolute; display: grid; color: rgba(232, 232, 224, .72); font-size: 12px; font-weight: 700; }
.labels.ranks {
  left: 0; top: 0; bottom: 22px; width: 22px;
  grid-template-rows: repeat(9, 1fr);
}
.labels.ranks span { display: flex; align-items: center; justify-content: center; }
.labels.files {
  left: 22px; right: 0; bottom: 0; height: 22px;
  grid-template-columns: repeat(9, 1fr);
}
.labels.files span { display: flex; align-items: center; justify-content: center; }

/* ---- side panel ---- */
.panel {
  width: 320px;
  display: flex; flex-direction: column; gap: 14px;
}
.player-card {
  position: relative;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 14px 16px;
  overflow: hidden;
  transition: border-color .2s, box-shadow .2s;
}
/* The to-move card gets an accent border + a 3px left accent bar. */
.player-card.active {
  border-color: var(--color-accent);
  box-shadow: inset 3px 0 0 var(--color-accent);
}

/* ===== chess.com-style player bars (above & below the board) ============= */
/* Full-board-width horizontal strips: avatar, name, ELO, connection on the
   left; walls-left and clock on the right. Hidden until a game is live, and
   during review (which uses its own panel). */
.seat-bar {
  display: none;
  width: var(--board-size);
  box-sizing: border-box;
  flex-direction: row; align-items: center; gap: 6px;
  padding: 5px 12px; margin: 0;
}
.board-wrap.in-game .seat-bar { display: flex; }
body.review-mode .seat-bar { display: none; }

/* Stacking order: top bar, board, bottom bar, then the control rows.
   .seats-flipped swaps the two bars so "you" stay at the bottom when the board
   is flipped to your perspective. */
.board-wrap > #card-2 { order: 1; }
.board-wrap > .board-row { order: 2; }
.board-wrap > #card-1 { order: 3; }
.board-wrap > #review-nav { order: 4; }
.board-wrap > .touch-controls { order: 5; }
.board-wrap > .emote-bar { order: 6; }
.board-wrap.seats-flipped > #card-1 { order: 1; }
.board-wrap.seats-flipped > #card-2 { order: 3; }

/* Bar internals */
.seat-bar .player-name {
  font-weight: 700; font-size: 13px;
  /* Username + rank sit in a row. The group only takes the width it needs (it
     won't grow to eat the bar), and it won't be squeezed below its content —
     so the name always shows in full for normal usernames. */
  flex: 0 1 auto; min-width: 0;
  display: inline-flex; align-items: baseline; gap: 5px;
}
/* The username shows in full up to 20 characters (the account name cap) and
   only ellipsizes beyond that — never collapsing to nothing. The rank label
   keeps its full width beside it. */
.seat-bar .player-name > .seat-name-link,
.seat-bar .player-name > a,
.seat-bar .player-name { white-space: nowrap; }
.seat-bar .player-name > .seat-name-link,
.seat-bar .player-name > a {
  max-width: 20ch; overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto;
}
.seat-rank { flex: none; }
.seat-rank-sep { flex: none; color: var(--text-dim); font-size: 11px; opacity: .7; }
.seat-bar .seat-avatar { width: 22px; height: 22px; font-size: 11px; }
.seat-bar .pawn-chip { width: 8px; height: 8px; }
.seat-bar .conn-dot { margin-left: 3px; width: 8px; height: 8px; }
.seat-bar .seat-elo { font-size: 11px; padding: 0 5px; }
.seat-bar .seat-stake { font-size: 10.5px; margin-left: 1px; }
.seat-bar .turn-flag { margin-left: 5px; font-size: 10px; padding: 1px 6px; }
.seat-bar .bar-walls { margin-left: auto; display: flex; align-items: center; gap: 6px; flex: none; }
.seat-bar .walls-row { margin-top: 0; flex-wrap: nowrap; gap: 2px; }
.seat-bar .walls-row .fence-icon { width: 6px; height: 6px; border-radius: 1px; }
.seat-bar .walls-count { font-size: 13px; min-width: 2ch; text-align: right; }
.seat-bar .player-time { font-size: 14px; padding: 2px 7px; }
.seat-bar .player-time::before { content: none; }   /* drop the clock glyph to save width */

/* Symmetric turn state: the active player's whole bar gets a green glow + the
   "To move" chip (rendered by the JS); the waiting player's bar dims, and its
   idle clock is muted so the running clock stands out. */
.seat-bar.active {
  border-color: var(--color-accent);
}
.seat-bar:not(.active) { opacity: .8; }
.seat-bar:not(.active) .player-time { opacity: .5; }
/* Walls-left pips echo each player's WALL colour on the board (gold = player 1,
   blue = player 2); spent pips dim rather than disappear. */
.seat-bar .fence-icon.off { background: rgba(255, 255, 255, .12); }
.player-id { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 15px; }
.pawn-chip { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex: none; }
.pawn-chip.p1 { background: var(--color-cream); border: 1px solid rgba(0,0,0,.25); }
.pawn-chip.p2 { background: var(--color-blue); border: 1px solid rgba(0,0,0,.25); }
/* "To move" badge: a small green pill, only rendered when the flag has text. */
.turn-flag {
  margin-left: auto;
  font-size: 11px; font-weight: 600;
  color: var(--color-accent);
  background: rgba(90, 158, 63, .15);
  border-radius: 10px; padding: 2px 8px;
}
.turn-flag:empty { display: none; }

.walls-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: 12px;
}
.walls-label { font-size: 11px; font-weight: 600; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: .08em; }
.walls-count { font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.card-1 .walls-count, #count-1 { color: var(--color-gold); }
#count-2 { color: var(--color-blue); }
/* Wall pips: 10 individual square tiles; spent slots read as faint empties. */
.walls-row { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; min-height: 8px; }
.fence-icon {
  width: 8px; height: 8px; border-radius: 2px;
  background: rgba(255,255,255,.12);
  transition: transform .25s ease, opacity .25s ease;
}
.walls-row.p1 .fence-icon.on { background: var(--color-gold); }
.walls-row.p2 .fence-icon.on { background: var(--color-blue); }
/* Spent slot: stays as a faint empty tile (the base background). The colour
   transition gives a brief fade as a wall flips from .on to spent. */
.fence-icon.off { background: rgba(255,255,255,.12); }

.status-line {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  text-align: center; font-weight: 600; font-size: 14px;
  color: var(--color-accent);
  padding: 10px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md);
}
/* Pulsing dot before the status text. */
.status-line::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--color-accent); flex: none;
  animation: status-pulse 1.6s ease-in-out infinite;
}
.status-line.thinking { color: var(--color-accent-hover); }

.history-box {
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md);
  display: flex; flex-direction: column; max-height: 220px;
}
.history-head { padding: 10px 14px; font-weight: 700; font-size: 13px; color: var(--color-text-muted); border-bottom: 1px solid var(--color-border); }
.history-list {
  margin: 0; padding: 6px 10px; list-style: none; overflow-y: auto;
  display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px;
  font-size: 13px; font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-variant-numeric: tabular-nums;
  /* thin rounded scrollbar */
  scrollbar-width: thin; scrollbar-color: var(--color-border) transparent;
}
.history-list::-webkit-scrollbar { width: 8px; }
.history-list::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: 999px; }
.history-list::-webkit-scrollbar-track { background: transparent; }
.history-list li { padding: 3px 6px; color: var(--color-text-primary); border-radius: 4px; }
/* Alternating row tint (every other move pair) for readability. */
.history-list li:nth-child(4n+1),
.history-list li:nth-child(4n+2) { background: rgba(255,255,255,.03); }
.history-list li .num { color: var(--color-text-muted); margin-right: 6px; }
.history-list li { cursor: default; transition: background .12s; }
.history-list li:hover { background: rgba(90,158,63,.14); }

/* Move-history hover highlight drawn on the board. */
.hist-hl { position: absolute; transform: translate(-50%, -50%); pointer-events: none; z-index: 14; }
.hist-hl-cell {
  border-radius: 6px; box-sizing: border-box;
  border: 3px solid var(--color-accent);
  background: rgba(90,158,63,.22);
  animation: hist-hl-in .14s ease-out;
}
.hist-hl-wall { border-radius: 5px; background: rgba(90,158,63,.8); box-shadow: 0 0 0 2px rgba(90,158,63,.4); animation: hist-hl-in .14s ease-out; }
@keyframes hist-hl-in { from { opacity: 0; } to { opacity: 1; } }

.controls { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.controls .primary { grid-column: 1 / -1; height: 44px; }
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: 1px solid var(--color-border); background: var(--color-surface-raised); color: var(--color-text-primary);
  font: inherit; font-weight: 600; height: 40px; padding: 0 14px; border-radius: var(--radius-md); cursor: pointer;
  transition: var(--transition-fast);
}
.btn:hover { background: #383838; transform: translateY(-1px); }
.btn:active { transform: scale(.98); }
.btn.primary { background: var(--color-accent); color: #fff; border-color: transparent; box-shadow: 0 2px 8px rgba(90,158,63,.25); }
.btn.primary:hover { background: var(--color-accent-hover); box-shadow: 0 4px 12px rgba(90,158,63,.35); }
.btn.danger:hover { background: #c0392b; border-color: #c0392b; color: #fff; }
.btn.big { height: 52px; padding: 0 26px; font-size: 16px; }

/* Review game: a secondary outlined button. */
.review-entry { background: transparent; }
.review-entry:hover { background: var(--color-surface-raised); }

/* Copy-link confirmation state (checkmark swap). */
.btn.is-copied { color: var(--color-accent); border-color: var(--color-accent); }

/* Loading state: hide the label, show a centered spinner, lock interaction. */
.btn.is-loading, .lobby-join-row .btn.is-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn.is-loading > * { visibility: hidden; }
.btn.is-loading::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 16px; height: 16px; margin: -8px 0 0 -8px;
  border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;
  border-radius: 50%; animation: btn-spin .6s linear infinite;
  visibility: visible;
}
/* On outlined/secondary buttons the white spinner needs an accent tint. */
.btn:not(.primary).is-loading::after { border-color: rgba(255,255,255,.2); border-top-color: var(--color-accent); }
@keyframes btn-spin { to { transform: rotate(360deg); } }

.hint { font-size: 13px; color: var(--color-text-muted); line-height: 1.6; }
/* Show the input-appropriate hint: mouse copy by default, touch copy on coarse
   pointers (phones/tablets). */
.hint-touch { display: none; }
@media (pointer: coarse) {
  .hint-mouse { display: none; }
  .hint-touch { display: block; }
}
kbd {
  background: var(--color-surface-raised); border: 1px solid var(--color-border); border-bottom-width: 2px;
  border-radius: 5px; padding: 1px 6px; font-size: 11px; font-family: ui-monospace, Menlo, Consolas, monospace;
  color: var(--color-text-primary); line-height: 1;
}

/* ---- right panel: one cohesive card with divided sections ---- */
.play-card {
  display: flex; flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
/* Section dividers between the stacked parts (status / moves / actions / hint). */
.play-card > * { border-top: 1px solid var(--color-border); }
.play-card > :first-child { border-top: 0; }
/* Flatten inner blocks so they read as sections of ONE card, not nested cards. */
.play-card .status-line {
  background: transparent; border: 0; border-radius: 0;
  justify-content: flex-start; padding: 12px 14px;
}
.play-card .history-box { background: transparent; border: 0; border-radius: 0; }
.play-card .controls { padding: 12px 14px; grid-template-columns: 1fr; }
.play-card .hint { padding: 12px 14px; margin: 0; }
.play-card .review-entry { border-radius: 0; border-left: 0; border-right: 0; border-bottom: 0; }

/* Resign reads as danger before hover too (Undo stays neutral). */
.btn.danger { color: #e0796b; }

/* Empty Moves state. */
.history-list:empty::before {
  content: "No moves yet — make a move to begin.";
  grid-column: 1 / -1;
  color: var(--color-text-muted); font-size: 13px; padding: 8px 6px;
  font-family: var(--font-ui);
}

/* ---- modal overlays (site-wide) ---- */
.overlay-screen {
  position: fixed; inset: 0; background: rgba(0,0,0,.65);
  display: flex; align-items: center; justify-content: center; z-index: 100;
  backdrop-filter: blur(4px);
  padding: 20px;
}
.overlay-screen.hidden { display: none; }
.win-card {
  position: relative;
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 36px; text-align: center; box-shadow: 0 20px 60px rgba(0,0,0,.6);
  max-width: 440px; margin: auto;
  animation: rise .2s ease-out;
  /* Stay usable on short/mobile screens: scroll the card rather than pushing
     its actions off-screen. */
  max-height: 90vh; overflow-y: auto;
}
@keyframes rise { from { transform: translateY(12px) scale(.97); opacity: 0; } to { transform: none; opacity: 1; } }
/* Inner menu-screen swaps (e.g. lobby choice -> share) fade+slide in. */
#lobby-choice, #lobby-share { animation: menu-swap .2s ease-out; }
@keyframes menu-swap { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.win-emoji { font-size: 52px; line-height: 1; }
.win-trophy { display: inline-flex; color: var(--color-gold); }
.svg-emoji { display: inline-flex; color: var(--color-accent); }
.win-title { margin: 10px 0 22px; font-size: 1.6rem; font-weight: 800; }

/* ---- end-of-game result popup (win/lose) ----
   A larger, more polished version of the shared win-card, scoped so it never
   affects the lobby/help/analyze cards. A coloured top accent + soft glow tints
   the whole thing win-green or loss-red from the local player's perspective. */
.win-result-card {
  max-width: 480px; padding: 40px 40px 32px;
  border-top: 4px solid var(--color-border);
  box-shadow: 0 24px 70px rgba(0, 0, 0, .65);
}
.win-result-card.is-win {
  border-top-color: var(--color-accent);
  box-shadow: 0 24px 70px rgba(0, 0, 0, .65), 0 0 0 1px color-mix(in srgb, var(--color-accent) 35%, transparent), 0 -1px 40px color-mix(in srgb, var(--color-accent) 22%, transparent);
}
.win-result-card.is-loss {
  border-top-color: var(--illegal);
  box-shadow: 0 24px 70px rgba(0, 0, 0, .65), 0 0 0 1px color-mix(in srgb, var(--illegal) 30%, transparent), 0 -1px 40px color-mix(in srgb, var(--illegal) 18%, transparent);
}
/* Bigger, bolder title; tinted to match the outcome. */
.win-result-card .win-title { margin: 14px 0 6px; font-size: 2rem; letter-spacing: -.01em; }
.win-result-card.is-win .win-title { color: var(--accent-strong); }
.win-result-card.is-loss .win-title { color: var(--illegal); }
/* The trophy turns into a muted "broken" tone on a loss so the icon reads too. */
.win-result-card.is-loss .win-trophy { color: var(--text-dim); }
.win-result-card .win-emoji { font-size: 60px; }
/* Short "how it ended" line under the title. */
.win-reason { margin: 0 0 20px; color: var(--text-dim); font-size: 15px; font-weight: 500; }

/* Close (X) button, top-right of the result card. */
.win-close {
  position: absolute; top: 14px; right: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--color-border); background: var(--color-surface-raised);
  color: var(--text-dim); cursor: pointer; transition: var(--transition-fast);
}
.win-close:hover { color: var(--color-text-primary); border-color: var(--color-accent); transform: rotate(90deg); }
/* Win celebration: the trophy pops in. Scoped to the win screen only. */
@keyframes win-pop {
  0%   { transform: scale(.4); opacity: 0; }
  60%  { transform: scale(1.18); }
  100% { transform: scale(1); opacity: 1; }
}
#win-screen:not(.hidden) .win-emoji { animation: win-pop .5s cubic-bezier(.2,.8,.3,1.2); }

/* ---- toast ---- */
.toast {
  position: fixed; top: 64px; right: 20px; left: auto; transform: translateY(-8px);
  background: var(--color-surface); color: var(--color-text-primary);
  border: 1px solid var(--color-border); border-left: 3px solid var(--color-accent);
  padding: 12px 18px; border-radius: var(--radius-md); font-weight: 600; font-size: 14px;
  box-shadow: var(--shadow-card); opacity: 0; transition: opacity .2s, transform .2s; z-index: 120;
  max-width: min(340px, calc(100vw - 40px));
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast.error { border-left-color: #c0392b; }
.toast.hidden { display: none; }

/* shake for illegal feedback */
.shake { animation: shake .32s; }
@keyframes shake {
  0%,100% { transform: translateX(0); }
  20% { transform: translateX(-6px); } 40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); } 80% { transform: translateX(4px); }
}
.touch-controls { display: none; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap; }
.pill-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text-primary);
  font: inherit; font-weight: 600; padding: 9px 16px; border-radius: 999px; cursor: pointer;
  transition: var(--transition-fast);
}
.pill-btn:hover { background: var(--color-surface-raised); }
/* Touch wall confirm/cancel bar (shown only while a wall preview is armed). */
.wall-actions { display: flex; gap: 10px; }
.wall-actions.hidden { display: none; }
.pill-btn.confirm { background: var(--accent); color: #16210d; border-color: transparent; font-weight: 800; }
.pill-btn.confirm:disabled { opacity: .45; cursor: not-allowed; }
.pill-btn.cancel { background: var(--panel-2); }

/* =========================================================================
   Analysis / review mode
   ========================================================================= */
.board-row { display: flex; align-items: stretch; gap: 12px; justify-content: center; }

/* eval bar — vertical, P1 fills from the bottom (their pawn starts at bottom) */
.eval-bar {
  position: relative;
  width: 18px;
  align-self: stretch;
  border-radius: 6px;
  overflow: hidden;
  background: var(--p2);          /* top = P2 colour */
  box-shadow: var(--shadow);
  /* match the board's square height via the frame */
  min-height: 100%;
}
.eval-bar.hidden { display: none; }
.eval-fill-top { position: absolute; top: 0; left: 0; right: 0; background: var(--p2); height: 50%; transition: height .35s cubic-bezier(.22,.61,.36,1); }
.eval-fill-bot { position: absolute; bottom: 0; left: 0; right: 0; background: var(--p1); height: 50%; transition: height .35s cubic-bezier(.22,.61,.36,1); }
.eval-mid { position: absolute; top: 50%; left: 0; right: 0; height: 2px; background: rgba(0,0,0,.35); transform: translateY(-50%); pointer-events: none; }

/* review panel */
.review-panel.hidden { display: none; }
.review-head { padding: 2px 2px 4px; }
.review-title { font-weight: 800; font-size: 18px; }
.review-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }

.summary-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 14px 16px; display: flex; flex-direction: column; gap: 12px;
}
.summary-row { display: flex; align-items: center; gap: 9px; font-weight: 700; }
.summary-name { flex: 1; }
.summary-acc { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--accent-strong); }
.summary-tally { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }
.tally { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--line); }

/* classification colors */
.c-best       { color: #9bd66b; }
.c-excellent  { color: #86c25a; }
.c-good       { color: #9a958c; }
.c-inaccuracy { color: #e6c64a; }
.c-mistake    { color: #e08c3a; }
.c-blunder    { color: #d65a4a; }
.c-great      { color: #5ec0e6; }
.c-miss       { color: #d65a4a; }
.tally.c-blunder { border-color: #6e3531; background: #3a211e; }
.tally.c-mistake { border-color: #6e5331; background: #3a3020; }
.tally.c-inaccuracy { border-color: #6e6531; background: #3a3820; }
.tally.c-best, .tally.c-good { border-color: #3f5a2c; background: #233019; }

/* eval graph */
.graph-box { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 10px 12px; }
.graph-head { font-size: 12px; font-weight: 700; color: var(--text-dim); margin-bottom: 6px; }
.eval-graph { width: 100%; height: 110px; display: block; cursor: pointer; }
.graph-zero { stroke: rgba(255,255,255,.18); stroke-width: 1; stroke-dasharray: 3 3; }
.graph-line { fill: none; stroke: #cfcabf; stroke-width: 1.6; stroke-linejoin: round; }
.graph-dot { stroke: #1d1b19; stroke-width: .6; }
.graph-dot.c-best { fill: #9bd66b; } .graph-dot.c-excellent { fill: #86c25a; }
.graph-dot.c-good { fill: #9a958c; } .graph-dot.c-inaccuracy { fill: #e6c64a; }
.graph-dot.c-mistake { fill: #e08c3a; } .graph-dot.c-blunder { fill: #d65a4a; }
.graph-dot.c-great { fill: #5ec0e6; } .graph-dot.c-miss { fill: #d65a4a; }
.graph-cursor { stroke: var(--accent-strong); stroke-width: 1.4; opacity: .8; }

.review-caption {
  font-size: 13px; font-weight: 600; text-align: center; padding: 10px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 10px; min-height: 38px;
}

/* review move list */
.review-moves { max-height: 240px; }
.review-list { margin: 0; padding: 6px 8px; list-style: none; overflow-y: auto; }
.review-move {
  display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-radius: 7px;
  cursor: pointer; font-size: 13px; font-variant-numeric: tabular-nums;
}
.review-move:hover { background: var(--panel-2); }
.review-move.active { background: #38342f; outline: 1px solid var(--accent); }
.rm-num { color: var(--text-dim); width: 26px; }
.rm-chip { width: 13px; height: 13px; border-radius: 50%; flex: none; }
.rm-chip.p1 { background: var(--p1); border: 1.5px solid var(--p1-ring); }
.rm-chip.p2 { background: var(--p2); border: 1.5px solid var(--p2-ring); }
.rm-note { flex: 1; }
.rm-badge { font-weight: 800; font-size: 13px; min-width: 18px; text-align: right; }

/* step controls — they live under the board but only appear in review mode. */
.step-controls { display: none; }
body.review-mode .step-controls {
  display: flex; justify-content: center; gap: 8px;
  width: 100%; max-width: 360px; margin: 2px auto 0;
}
.step-controls .btn.step {
  flex: 1 1 0; min-width: 56px; min-height: 44px; padding: 10px 0; font-size: 18px;
}
/* In review mode the gameplay touch / emote bars aren't relevant — hide them so
   only the move-navigation arrows sit under the board. */
body.review-mode .touch-controls,
body.review-mode .emote-bar { display: none; }
.review-entry.hidden { display: none; }

/* ghost best-move overlay */
.ghost-target {
  position: absolute; transform: translate(-50%, -50%); border-radius: 50%;
  border: 3px dashed var(--accent-strong); background: rgba(140,177,91,.16);
  box-sizing: border-box; z-index: 16;
}
.ghost-wall {
  position: absolute; transform: translate(-50%, -50%); border-radius: 5px;
  background: repeating-linear-gradient(45deg, rgba(140,177,91,.85) 0 6px, rgba(140,177,91,.45) 6px 12px);
  outline: 2px solid var(--accent-strong); z-index: 16;
}

/* progress overlay */
.progress-track { width: 280px; height: 12px; background: var(--panel-2); border: 1px solid var(--line); border-radius: 999px; overflow: hidden; margin: 6px auto 10px; }
.progress-fill { height: 100%; width: 0%; background: var(--accent); transition: width .12s; }
.progress-label { font-size: 13px; color: var(--text-dim); }

.win-actions { display: flex; flex-direction: column; gap: 10px; align-items: stretch; }

/* ---- player clock + record ---- */
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* Per-player countdown clock (ranked, 5-minute games). Prominent and tabular so
   it reads at a glance; the side on move is highlighted and low time pulses. */
.player-time {
  font-variant-numeric: tabular-nums; font-weight: 800; font-size: 21px;
  color: var(--text); background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 9px; padding: 4px 12px; letter-spacing: .02em; line-height: 1.1;
}
.player-time::before { content: "⏱ "; font-size: 12px; opacity: .6; font-weight: 400; }
.player-card.active .player-time {
  border-color: var(--accent); color: #fff; background: rgba(127, 166, 80, .18);
}
.player-time.low { color: var(--illegal); border-color: var(--illegal); }
@keyframes clock-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .5; } }
.player-card.active .player-time.low { animation: clock-pulse 1s ease-in-out infinite; }
.win-sub { color: var(--text-dim); font-size: 14px; margin: -12px 0 18px; }

/* Animated ranked rating on the win screen. */
#win-record { display: flex; align-items: baseline; justify-content: center; gap: 10px; }
.rating-label { color: var(--color-text-muted); font-size: 13px; text-transform: uppercase; letter-spacing: .08em; }
.rating-value { font-size: 28px; font-weight: 800; font-variant-numeric: tabular-nums; }
.rating-delta {
  font-size: 14px; font-weight: 700; border-radius: 999px; padding: 2px 9px;
  animation: delta-in .4s .25s both ease-out;
}
.rating-delta.up { color: var(--color-accent); background: rgba(90,158,63,.15); }
.rating-delta.down { color: #e07a6a; background: rgba(192,57,43,.18); }
@keyframes delta-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ---- lobby seat choice + spectating ---- */
.lobby-seg { width: 100%; }
.lobby-seg .seg-btn { flex: 1; }
.spectate-banner {
  position: fixed; top: 12px; left: 50%; transform: translateX(-50%); z-index: 90;
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--color-accent); color: #fff; font-weight: 700; font-size: 13px;
  padding: 6px 14px; border-radius: 999px; box-shadow: var(--shadow-card);
}
.spectate-banner.hidden { display: none; }

/* ---- how to play ---- */
.help-card { position: relative; max-width: min(520px, 92vw); text-align: left; }
.help-card .win-emoji, .help-card .win-title { text-align: center; }
.help-list { margin: 8px 0 22px; padding: 0; list-style: none; }
.help-list li {
  position: relative; padding: 10px 0 10px 22px; font-size: 14.5px; line-height: 1.5;
  color: var(--text); border-top: 1px solid var(--line);
}
.help-list li:first-child { border-top: 0; }
.help-list li::before {
  content: "▸"; position: absolute; left: 2px; top: 10px; color: var(--accent-strong);
}
.help-list b { color: #fff; }
.help-list kbd {
  background: var(--color-surface-raised); border: 1px solid var(--color-border); border-radius: 5px;
  padding: 1px 6px; font-size: 12px; font-family: inherit;
}
.help-book { display: inline-flex; color: var(--color-accent); }

/* ---- settings: full-page panel ---- */
/* Override the centred-modal layout: the settings sheet fills the viewport. */
.settings-page { align-items: stretch; justify-content: center; padding: 0; }
.settings-sheet {
  display: flex; flex-direction: column;
  width: 100%; max-width: 640px; max-height: 100vh; height: 100%;
  background: var(--color-surface);
  border-left: 1px solid var(--color-border); border-right: 1px solid var(--color-border);
  box-shadow: var(--shadow-card);
  animation: settings-in .22s ease-out;
}
@keyframes settings-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.settings-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 24px; border-bottom: 1px solid var(--color-border); flex: none;
}
.settings-title { margin: 0; font-size: 20px; font-weight: 800; }
.settings-header .lobby-close { position: static; }
.settings-body { padding: 8px 24px 28px; overflow-y: auto; flex: 1; }
.settings-block { padding: 18px 0; border-bottom: 1px solid var(--color-border); }
.settings-block:last-child { border-bottom: 0; }
.settings-block-title {
  margin: 0 0 12px; font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--color-text-muted); font-weight: 700;
}
.settings-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 10px 0; min-height: 40px;
}
.settings-row-col { flex-direction: column; align-items: stretch; gap: 10px; }
.settings-row.is-disabled { opacity: .45; pointer-events: none; }
.settings-row-label { font-size: 15px; font-weight: 500; display: flex; flex-direction: column; gap: 2px; }
.settings-hint-text { font-size: 12.5px; font-weight: 400; color: var(--color-text-muted); }
.settings-soon {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--color-text-muted); border: 1px solid var(--color-border);
  border-radius: 6px; padding: 1px 5px; margin-left: 6px; vertical-align: middle;
}
.settings-range { width: 140px; accent-color: var(--color-accent); }

/* iOS-style toggle switch. */
.toggle {
  position: relative; width: 44px; height: 26px; flex: none;
  border-radius: 999px; border: 1px solid var(--color-border);
  background: var(--color-surface-raised); cursor: pointer; padding: 0;
  transition: background .2s, border-color .2s;
}
.toggle[aria-checked="true"] { background: var(--color-accent); border-color: var(--color-accent); }
.toggle-knob {
  position: absolute; top: 2px; left: 2px; width: 20px; height: 20px;
  border-radius: 50%; background: #fff; transition: transform .2s;
  box-shadow: 0 1px 3px rgba(0,0,0,.4);
}
.toggle[aria-checked="true"] .toggle-knob { transform: translateX(18px); }

.settings-label {
  display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--color-text-muted); font-weight: 600; margin-bottom: 10px;
}
.theme-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.theme-swatch {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 8px 4px; border-radius: var(--radius-md); cursor: pointer;
  background: transparent; border: 1px solid var(--color-border); color: var(--color-text-muted);
  font: inherit; font-size: 12px; transition: var(--transition-fast);
}
.theme-swatch:hover { background: var(--color-surface-raised); transform: translateY(-1px); }
.theme-swatch[aria-checked="true"] {
  border-color: var(--color-accent); color: var(--color-text-primary);
  background: rgba(90,158,63,.1);
}
/* Mini board preview: a 2x2 checker plus an edge ring. */
.theme-preview {
  width: 38px; height: 38px; border-radius: 7px; border: 3px solid var(--e);
  background:
    linear-gradient(135deg, var(--a) 0 50%, var(--b) 50% 100%);
  background-size: 100% 100%;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.15);
}
.settings-seg { width: 100%; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; }
.settings-seg .seg-btn.is-active { background: var(--color-accent); color: #fff; }
/* Keyboard-shortcut reference row in the help modal. */
.help-shortcuts {
  margin: 4px 0 22px; padding: 14px 16px; text-align: left;
  background: var(--color-surface-raised); border: 1px solid var(--color-border); border-radius: var(--radius-md);
}
.help-shortcuts-title {
  display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--color-text-muted); font-weight: 600; margin-bottom: 10px;
}
.kbd-row { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--color-text-primary); }
.kbd-row + .kbd-row { margin-top: 8px; }
.kbd-row span { display: inline-flex; gap: 4px; }

/* ---- online lobby ---- */
.lobby-card { position: relative; min-width: 320px; max-width: 92vw; }
.lobby-close {
  position: absolute; top: 12px; right: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 50%;
  border: 0; background: transparent;
  color: var(--color-text-muted); font-size: 18px; cursor: pointer; line-height: 1;
  transition: var(--transition-fast);
}
.lobby-close:hover { color: var(--color-text-primary); background: var(--color-surface-raised); }
.lobby-sub { color: var(--text-dim); margin: -8px 0 18px; font-size: 14px; }
.lobby-field { display: block; text-align: left; margin: 0 0 16px; }
.lobby-field-label {
  display: block; color: var(--text-dim); font-size: 12px; margin-bottom: 6px;
  text-transform: uppercase; letter-spacing: .07em;
}
.lobby-field .lobby-input { text-align: left; letter-spacing: 0; width: 100%; box-sizing: border-box; }
.lobby-or { color: var(--text-dim); font-size: 13px; margin: 14px 0 10px; text-transform: uppercase; letter-spacing: .08em; }
.lobby-join-row, .lobby-link-row { display: flex; gap: 8px; }
.lobby-input {
  flex: 1; min-width: 0; height: 44px; box-sizing: border-box;
  background: rgba(255,255,255,.05); border: 1px solid var(--color-border);
  color: var(--color-text-primary); border-radius: var(--radius-sm); padding: 0 12px; font-size: 14px;
  font-family: inherit; text-align: center; letter-spacing: .12em;
  transition: var(--transition-fast);
}
.lobby-input:focus {
  outline: none; border-color: var(--color-border-focus);
  box-shadow: 0 0 0 3px rgba(100,180,80,.15);
}
.lobby-code {
  font-size: 34px; font-weight: 800; letter-spacing: .22em; margin: 6px 0 16px;
  color: var(--color-accent);
}
.lobby-link-row .lobby-input { text-align: left; letter-spacing: 0; font-size: 13px; }
.lobby-waiting { color: var(--text-dim); font-size: 14px; margin-top: 18px; }
.lobby-error { color: var(--illegal); font-size: 13px; margin-top: 12px; }
.lobby-error.hidden { display: none; }

/* ---- sign in / sign up ---- */
.auth-card { position: relative; min-width: 320px; max-width: 92vw; }
.auth-card .lobby-field { margin-bottom: 14px; }
.auth-switch { margin-top: 16px; font-size: 13px; color: var(--text-dim); }

/* ---- profile ---- */
.profile-card { position: relative; min-width: 340px; max-width: 92vw; text-align: left; }
.profile-head { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.profile-avatar {
  width: 56px; height: 56px; border-radius: 14px; flex: none;
  display: grid; place-items: center; font-size: 26px; font-weight: 800;
  background: var(--accent); color: #16210d; overflow: hidden;
}

/* Editable avatar on your own profile: a hover "Change" overlay. */
.profile-avatar.editable { cursor: pointer; position: relative; }
.profile-avatar.editable .avatar-edit-overlay {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .5); color: #fff; font-size: 10px; font-weight: 700;
  opacity: 0; transition: opacity .15s ease; text-align: center; border-radius: inherit;
}
.profile-avatar.editable:hover .avatar-edit-overlay { opacity: 1; }
.avatar-actions { display: flex; gap: 10px; margin: 4px 0 14px; font-size: 13px; }
.avatar-actions button {
  background: none; border: none; padding: 0; cursor: pointer; font: inherit;
  color: var(--accent-strong); font-weight: 600;
}
.avatar-actions button:hover { text-decoration: underline; }
.avatar-actions .danger { color: var(--illegal); }

/* Small avatar circle for leaderboard rows. */
.lb-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%; flex: none; overflow: hidden;
  background: var(--accent); color: #16210d; font-size: 12px; font-weight: 800;
}
.profile-id .win-title { margin: 0; text-align: left; }
.profile-rating { color: var(--text-dim); font-size: 15px; }
.profile-rating span:first-child { color: var(--accent-strong); font-weight: 800; font-size: 18px; }
.profile-stats { display: flex; gap: 10px; margin-bottom: 18px; }
.pstat {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 10px; padding: 12px 8px;
}
.pstat-num { font-size: 22px; font-weight: 800; }
.pstat-label { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .06em; }
.profile-games { max-height: 280px; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.game-row {
  display: grid; grid-template-columns: 44px 1fr auto; align-items: center;
  gap: 8px 10px; padding: 9px 12px; border-radius: 9px;
  background: var(--panel-2); border-left: 3px solid var(--line);
}
.game-row.win { border-left-color: var(--accent); }
.game-row.loss { border-left-color: var(--illegal); }
.game-result { font-weight: 800; font-size: 13px; }
.game-row.win .game-result { color: var(--accent-strong); }
.game-row.loss .game-result { color: var(--illegal); }
.game-opp { font-weight: 600; }
.game-delta { font-weight: 800; font-variant-numeric: tabular-nums; }
.game-row.win .game-delta { color: var(--accent-strong); }
.game-row.loss .game-delta { color: var(--illegal); }
/* Casual (unranked) games show a neutral "Casual" chip instead of an ELO delta
   — they're in the history but never moved the rating, so the colour is muted
   regardless of win/loss. */
.game-delta.casual,
.game-row.win .game-delta.casual,
.game-row.loss .game-delta.casual {
  color: var(--text-dim); font-weight: 700; font-size: 11px;
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px;
}
.game-meta { grid-column: 2 / 4; color: var(--text-dim); font-size: 12px; }
.profile-empty { color: var(--text-dim); font-size: 14px; padding: 18px 0; text-align: center; }

/* ---- online: seat ELO + connection dot + quick-chat emotes ---- */
.player-id { position: relative; }
.seat-elo {
  font-size: 12px; font-weight: 700; color: var(--text-dim);
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 6px; padding: 0 6px;
}
.seat-elo.hidden { display: none; }

/* Rating at stake for the local player this ranked game: "+N / −M". Sits right
   after their ELO; the up value is win-green, the down value loss-red, kept
   compact so it reads naturally inline on desktop and mobile alike. */
.seat-stake {
  display: inline-flex; align-items: baseline; gap: 3px; flex: none;
  font-size: 11px; font-weight: 800; font-variant-numeric: tabular-nums;
  line-height: 1; white-space: nowrap;
}
.seat-stake.hidden { display: none; }
.seat-stake .stake-up { color: var(--accent-strong); }
.seat-stake .stake-down { color: var(--illegal); }
.seat-stake .stake-sep { color: var(--text-dim); opacity: .6; font-weight: 600; }
.conn-dot {
  width: 9px; height: 9px; border-radius: 50%; margin-left: auto; flex: none;
  background: var(--illegal); box-shadow: 0 0 0 2px rgba(0, 0, 0, .15);
}
.conn-dot.on { background: var(--accent-strong); }

.emote-bar { position: relative; display: flex; justify-content: center; margin-top: 8px; }
.emote-menu {
  position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px; z-index: 40;
  width: max-content; max-width: 92vw;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 8px; box-shadow: var(--shadow);
}
.emote-item {
  border: 1px solid var(--line); background: var(--panel-2); color: var(--text);
  font: inherit; font-size: 14px; padding: 8px 12px; border-radius: 8px; cursor: pointer; white-space: nowrap;
}
.emote-item:hover { border-color: var(--accent); }

.player-card { position: relative; }
.emote-bubble {
  position: absolute; top: -14px; right: 12px; z-index: 20;
  background: var(--accent); color: #16210d; font-weight: 700; font-size: 13px;
  padding: 6px 12px; border-radius: 12px; box-shadow: var(--shadow);
  opacity: 0; transform: translateY(6px) scale(.96); pointer-events: none;
  transition: opacity .18s, transform .18s;
}
.emote-bubble.show { opacity: 1; transform: translateY(0) scale(1); }

/* ---- player rank label + leaderboard ----
   The rank shown beside a username (Scout … Grand Architect). Rendered by
   tiers.js, which sets the per-rank brand colour on `--rank`. Design goals:
   bold, slightly smaller than the username it sits next to, in its rank colour,
   with a subtle glow — and legible on any surface, light or dark.

   Legibility trick: the visible glyph colour is the rank colour lifted slightly
   toward white so even the darker bronze (Scout) reads on a dark panel, while
   the layered text-shadow pairs a coloured glow with a faint dark halo — the
   halo keeps the light ranks (Silver/Gold) readable should they ever sit on a
   light surface. `currentColor`-independent so it works wherever it's dropped. */
/* Searching player's standing on the matchmaking card. */
.mm-rank { margin: 2px 0 6px; font-weight: 700; font-size: 15px; }
.mm-rank.hidden { display: none; }

.rank-label {
  font-weight: 800;
  font-size: 0.82em;            /* slightly smaller than the adjacent username */
  letter-spacing: .2px;
  white-space: nowrap; vertical-align: baseline;
  color: color-mix(in srgb, var(--rank, #9a958c) 85%, #ffffff);
  text-shadow:
    0 0 6px color-mix(in srgb, var(--rank, #9a958c) 50%, transparent),
    0 1px 1px rgba(0, 0, 0, .4);
}
/* Light-theme safety net (no light theme ships today, but keep it correct):
   drop the white lift and lean on the brand colour + dark halo for contrast. */
@media (prefers-color-scheme: light) {
  .rank-label {
    color: color-mix(in srgb, var(--rank, #9a958c) 88%, #000000);
    text-shadow:
      0 0 5px color-mix(in srgb, var(--rank, #9a958c) 40%, transparent),
      0 0 1px rgba(0, 0, 0, .35);
  }
}
.lb-list { display: flex; flex-direction: column; gap: 5px; margin-top: 10px; }
.lb-row {
  display: grid; grid-template-columns: 42px 1fr auto; align-items: center;
  gap: 4px 12px; padding: 10px 12px; border-radius: 10px; text-decoration: none;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text);
}
a.lb-row:hover { border-color: var(--accent); }
.lb-row.me { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.lb-rank { font-weight: 800; font-size: 15px; text-align: center; font-variant-numeric: tabular-nums; color: var(--text-dim); }
.lb-rank-1, .lb-rank-2, .lb-rank-3 { font-size: 19px; }
.lb-name { font-weight: 700; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lb-elo { font-weight: 800; font-variant-numeric: tabular-nums; color: var(--accent-strong); }
.lb-wl { grid-column: 2 / 4; color: var(--text-dim); font-size: 12px; }

/* ---- ranks page: "you are here" panel + the seven-rung ladder ---- */

/* The signed-in player's current standing: rank, ELO, and a progress bar to the
   next rank. Tinted with the player's current rank colour (--rank). */
.rank-you {
  margin: 20px 0 8px;
  padding: 18px 20px;
  border-radius: 16px;
  background:
    linear-gradient(135deg, color-mix(in srgb, var(--rank, var(--color-accent)) 16%, transparent), transparent 60%),
    var(--panel-2);
  border: 1px solid color-mix(in srgb, var(--rank, var(--color-accent)) 40%, var(--line));
  box-shadow: 0 8px 26px color-mix(in srgb, var(--rank, var(--color-accent)) 14%, transparent);
}
.rank-you[hidden] { display: none; }
.rank-you-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 12px;
}
.rank-you-label { font-size: 16px; color: var(--text-dim); font-weight: 600; }
.rank-you-name {
  font-weight: 800;
  color: color-mix(in srgb, var(--rank) 85%, #ffffff);
  text-shadow: 0 0 10px color-mix(in srgb, var(--rank) 45%, transparent), 0 1px 1px rgba(0,0,0,.4);
}
.rank-you-elo {
  font-size: 24px; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums;
}
.rank-you-elo-unit { font-size: 13px; font-weight: 600; color: var(--text-dim); }
/* Progress track from the current rank's floor to the next rank's threshold. */
.rank-you-bar {
  height: 10px; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--rank) 12%, rgba(255,255,255,.06));
  border: 1px solid color-mix(in srgb, var(--rank) 30%, transparent);
}
.rank-you-fill {
  height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--rank) 70%, #ffffff), var(--rank));
  box-shadow: 0 0 12px color-mix(in srgb, var(--rank) 60%, transparent);
  transition: width .5s cubic-bezier(.22,.61,.36,1);
}
.rank-you-next { margin-top: 9px; font-size: 13.5px; color: var(--text-dim); }
.rank-you-next b { color: var(--text); font-variant-numeric: tabular-nums; }
.rank-you-nextname { font-weight: 800; color: color-mix(in srgb, var(--rank) 85%, #ffffff); }
.rank-you-max { color: var(--accent-strong); font-weight: 600; }
/* Guest variant: prompt + sign-in button on one comfortable row. */
.rank-you-guest {
  display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  --rank: var(--color-accent);
}
.rank-you-guest .rank-you-msg { font-size: 15px; color: var(--text); font-weight: 500; }

/* ---- the seven-rung ladder as tall colour columns ----
   Each column is tinted by its rank colour (set on `--rank` by ranks-page.js).
   Built so adding a rank just adds a column — the row is a flex track that wraps
   on narrow screens. Each column has a coloured "fill" that grows with rank, so
   the row reads as an ascending climb, with the rank name + ELO range below. */
.rank-ladder {
  display: flex; gap: 10px; align-items: stretch;
  margin-top: 16px;
}
.rank-col {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column;
  border-radius: 14px; overflow: hidden;
  background: color-mix(in srgb, var(--rank) 10%, var(--panel-2));
  border: 1px solid color-mix(in srgb, var(--rank) 45%, transparent);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--rank) 16%, transparent);
  transition: transform .18s ease, box-shadow .18s ease;
}
.rank-col:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 26px color-mix(in srgb, var(--rank) 30%, transparent);
}
/* The player's CURRENT rank: lifted, ringed and glowing so it pops out of the
   row, with a small "You are here" flag on its coloured bar. */
.rank-col.is-current {
  transform: translateY(-6px);
  background: color-mix(in srgb, var(--rank) 20%, var(--panel-2));
  border-color: color-mix(in srgb, var(--rank) 75%, transparent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--rank) 70%, transparent),
              0 14px 32px color-mix(in srgb, var(--rank) 38%, transparent);
}
.rank-col.is-current:hover { transform: translateY(-9px); }
.rank-col-you {
  position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
  white-space: nowrap;
  font-size: 9.5px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: #fff; background: rgba(0,0,0,.55);
  padding: 3px 8px; border-radius: 999px;
  border: 1px solid rgba(255,255,255,.35);
  box-shadow: 0 2px 8px rgba(0,0,0,.4);
}
/* The tall coloured bar. A vertical gradient in the rank colour; its height is
   the per-rank `--rank-fill` so higher ranks stand taller. */
.rank-col-bar {
  position: relative;
  height: var(--rank-fill, 60%); min-height: 150px;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--rank) 92%, #ffffff) 0%,
    var(--rank) 45%,
    color-mix(in srgb, var(--rank) 78%, #000000) 100%);
  display: flex; align-items: flex-start; justify-content: center;
}
.rank-col-tier {
  margin-top: 12px;
  font-weight: 800; font-size: 22px; line-height: 1;
  color: #fff; text-shadow: 0 1px 3px rgba(0, 0, 0, .45);
  font-variant-numeric: tabular-nums;
}
/* When the "You are here" flag is shown, drop the tier number below it. */
.rank-col.is-current .rank-col-tier { margin-top: 32px; }
.rank-col-of { font-size: 12px; font-weight: 700; opacity: .7; }
.rank-col-body { padding: 12px 6px 16px; text-align: center; }
.rank-col-name {
  font-weight: 800; font-size: 14.5px; line-height: 1.15; margin-bottom: 5px;
  color: color-mix(in srgb, var(--rank) 86%, #ffffff);
  text-shadow: 0 0 8px color-mix(in srgb, var(--rank) 45%, transparent),
               0 1px 1px rgba(0, 0, 0, .4);
}
.rank-col-elo {
  font-weight: 700; font-size: 12px; color: var(--text-dim);
  font-variant-numeric: tabular-nums; line-height: 1.3;
}
.rank-col-elo-unit { opacity: .7; font-weight: 600; }

/* Narrow screens: let the columns wrap to two-ish per row and grow their min
   width so names/ELO stay readable instead of squeezing into 7 across. */
@media (max-width: 720px) {
  .rank-ladder { flex-wrap: wrap; gap: 12px; align-items: stretch; }
  .rank-col { flex: 1 1 40%; min-width: 130px; }
  .rank-col-bar { height: auto; min-height: 90px; }
}

/* profile: rank line + achievements */
.profile-rankline { color: var(--text-dim); font-size: 13px; margin: -8px 0 10px; }
.profile-rankline b { color: var(--accent-strong); }
.recent-opps { color: var(--text-dim); font-size: 12.5px; margin: 0 0 16px; }
.opp-chip {
  display: inline-block; background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 999px; padding: 1px 9px; margin: 2px 2px 0; color: var(--text); font-weight: 600;
  text-decoration: none;
}
a.opp-chip:hover { border-color: var(--accent); color: var(--accent-strong); }

/* Clickable player names: recent-game opponent + live-game seat names. */
.opp-link, .seat-name-link {
  color: inherit; text-decoration: none; cursor: pointer;
  border-bottom: 1px dotted color-mix(in srgb, currentColor 45%, transparent);
}
.opp-link:hover, .seat-name-link:hover {
  color: var(--accent-strong); border-bottom-color: var(--accent-strong);
}
/* Trophy grid. Catalog order is easiest -> hardest, and the wrap flows
   left-to-right then down, so the easiest trophies sit top-left and the hardest
   end bottom-right. */
.achievements { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 18px; }
.ach {
  display: flex; align-items: center; gap: 7px; padding: 8px 11px; border-radius: 10px;
  background: var(--panel-2); border: 1px solid var(--line); font-size: 12.5px; font-weight: 600;
  cursor: default; transition: transform .12s ease, box-shadow .12s ease;
}
.ach .ach-ico { font-size: 16px; line-height: 1; }
/* Locked: greyed and dimmed so earned ones stand out, but still readable as a
   "to chase" list. */
.ach.locked { opacity: .42; filter: grayscale(.9); }
/* Earned: gold-tinted, lit, with a subtle glow. */
.ach.unlocked {
  border-color: color-mix(in srgb, var(--color-gold, #e9c46a) 55%, var(--line));
  background: color-mix(in srgb, var(--color-gold, #e9c46a) 12%, var(--panel-2));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-gold, #e9c46a) 22%, transparent),
              0 2px 10px color-mix(in srgb, var(--color-gold, #e9c46a) 18%, transparent);
}
.ach.unlocked:hover { transform: translateY(-2px); }
.ach.unlocked .ach-name { color: color-mix(in srgb, var(--color-gold, #e9c46a) 60%, var(--text)); }
.game-row.replayable { cursor: pointer; }
.game-row.replayable:hover { border-left-color: var(--accent-strong); background: #34302b; }

/* ---- standalone account pages (signin / signup / profile) ---- */
.auth-page {
  min-height: 100vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 22px; padding: 32px 16px;
}
.auth-brand {
  display: inline-flex; align-items: center; gap: 8px; text-decoration: none;
  color: var(--text); font-weight: 800; font-size: 22px;
}
.auth-brand .brand-mark { color: var(--accent-strong); }
.auth-page .auth-card { width: 360px; max-width: 92vw; text-align: center; }
.auth-page .auth-card .win-title { margin-top: 0; }
.auth-or {
  color: var(--text-dim); font-size: 12px; text-transform: uppercase;
  letter-spacing: .1em; margin: 16px 0 14px;
}
.google-btn-wrap { display: flex; justify-content: center; min-height: 44px; }

.profile-page { max-width: 560px; margin: 0 auto; padding: 24px 16px; }
/* The ranks ladder needs room for seven side-by-side columns. */
.ranks-page { max-width: 1040px; }
.profile-topbar {
  display: flex; align-items: center; justify-content: space-between; margin-bottom: 22px;
}
.profile-topbar-actions { display: flex; gap: 8px; }
.profile-page .profile-card { width: 100%; box-sizing: border-box; }
.link-btn {
  border: 0; background: transparent; color: var(--color-accent); cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 500; padding: 0 2px; text-decoration: none;
}
.link-btn:hover { color: var(--color-accent-hover); text-decoration: underline; }

/* ---- static doc pages (about / privacy / terms) ---- */
.doc-page { max-width: 680px; margin: 0 auto; padding: 40px 24px 64px; }
.doc-page h1 { font-size: 28px; margin: 14px 0 4px; }
.doc-page h2 { font-size: 1.1rem; font-weight: 700; margin: 28px 0 8px; color: var(--color-accent); }
.doc-page p, .doc-page li { color: var(--color-text-primary); line-height: 1.65; font-size: 15px; }
.doc-page a { color: var(--color-accent); }
.doc-page a:hover { color: var(--color-accent-hover); }
.doc-meta { color: var(--color-text-muted); font-size: 13px; margin-bottom: 16px; }
.auth-brand { display: inline-flex; align-items: center; gap: 8px; color: var(--color-text-primary); text-decoration: none; font-weight: 700; font-size: 18px; }
.auth-brand .brand-mark { display: inline-flex; color: var(--color-accent); }

/* ---- site footer ---- */
.site-footer {
  border-top: 1px solid var(--color-border); margin-top: auto;
  /* Add the bottom safe-area inset so the links clear the home indicator. */
  padding: 16px max(24px, env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) max(24px, env(safe-area-inset-left));
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: center; gap: 8px 18px; color: var(--color-text-muted); font-size: 13px;
}
.site-footer a, .footer-link {
  color: var(--color-text-muted); text-decoration: none; background: none; border: 0;
  font: inherit; cursor: pointer; padding: 0; transition: color .15s;
}
.site-footer a:hover, .footer-link:hover { color: var(--color-text-primary); text-decoration: underline; }
.footer-sep { opacity: .5; }
/* Feedback: a small outlined pill button with an icon. */
.footer-feedback {
  display: inline-flex; align-items: center; gap: 6px;
  background: transparent; border: 1px solid var(--color-border); color: var(--color-text-muted);
  font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
  padding: 5px 12px; border-radius: 999px; transition: var(--transition-fast);
}
.footer-feedback:hover { color: var(--color-text-primary); border-color: var(--color-text-muted); background: var(--color-surface-raised); }

/* feedback textarea */
.feedback-text {
  width: 100%; min-height: 110px; resize: vertical; box-sizing: border-box;
  background: rgba(255,255,255,.05); border: 1px solid var(--color-border); color: var(--color-text-primary);
  border-radius: var(--radius-sm); padding: 10px 12px; font: inherit; font-size: 14px;
  transition: var(--transition-fast);
}
.feedback-text:focus { outline: none; border-color: var(--color-border-focus); box-shadow: 0 0 0 3px rgba(100,180,80,.15); }

/* ---- responsive ---- */
/* Tablet / small laptop: panel stacks under the board, touch controls appear. */
@media (max-width: 820px) {
  /* Single column: board first, then the info panel, then the action buttons.
     justify-items:center keeps the column centred and matches .panel max-width. */
  .layout { grid-template-columns: 1fr; padding: 16px; gap: 18px; justify-items: center; }
  /* The desktop layout pins these to explicit grid columns (2 and 3). In a
     single-column grid that would spawn phantom implicit columns and shove the
     board off-screen, so collapse everything back into the one real column. */
  .layout > .board-wrap,
  .layout > .panel,
  .layout > .review-panel { grid-column: 1; }
  .panel { width: 100%; max-width: 560px; }
  /* The board is capped by the available WIDTH (hard cap = viewport minus the
     16px layout padding on each side, so there is never horizontal overflow)
     and by the available HEIGHT (so it fits above the fold with room for the
     topbar + touch controls). Safe-area insets are subtracted from the height
     term for notched devices. */
  .board-wrap {
    --board-size: min(600px, calc(100vw - 32px),
               calc(100dvh - 256px - env(safe-area-inset-top) - env(safe-area-inset-bottom)));
  }
  .touch-controls { display: flex; }
  .eval-bar { width: 14px; }
  /* The side rail returns to normal flow and sits inline above the board as a
     single compact, wrapping row of PRIMARY nav only. The secondary in-game
     options (Difficulty / Your colour) are relocated into the panel by
     syncOptionPlacement() in main.js so they don't push the board down. */
  .side-rail {
    position: static; width: auto; bottom: auto;
    background: none; border-right: 0; box-shadow: none;
    flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center;
    padding: 10px 12px 0; gap: 8px;
  }
  .side-rail .play-btn,
  .side-rail .profile-link { width: auto; height: 40px; flex: 0 0 auto; }
  .side-rail-settings { margin-top: 0; }   /* no push-to-bottom in a flat row */
  /* Difficulty/colour groups: inline chips. In the panel they read as a labelled
     row; no top divider needed. */
  .side-group { flex-direction: row; align-items: center; gap: 8px; flex-wrap: wrap; }
  .side-divider { display: none; }
  .side-label { margin: 0 4px 0 0; }
  /* Relocated option groups sit at the top of the panel; keep the segmented
     control able to grow so its tap targets stay finger-sized. */
  .panel > .side-group { width: 100%; }
  .panel > .side-group .side-seg { flex: 1 1 auto; }

  /* Comfortable touch targets (>= ~44px) for the on-screen action buttons. */
  .btn { height: 44px; }
  .pill-btn { min-height: 44px; }
  .controls .primary { height: 44px; }

  /* The Moves list must not grow unbounded and shove the action buttons below
     the fold — cap it and let it scroll internally. */
  .history-box { max-height: 160px; }

  /* ====== In a game: hand the screen to the board ======================
     The full nav rail is only useful between games. While playing, it would
     eat ~150px above the board, so we collapse it off-screen and surface a
     small "Menu" chip in the topbar to bring it back on demand. The board's
     height reservation drops accordingly, so the board grows to fill the gap. */
  body.in-game .nav-toggle { display: inline-flex; }

  /* Collapse the rail into a dropdown anchored just under the topbar. It's taken
     out of flow (position:fixed) so the board claims its old band, and it scrolls
     internally if the menu is taller than the space below the header. */
  body.in-game .side-rail {
    position: fixed; left: 0; right: 0;
    top: calc(var(--topbar-h) + env(safe-area-inset-top) + 1px);
    max-height: calc(100dvh - var(--topbar-h) - env(safe-area-inset-top) - 12px);
    overflow-y: auto; z-index: 25;
    flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center;
    gap: 8px; padding: 12px;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    box-shadow: 0 14px 30px rgba(0, 0, 0, .45);
    transform: translateY(-12px);
    opacity: 0; pointer-events: none;
    transition: transform .22s ease, opacity .22s ease;
  }
  /* Open state (toggled by the Menu chip): slide the rail down into view. */
  body.in-game.nav-open .side-rail {
    transform: translateY(0); opacity: 1; pointer-events: auto;
  }
  /* The relocated Difficulty/Colour groups live in the panel, not the rail, so
     they stay reachable even while the rail is collapsed. */
  body.in-game .side-rail .side-group { display: none; }

  /* With the rail gone, reclaim its vertical band for the board: only the
     topbar + the two player bars + the touch row remain above/below it. */
  body.in-game .layout { padding-top: 12px; }
  body.in-game .board-wrap {
    --board-size: min(600px, calc(100vw - 24px),
               calc(100dvh - 168px - env(safe-area-inset-top) - env(safe-area-inset-bottom)));
  }
}

/* Phones: tighten chrome and give the board the full width. */
@media (max-width: 480px) {
  /* Keep the header on ONE row (don't wrap the icon buttons to a second line);
     scale the controls down to fit instead. Horizontal padding honours the
     safe-area insets so nothing hides behind rounded corners. */
  .topbar { padding: env(safe-area-inset-top) max(12px, env(safe-area-inset-right)) 0 max(12px, env(safe-area-inset-left)); gap: 6px; }
  .brand-name { font-size: 17px; }
  .topbar-account { gap: 4px; min-width: 0; }
  .icon-btn { width: 36px; height: 36px; }
  .icon-btn .ic { width: 18px; height: 18px; }
  .signin-btn { padding: 8px 14px; }
  /* The chip is just avatar + ELO now (name/rank hidden globally), so it stays
     naturally compact — no truncation needed. */
  .account-summary { min-width: 0; }

  /* Seat bars: keep the player name + rank visible (names only clip past 20
     chars). The wall PIPS are the least essential bit, so on the narrowest
     phones they give up space first (the numeric wall count stays). A slightly
     smaller font keeps the name + rank on one row. */
  .seat-bar .player-name { font-size: 12px; }
  .seat-bar .walls-row { display: none; }   /* keep the count, drop the pip strip */
  .seat-bar .bar-walls { gap: 0; }
  /* Board takes the full width (hard width cap = viewport minus 12px padding per
     side) while still fitting the viewport height. */
  .board-wrap {
    --board-size: min(calc(100vw - 24px),
               calc(100dvh - 238px - env(safe-area-inset-top) - env(safe-area-inset-bottom)));
  }
  /* In a game the nav rail is collapsed, so the board reclaims that band — only
     topbar + two player bars + the touch row sit around it. */
  body.in-game .board-wrap {
    --board-size: min(calc(100vw - 16px),
               calc(100dvh - 150px - env(safe-area-inset-top) - env(safe-area-inset-bottom)));
  }
  .layout { padding: 12px; }
  /* Moves list reads better as a single column when space is tight. */
  .history-list { grid-template-columns: 1fr; }
  .win-card { padding: 28px 22px; }
  .win-result-card { padding: 32px 22px 24px; }
  .win-title { font-size: 22px; }
  .win-result-card .win-title { font-size: 26px; }
  /* Settings theme grid: 2x2 on narrow screens. */
  .theme-grid { grid-template-columns: repeat(2, 1fr); }
  /* Sidebar buttons wrap comfortably; let the Play button take a full row. */
  .side-rail .play-btn { flex: 1 1 100%; justify-content: center; }
  /* Toast spans most of the width, centred near the top. */
  .toast { left: 12px; right: 12px; max-width: none; text-align: center; }
}

/* Respect users who prefer reduced motion: drop the tweens/animations but keep
   the app fully functional (pawn/wall positions still update instantly). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
