/* Pai Sho Lab — design system.
   One accent (jade). Garden red/white belong to the board and tiles only —
   never to UI chrome. Dark by default, a light theme follows the system
   setting or the menu's manual toggle (`<html data-theme="dark|light">`). */

:root {
  --bg: #161512;
  --surface: #22201c;
  --surface-2: #2c2a25;
  --text: #e8e4dc;
  --muted: #a39e94;
  --accent: #3fae82;
  --accent-ink: #0d2a1f;
  --danger: #d0615b;
  --garden-red: #b8453c;
  --garden-white: #efe9dc;

  --radius: 10px;
  --gap: 12px;
  --tap: 44px;

  --hairline: rgba(255, 255, 255, 0.08);
  --shadow-lift: 0 10px 28px rgba(0, 0, 0, 0.35);
  --shadow-sheet: 0 -12px 32px rgba(0, 0, 0, 0.4);

  --fs-title: 1.75rem;
  --fs-body: 1rem;
  --fs-label: 0.8125rem;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #f1efe8;
    --surface: #ffffff;
    --surface-2: #e8e4d9;
    --text: #221f1a;
    --muted: #6b655a;
    --accent: #2b8f68;
    --accent-ink: #ffffff;
    --danger: #a93832;
    --hairline: rgba(0, 0, 0, 0.09);
    --shadow-lift: 0 10px 24px rgba(0, 0, 0, 0.12);
    --shadow-sheet: 0 -12px 28px rgba(0, 0, 0, 0.16);
  }
}

:root[data-theme="light"] {
  --bg: #f1efe8;
  --surface: #ffffff;
  --surface-2: #e8e4d9;
  --text: #221f1a;
  --muted: #6b655a;
  --accent: #2b8f68;
  --accent-ink: #ffffff;
  --danger: #a93832;
  --hairline: rgba(0, 0, 0, 0.09);
  --shadow-lift: 0 10px 24px rgba(0, 0, 0, 0.12);
  --shadow-sheet: 0 -12px 28px rgba(0, 0, 0, 0.16);
}

* { box-sizing: border-box; }

body {
  background: var(--bg);
  color: var(--text);
  margin: 0;
  font: 16px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

h1, h2, h3 { line-height: 1.2; margin: 0 0 4px; }

.page {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 16px;
}

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

.num { font-variant-numeric: tabular-nums; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------- top bar */

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--gap);
  padding: 10px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--hairline);
}

.topbar__brand {
  display: flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  color: var(--text);
  font-weight: 650;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}

.topbar__logo {
  width: 28px;
  height: 28px;
  display: block;
}

.topbar__right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ------------------------------------------------------------------- chip */

.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 14px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--hairline);
  color: var(--text);
  font-size: var(--fs-label);
  font-weight: 600;
  cursor: pointer;
}

.chip--level {
  padding: 0;
  width: var(--tap);
  font-size: 1rem;
}

.chip.is-selected {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--surface-2));
}

/* ----------------------------------------------------------------- button */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--tap);
  padding: 0 20px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  font: inherit;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--text);
  background: var(--surface-2);
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.1s ease, filter 0.15s ease, background-color 0.15s ease;
}

@media (hover: hover) and (pointer: fine) {
  .btn:hover { filter: brightness(1.08); }
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; filter: none; }

.btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.btn-secondary {
  background: var(--surface-2);
  border-color: var(--hairline);
  color: var(--text);
}

.btn-quiet {
  background: transparent;
  color: var(--muted);
}
@media (hover: hover) and (pointer: fine) {
  .btn-quiet:hover { background: var(--surface-2); filter: none; color: var(--text); }
}

/* the hamburger menu trigger: its own shape, not a text button */
.menu-btn {
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border: none;
  border-radius: var(--radius);
  background: transparent;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .menu-btn:hover { background: var(--surface-2); }
}
.menu-btn span {
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--text);
}

/* ------------------------------------------------------------------- tile */

.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--gap);
}

.tile {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 8px;
  min-height: 110px;
  width: 100%;
  margin: 0;
  padding: 18px;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  background: var(--surface);
  color: inherit;
  font: inherit;
  text-decoration: none;
  cursor: pointer;
  text-align: left;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

@media (hover: hover) and (pointer: fine) {
  .tile:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-lift);
  }
}

.tile h3 {
  font-size: 1.1rem;
  font-weight: 650;
}

.tile--primary {
  position: relative;
  overflow: hidden;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
}

/* A very subtle garden-colour motif — a thin red/white sliver along the top
   edge of the primary tile. The only place garden colour touches the home
   page's chrome; everywhere else on this page stays jade + neutral. */
.tile--primary::after {
  content: '';
  position: absolute;
  top: 0;
  right: 18px;
  width: 56px;
  height: 4px;
  border-radius: 0 0 3px 3px;
  background: linear-gradient(to right, var(--garden-red) 50%, var(--garden-white) 50%);
  opacity: 0.75;
  pointer-events: none;
}

/* Tiles that pair an icon with a title/subtitle (the three "play a person"
   cards): icon on the left, text to its right, instead of the plain
   top-aligned stack the Lab's tool grid and the Learn card use. */
.home-play .tile {
  flex-direction: row;
  align-items: center;
  gap: 14px;
  min-height: 84px;
}

.tile-icon {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-2);
}

.tile-icon--pair {
  position: relative;
  flex: 0 0 auto;
  width: 46px;
  height: 36px;
}
.tile-icon--pair img {
  position: absolute;
  top: 3px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-2);
  box-shadow: 0 0 0 2px var(--surface);
}
.tile-icon--pair img:first-child { left: 0; z-index: 1; }
.tile-icon--pair img:last-child { left: 16px; }

.tile-text {
  flex: 1 1 auto;
  min-width: 0;
}

.tile-sub { margin: 2px 0 0; }

/* ------------------------------------------------------------------ strip */

.strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  padding: 10px 14px;
  border-radius: var(--radius);
  background: var(--surface-2);
}

.strip__name {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
}

.strip__turn-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--muted);
}

.strip.is-turn .strip__turn-dot {
  background: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
}

/* Who a seat is: an account links to its profile, a guest is only typed text (italic, tagged),
   a bot is tagged. A guest called "MushiBot" must not look official. */
.strip__guest { font-style: italic; color: var(--muted); }
.strip__user { color: inherit; text-decoration: underline dotted; text-underline-offset: 3px; }
.strip__tag {
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 6px;
  border: 1px solid var(--hairline);
  border-radius: 4px;
  color: var(--muted);
}

/* Clocks: display only (the server decides). Tabular figures so the digits don't jitter. */
.strip__clock {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 1.125rem;
  padding: 2px 10px;
  border-radius: 6px;
  background: var(--surface);
  color: var(--muted);
}

.strip__clock.clock--running { color: var(--accent-ink); background: var(--accent); }

.strip__clock.clock--warn { color: color-mix(in srgb, var(--danger) 55%, var(--text)); }

.strip__clock.clock--low {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, var(--surface));
}

.time-control { margin: 0 0 var(--gap); }

.time-control__label { display: block; font-size: var(--fs-label); margin-bottom: 4px; }

.time-control__note { margin: 6px 0 0; font-size: var(--fs-label); }

/* ------------------------------------------------------------------- menu */

.menu-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.menu-list a, .menu-list button {
  display: flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 12px;
  border-radius: 8px;
  border: none;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 1rem;
  font-weight: 500;
  text-decoration: none;
  text-align: left;
  cursor: pointer;
}

/* `display: flex` above would otherwise outrank the UA `[hidden]` rule (Admin, Lab, sign in/out). */
.menu-list a[hidden], .menu-list button[hidden] { display: none; }

@media (hover: hover) and (pointer: fine) {
  .menu-list a:hover, .menu-list button:hover { background: var(--surface-2); }
}

.menu-sep { height: 1px; background: var(--hairline); margin: 8px 4px; }

/* ------------------------------------------------------------------ sheet */

.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
}

.sheet-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

.sheet {
  width: 100%;
  max-height: 80vh;
  overflow: auto;
  padding: 20px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-radius: 16px 16px 0 0;
  box-shadow: var(--shadow-sheet);
  transform: translateY(100%);
  transition: transform 0.22s ease;
}

.sheet-backdrop.is-open .sheet { transform: translateY(0); }

@media (min-width: 640px) {
  .sheet-backdrop { align-items: center; }
  .sheet {
    width: 420px;
    max-height: 85vh;
    border-radius: 16px;
    box-shadow: var(--shadow-lift);
    transform: translateY(12px) scale(0.98);
  }
  .sheet-backdrop.is-open .sheet { transform: translateY(0) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .tile, .btn, .sheet, .sheet-backdrop { transition: none; }
}

/* ------------------------------------------------------------------ board */
/* Board-only colours: the two players' piece/harmony colour, distinct from
   --accent (which means "your turn" / primary action) and from the garden
   red/white that belong to the board's own diamond and gates. */
:root {
  --pb-p1: #5a9ad9;
  --pb-p2: #d9735a;
  --pb-tile-size: 52px;
  --last-move: #e3b93c;     /* the newest move's points, as Lichess's yellow squares */
}

.pb-root {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
}

/* Lichess-style two-column layout once there's room: the board stays the
   large, central element and both hands move into a side panel to its
   right (opponent's hand on top, yours on the bottom) instead of stacking
   above/below the board and eating into its own size budget. */
@media (min-width: 900px) {
  .pb-root {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 240px;
    grid-template-areas: "board handtop" "board handbottom";
    align-items: start;
    gap: 16px;
  }
  .pb-wrap { grid-area: board; }
  .pb-hand[data-slot="top"] { grid-area: handtop; }
  .pb-hand[data-slot="bottom"] { grid-area: handbottom; }
}

.pb-hand {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  max-width: 720px;
}

.pb-hand-row {
  display: flex;
  gap: 6px;
  justify-content: center;
}

/* flex-basis + shrink (not a fixed width, and no flex-wrap) rather than a
   flat width: a narrower container - the Lab-card-padded tutorial board, or
   the ~240px desktop hand panel - shrinks all 6 tiles in the row together
   down to min-width, instead of wrapping one lone tile onto its own line. */
.pb-tile {
  position: relative;
  flex: 0 1 var(--pb-tile-size);
  min-width: 34px;
  aspect-ratio: 1 / 1;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
@media (hover: hover) and (pointer: fine) {
  .pb-tile:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 5px 12px rgba(0, 0, 0, 0.3);
  }
}
.pb-tile:active:not(:disabled) { transform: translateY(0); }
.pb-tile:disabled { opacity: 0.35; cursor: not-allowed; }
.pb-tile--empty { opacity: 0.3; }
.pb-tile--selected {
  box-shadow: 0 0 0 2px var(--accent), 0 0 0 5px color-mix(in srgb, var(--accent) 30%, transparent);
}
/* The harmony/clash indicator: while a basic flower is selected (in a hand
   or on the board), its two Circle-of-Harmony neighbours get a solid green
   ring and its opposite (clashing) flower a dashed red ring - the same
   colour language the Learn page's own circle legend already uses for
   harmony vs. clash. Shown on both players' hands and on board tiles
   regardless of owner, since harmony/clash is about flower identity, not
   who played it. */
.pb-tile--harmony-hint, .pb-piece--harmony-hint {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.pb-tile--clash-hint, .pb-piece--clash-hint {
  outline: 2px dashed var(--danger);
  outline-offset: 2px;
}
.pb-tile__img { display: block; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; pointer-events: none; }
.pb-tile__count {
  position: absolute;
  bottom: -4px;
  right: -4px;
  min-width: 16px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  pointer-events: none;
}

.pb-wrap {
  position: relative;
  width: 100%;
  max-width: 820px;
  min-width: 320px;
  aspect-ratio: 1 / 1;
  margin: 0 auto;
}

.pb-visuals {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 50%;
  background: var(--surface-2);
  /* A real lift off the page (two stacked drop shadows, near + far) plus an
     inner rim bevel, instead of the single 1.5px ring this used to be - the
     board reads as an object sitting on the table, not a flat CSS shape. */
  box-shadow:
    0 16px 36px rgba(0, 0, 0, 0.32),
    0 3px 10px rgba(0, 0, 0, 0.22),
    inset 0 0 0 1.5px color-mix(in srgb, var(--muted) 45%, transparent),
    inset 0 3px 14px rgba(0, 0, 0, 0.16);
}

/* Board lines (drawn by board.js as an SVG in point coordinates): Pai Sho
   is played on the INTERSECTIONS, so every line passes through the centre of
   a cell — never along cell edges. Weighted enough to read as etched into
   the board, not a faint CSS overlay. */
.pb-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.pb-lines line {
  stroke: color-mix(in srgb, var(--muted) 48%, transparent);
  stroke-width: 0.06;
  vector-effect: none;
}
.pb-lines .pb-line--axis { stroke: color-mix(in srgb, var(--muted) 70%, transparent); stroke-width: 0.085; }

.pb-diamond {
  position: absolute;
  left: calc(100% * 2.5 / 19);
  top: calc(100% * 2.5 / 19);
  width: calc(100% * 14 / 19);
  height: calc(100% * 14 / 19);
  /* The vignette (transparent centre, darkened rim) is blended `multiply`
     over the flat red/white quadrants - it reads as a lit surface with real
     depth without touching the quadrants' own hue, and without the glossy
     skeuomorphic highlight a lighten-blend would give. */
  background:
    radial-gradient(circle at 50% 50%, transparent 55%, rgba(0, 0, 0, 0.24) 100%),
    conic-gradient(
      var(--garden-red) 0deg 90deg,
      var(--garden-white) 90deg 180deg,
      var(--garden-red) 180deg 270deg,
      var(--garden-white) 270deg 360deg
    );
  background-blend-mode: multiply, normal;
  clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}

.pb-gate { position: absolute; background: var(--garden-red); opacity: 0.85; }
.pb-gate--n { top: 0; left: calc(100% * 7 / 19); width: calc(100% * 5 / 19); height: calc(100% * 2.5 / 19); clip-path: polygon(0 0, 100% 0, 50% 100%); }
.pb-gate--s { bottom: 0; left: calc(100% * 7 / 19); width: calc(100% * 5 / 19); height: calc(100% * 2.5 / 19); clip-path: polygon(50% 0, 100% 100%, 0 100%); }
.pb-gate--w { left: 0; top: calc(100% * 7 / 19); width: calc(100% * 2.5 / 19); height: calc(100% * 5 / 19); clip-path: polygon(0 0, 100% 50%, 0 100%); }
.pb-gate--e { right: 0; top: calc(100% * 7 / 19); width: calc(100% * 2.5 / 19); height: calc(100% * 5 / 19); clip-path: polygon(100% 0, 100% 100%, 0 50%); }

.pb-grid {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(19, 1fr);
  grid-template-rows: repeat(19, 1fr);
}

.pb-cell {
  position: relative;
  cursor: pointer;
}

/* The intersection marker: present on every one of the 249 valid spaces
   regardless of whether a tile occupies it (a tile's .pb-piece-slot renders
   on top of it). Subtle on purpose — it's a point on the board, not a UI
   affordance competing with hint dots or tiles. */
.pb-point {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16%;
  height: 16%;
  min-width: 3px;
  min-height: 3px;
  margin: -8% 0 0 -8%;
  border-radius: 50%;
  background: transparent;   /* intersections are marked by the crossing lines, as on a real board */
  pointer-events: none;
}

.pb-piece-slot {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pb-cell--hint::after {
  content: '';
  position: absolute;
  inset: 12%;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 28%, transparent);
  border: 2px solid var(--accent);
  pointer-events: none;
  transition: background 0.12s ease, transform 0.12s ease;
  animation: pb-hint-enter 0.15s ease-out;
}
/* A legal-move dot grows slightly under the pointer - the one place on the
   board (besides a piece itself) that responds to hover, since every one of
   the 249 points is clickable but only hinted ones are a legal move right
   now. */
@media (hover: hover) and (pointer: fine) {
  .pb-cell--hint:hover::after {
    background: color-mix(in srgb, var(--accent) 42%, transparent);
    transform: scale(1.15);
  }
}

.pb-piece {
  width: 94%;
  height: 94%;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.3);
  transition: transform 0.12s ease, filter 0.12s ease;
}
@media (hover: hover) and (pointer: fine) {
  .pb-piece:hover { transform: scale(1.06); filter: brightness(1.15); }
}
.pb-piece--p1 { border: 2px solid var(--pb-p1); background: color-mix(in srgb, var(--pb-p1) 30%, var(--surface)); }
.pb-piece--p2 { border: 2px solid var(--pb-p2); background: color-mix(in srgb, var(--pb-p2) 30%, var(--surface)); }
.pb-piece--growing { opacity: 0.7; border-style: dashed; }
.pb-piece--selected { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent); }
.pb-piece__img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; pointer-events: none; }

/* A tile fades and scales up into place the moment it's actually new (see
   board.js's prevTileKeys) - redraws of a tile already there (a selection
   change, a poll tick) never replay this, so the board doesn't flicker. */
.pb-piece--enter { animation: pb-piece-enter 0.22s ease-out; }
@keyframes pb-piece-enter {
  from { transform: scale(0.4); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}
/* The newest move: both of its points are tinted (a plant has only the landing point), under the
   piece. The piece itself slides in from where it came from (board.js sets --dx/--dy in cells),
   and a captured piece fades out beneath it. */
.pb-cell--last-from::before, .pb-cell--last-to::before {
  content: '';
  position: absolute;
  inset: 2%;
  border-radius: 50%;
  background: color-mix(in srgb, var(--last-move) 32%, transparent);
  pointer-events: none;
}
.pb-cell--last-to::before { background: color-mix(in srgb, var(--last-move) 52%, transparent); }
.pb-piece--slide { position: relative; z-index: 6; animation: pb-piece-slide 0.18s ease-out; }
@keyframes pb-piece-slide {
  from { transform: translate(calc(var(--dx) * 106.4%), calc(var(--dy) * 106.4%)); }
  to { transform: none; }
}
.pb-piece--ghost { position: absolute; z-index: 1; pointer-events: none; animation: pb-piece-capture 0.2s ease-out forwards; }
@keyframes pb-piece-capture {
  from { opacity: 0.9; transform: scale(1); }
  to { opacity: 0; transform: scale(0.5); }
}
@keyframes pb-hint-enter {
  from { transform: scale(0.3); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .pb-piece--enter, .pb-piece--slide, .pb-cell--hint::after { animation: none; }
  .pb-piece--ghost { display: none; }
  .pb-piece, .pb-tile, .pb-cell--hint::after { transition: none; }
}

.pb-harmony { position: absolute; inset: 0; pointer-events: none; }
.pb-harmony-line--p1, .pb-harmony-line--p2 {
  stroke-width: 0.12;
  stroke-dasharray: 0.3 0.22;
  stroke-linecap: round;
  opacity: 0.8;
}
.pb-harmony-line--p1 { stroke: var(--pb-p1); }
.pb-harmony-line--p2 { stroke: var(--pb-p2); }
.pb-harmony-line--win { stroke-dasharray: none; stroke-width: 0.16; opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .pb-harmony-line--p1, .pb-harmony-line--p2 { animation: pb-flow 4s linear infinite; }
  @keyframes pb-flow { to { stroke-dashoffset: -1; } }
}

/* -------------------------------------------------------------- game page */

/* ------------------------------------------------------------- game page
   One grid. The board is the page: its cell is a size container, so the board is the largest
   square that fits (100cqmin) with no JavaScript measuring. Phones stack bars around it and
   never scroll; wide screens (and landscape phones) put everything else in a right-hand panel.
   Hands are filled by board.js into #game-hand-top / #game-hand-bottom. */

body.page--game { --topbar-h: 48px; overscroll-behavior: none; }
body.page--game .page { max-width: none; padding: 0; }
body.page--game .topbar {
  position: static;
  height: var(--topbar-h);
  padding: 0 12px;
}

.game {
  --gutter: 12px;
  display: grid;
  /* Pinned to the edges of the visible window (not 100vh/100dvh, which some mobile browsers get
     wrong while their toolbar is showing): the action bar can never be pushed below the fold. */
  position: fixed;
  inset: var(--topbar-h) 0 0 0;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto auto auto minmax(0, 1fr) auto auto auto auto;
  grid-template-areas: "meta" "moves" "topstrip" "tophand" "board" "botstrip" "bothand" "hint" "actions";
  gap: 4px;
  padding: 0 0 max(6px, env(safe-area-inset-bottom));
  overflow: hidden;
}
.game__meta { grid-area: meta; display: flex; gap: 8px; justify-content: center; align-items: center; margin: 0 var(--gutter); }
.game__meta:empty, .game__meta:not(:has(:not([hidden]))) { display: none; }
#game-top-strip { grid-area: topstrip; }
#game-hand-top { grid-area: tophand; }
.game__board { grid-area: board; container-type: size; min-height: 0; display: grid; place-items: center; }
#game-bottom-strip { grid-area: botstrip; }
#game-hand-bottom { grid-area: bothand; }
.game__hint { grid-area: hint; }
.game__actions { grid-area: actions; }
.game__moves { grid-area: moves; display: flex; align-items: center; gap: 6px; min-width: 0; margin-inline: var(--gutter); }
.game__moves:has(#game-move-list:empty) { display: none; }
.game > .strip, .game__hand, .game__hint, .game__actions { margin-inline: var(--gutter); }

.game__board .pb-root {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: none;
  place-items: center;
  width: 100%;
  height: 100%;
  gap: 0;
}
.game__board .pb-wrap {
  grid-area: auto;
  width: calc(100cqmin * var(--board-fit, 1));
  height: calc(100cqmin * var(--board-fit, 1));
  min-width: 0;
  max-width: none;
  margin: 0;
  aspect-ratio: auto;
}
#board-resize { display: none; }

.game__badge {
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: var(--fs-label);
  font-weight: 600;
}

/* Hands, compact (phones): yours is one row of six 44px+ tiles (accents and specials appear only
   on a bonus turn, when they can be played); the opponent's is a thin row of counts. */
.game__hand { display: flex; flex-direction: column; gap: 4px; width: auto; max-width: none; }
.game__hand .pb-hand-row { gap: 6px; justify-content: space-between; }
.game__hand .pb-tile { flex: 1 1 0; min-width: 44px; max-width: 56px; }
/* The accent/special row is always laid out (dimmed and disabled until a bonus turn), so earning a
   harmony never changes the height of the page: the board stays exactly where it was. */
.game__hand .pb-hand-row--other .pb-tile { max-width: 44px; }
#game-hand-bottom:not([data-bonus]) .pb-hand-row--other .pb-tile { opacity: 0.3; }
#game-hand-top .pb-hand-row { justify-content: center; }
#game-hand-top .pb-tile { flex: 0 0 28px; min-width: 0; max-width: none; pointer-events: none; }
#game-hand-top .pb-hand-row--other { display: none; }
#game-hand-top .pb-tile__count { bottom: -3px; right: -3px; min-width: 14px; font-size: 0.6rem; line-height: 14px; }
.game--watching #game-hand-bottom .pb-tile { flex: 0 0 28px; min-width: 0; max-width: none; pointer-events: none; }
.game--watching #game-hand-bottom .pb-hand-row { justify-content: center; }
.game--watching #game-hand-bottom .pb-hand-row--other { display: none; }

.game__hint {
  text-align: center;
  min-height: 22px;
  margin-block: 0;
  font-size: var(--fs-label);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.game__actions { display: flex; flex-wrap: nowrap; gap: 8px; justify-content: center; }
.game__actions .btn {
  flex: 1 1 0;
  min-width: 0;
  max-width: 200px;
  padding: 0 8px;
  font-size: 0.85rem;
  white-space: nowrap;      /* one line: a wrapped label would take board height */
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 430px) { .game__actions .btn { font-size: 0.8rem; padding: 0 4px; } }

/* Strips are bars: name on the left, a big clock on the right. */
.game > .strip { padding: 2px 12px; min-height: 44px; }
.game > .strip .strip__clock { font-size: 1.5rem; padding: 2px 12px; }

@media (max-width: 899px) and (orientation: portrait) {
  /* The slim game header: the global top bar keeps only the logo and the menu. */
  body.page--game .topbar__name,
  body.page--game .topbar [data-nickname-chip],
  body.page--game .topbar [data-account-slot] { display: none; }
}

/* Roomy hands once there is a side panel and the window is tall enough for them. */
@media (min-width: 900px) and (min-height: 600px) {
  .game__hand .pb-tile { flex: 0 1 var(--pb-tile-size); min-width: 40px; max-width: none; }
  .game__hand .pb-hand-row { justify-content: center; }
  #game-hand-bottom:not([data-bonus]) .pb-hand-row--other { display: flex; }
  #game-hand-top .pb-tile { flex: 0 1 var(--pb-tile-size); pointer-events: none; opacity: 0.8; }
  #game-hand-top .pb-hand-row--other { display: flex; }
  #game-hand-top .pb-tile:disabled { opacity: 0.55; }
  .game--watching #game-hand-bottom .pb-tile { flex: 0 1 var(--pb-tile-size); }
  .game--watching #game-hand-bottom .pb-hand-row--other { display: flex; }
}

/* Wide window (or a landscape phone): board on the left at full height, everything else in a
   right-hand panel. */
@media (min-width: 900px), (orientation: landscape) and (max-height: 500px) {
  .game {
    --panel-w: 320px;
    --col-gap: 24px;
    --avail-h: calc(100dvh - var(--topbar-h) - 24px);
    position: static;
    inset: auto;
    height: var(--avail-h);
    margin: 12px auto;
    padding: 0;
    overflow: visible;
    justify-content: center;
    column-gap: var(--col-gap);
    grid-template-columns: min(var(--avail-h), calc(100vw - var(--panel-w) - 3 * var(--col-gap))) var(--panel-w);
    grid-template-rows: auto auto auto minmax(0, 1fr) auto auto auto auto;
    grid-template-areas:
      "board meta" "board tophand" "board topstrip" "board moves"
      "board botstrip" "board bothand" "board hint" "board actions";
  }
  .game > .strip, .game__hand, .game__hint, .game__actions, .game__meta { margin-inline: 0; }
  #board-resize {
    display: block;
    position: absolute;
    right: 0;
    bottom: 0;
    width: 22px;
    height: 22px;
    cursor: nwse-resize;
    touch-action: none;
    background: linear-gradient(135deg, transparent 0 55%, var(--muted) 55% 62%, transparent 62% 72%, var(--muted) 72% 79%, transparent 79%);
    opacity: 0.6;
  }
  #board-resize:hover { opacity: 1; }
  .game__moves { display: flex; flex-direction: column; align-items: stretch; gap: 6px; min-height: 0; width: auto; max-width: none; margin: 0; }
  .game__moves:has(#game-move-list:empty) { display: flex; }
  .game .mv-nav { display: flex; align-items: center; gap: 4px; }
  .game .mv-nav button {
    min-width: 44px;
    min-height: 36px;
    border: 1px solid var(--hairline);
    border-radius: 8px;
    background: var(--surface-2);
    color: var(--text);
    font-size: 1.1rem;
    cursor: pointer;
  }
  .game .mv-nav button:disabled { opacity: 0.4; cursor: default; }
  .game .mv-nav .mv-download { margin-left: auto; }
  .game #game-move-list { flex: 1 1 auto; min-height: 0; flex-direction: column; align-items: stretch; gap: 2px; overflow-x: hidden; overflow-y: auto; white-space: normal; padding: 4px 0; }
  .game .mv-row { display: grid; grid-template-columns: 2.4em minmax(0, 1fr) minmax(0, 1fr); align-items: start; column-gap: 4px; }
  .game .mv-no { padding: 6px 0 0 4px; }
  .game .mv-turn { flex-direction: column; align-items: stretch; gap: 0; }
  .game .mv-cell { text-align: left; }
  .game .mv-bonus { padding-left: 16px; font-size: 0.75rem; }
  .game .mv-live { align-self: flex-start; }
  .game__hint { white-space: normal; text-align: left; min-height: 20px; }
  .game__actions { flex-wrap: wrap; justify-content: flex-start; }
  .game__actions .btn { flex: 0 1 auto; max-width: none; padding: 0 16px; }
  .game__meta { justify-content: flex-start; }
}
@media (orientation: landscape) and (max-height: 500px) {
  .game__moves, .game__moves:has(#game-move-list:empty) { display: none; }   /* no room: the panel is only strips, hand and actions */
}
@media (min-width: 900px) and (min-height: 600px) {
  .game { row-gap: 10px; }
  .game > .strip .strip__clock { font-size: 2rem; }
}

/* The move list. Phone: one horizontal strip of PSN tokens above the opponent's bar, newest at the
   right; wide: a table of turns (Guest | Host) filling the side panel. */
.mv-nav { display: none; }
.mv-live {
  flex: none;
  min-height: 28px;
  padding: 0 12px;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent);
  font: inherit;
  font-size: var(--fs-label);
  font-weight: 700;
  cursor: pointer;
}
#game-move-list {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 2px 0;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
}
#game-move-list::-webkit-scrollbar { display: none; }
.mv-row { display: contents; }
.mv-no { flex: none; color: var(--muted); font-size: 0.75rem; font-variant-numeric: tabular-nums; padding-left: 4px; }
.mv-turn { display: inline-flex; flex: none; align-items: center; gap: 2px; }
.mv-cell {
  min-height: 28px;
  padding: 2px 6px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font: 0.8125rem/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  cursor: pointer;
}
.mv-turn.is-p1 .mv-cell { border-left-color: var(--pb-p1); }
.mv-turn.is-p2 .mv-cell { border-left-color: var(--pb-p2); }
.mv-bonus { color: var(--muted); }
.mv-cell[aria-current="true"] {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  border-color: var(--accent);
  color: var(--text);
}
.mv-download { font-size: var(--fs-label); font-weight: 600; text-decoration: none; padding: 4px 8px; color: var(--accent); }
.shortcut-list { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 8px; }
kbd {
  display: inline-block;
  min-width: 1.6em;
  padding: 1px 6px;
  border: 1px solid var(--hairline);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--surface-2);
  font: 0.8125rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  text-align: center;
}
.pb-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 50%; }

.game__expired { max-width: 420px; margin: 40px auto; text-align: center; }

.game__sheet-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }

.game__field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 10px 0;
  font-size: var(--fs-label);
  color: var(--muted);
}
.game__field input {
  min-height: var(--tap);
  padding: 0 12px;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
}

/* -------------------------------------------------------------- home page */

.home-grid {
  display: flex;
  flex-direction: column;
  gap: 28px;
  padding: 16px 0;
}

.home-play h1 { font-size: var(--fs-title); margin-bottom: 4px; }
.home-play > p.muted { margin: 0 0 14px; }

.home-play .tile-grid { margin-bottom: 10px; }

/* Quick match is a full-width hero above Challenge/Pass as soon as there's
   room for two columns, not just at the ≥900px two-column breakpoint — a
   narrow single column still stacks all three tiles, as on mobile. */
@media (min-width: 560px) {
  .home-play .tile-grid {
    grid-template-columns: 1fr 1fr;
  }
  #quick-match-tile { grid-column: 1 / -1; }
  #quick-match-seeks { grid-column: 1 / -1; }
}

.custom-game-link {
  padding: 0;
  background: transparent;
  justify-content: flex-start;
}
@media (hover: hover) and (pointer: fine) {
  .custom-game-link:hover { background: transparent; filter: none; text-decoration: underline; }
}

.home-side {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.home-bots h2, .home-live h2 { font-size: 1.15rem; margin-bottom: 10px; }

.home-bots__row {
  display: flex;
  align-items: center;
  gap: var(--gap);
  flex-wrap: wrap;
}

.bot-level-line {
  margin: 8px 0 0;
  font-size: var(--fs-label);
}

.chip-row { display: flex; gap: 6px; flex-wrap: wrap; }

.home-learn-card { display: block; }

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

.qm-seek-item {
  display: block;
  width: 100%;
  min-height: var(--tap);
  padding: 0 10px;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-label);
  text-align: left;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .qm-seek-item:hover { filter: brightness(1.08); }
}

.live-card {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 10px 14px;
  margin-bottom: 8px;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--hairline);
  color: inherit;
  text-decoration: none;
  font-size: var(--fs-label);
}
@media (hover: hover) and (pointer: fine) {
  .live-card:hover { border-color: var(--accent); }
}

.qr-wrap { margin: 14px 0; display: flex; justify-content: center; }
.qr-wrap svg { width: 180px; height: 180px; background: #fff; border-radius: 8px; padding: 8px; }

.custom-accents {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin: 14px 0;
}

.custom-accent-field {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 6px 0;
  font-size: var(--fs-label);
  color: var(--muted);
}

.custom-accent-field input {
  width: 56px;
  min-height: var(--tap);
  padding: 0 8px;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
}

.home-footer {
  margin: 32px 0 16px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
  text-align: center;
  font-size: var(--fs-label);
}
.home-footer a { text-decoration: underline; }

@media (min-width: 900px) {
  .home-grid {
    display: grid;
    grid-template-columns: 1.3fr 1fr;
    align-items: start;
    gap: 28px;
  }
}

@media (max-width: 480px) {
  .custom-accents { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------------ learn */

.learn { padding: 16px 0 40px; display: flex; flex-direction: column; gap: 4px; }
.learn h1 { font-size: var(--fs-title); }
.learn > p.muted { margin: 0 0 20px; }

.learn-tutorial {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-top: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: 18px;
  margin-bottom: 32px;
}
.learn-tutorial__head { display: flex; align-items: center; justify-content: space-between; gap: var(--gap); flex-wrap: wrap; }
.learn-tutorial__head h2 { font-size: 1.15rem; margin: 0; }

.learn-dots { display: flex; gap: 6px; list-style: none; margin: 0; padding: 0; }
.learn-dots li {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--hairline);
}
.learn-dots li.is-current { background: var(--accent); border-color: var(--accent); }
.learn-dots li.is-done { background: var(--accent); border-color: var(--accent); opacity: 0.55; }

#tutorial-intro { margin-top: 12px; }
#tutorial-intro p { margin: 0 0 12px; }

.learn-step { margin-top: 12px; }
.learn-step h3 { font-size: 1.05rem; }
#tutorial-step-text { margin: 4px 0 14px; }

/* .learn-step-brief (title+text) and .learn-step-outcome (feedback+actions)
   wrap the board's usual neighbours without moving them: `display: contents`
   makes each wrapper invisible to layout, so the step still reads title,
   text, board, feedback, actions top to bottom exactly as before. Only at
   the panel breakpoint below do they become real boxes, for the desktop
   side-by-side layout. */
.learn-step-brief, .learn-step-outcome { display: contents; }

/* PC: the words sit in a panel beside the board instead of above it, so
   reading the instructions, making the move, and seeing the feedback/Next
   button no longer requires scrolling the board in and out of view (the
   complaint this fixes). */
@media (min-width: 900px) {
  /* :not([hidden]) matters: an ID selector's `display: grid` would otherwise
     outrank the UA stylesheet's `[hidden] { display: none }` (author rules
     always beat UA ones), showing the step while tutorial.js has it hidden. */
  #tutorial-step:not([hidden]) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 280px;
    grid-template-rows: auto auto 1fr;
    column-gap: 24px;
    align-items: start;
  }
  .learn-step-brief, .learn-step-outcome { display: block; }
  .learn-step-brief { grid-column: 2; grid-row: 1; }
  #tutorial-board { grid-column: 1; grid-row: 1 / span 3; }
  .learn-step-outcome { grid-column: 2; grid-row: 2; }
}

.learn-feedback { min-height: 22px; margin: 10px 0 0; font-weight: 600; text-align: center; }
.learn-feedback.is-success { color: var(--accent); }
.learn-feedback.is-error { color: var(--danger); }

.learn-step-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 14px; }

.learn-rules { max-width: 760px; }
.learn-rules h2 {
  font-size: 1.2rem;
  margin-top: 32px;
  padding-top: 4px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--hairline);
  scroll-margin-top: 70px;
}
.learn-rules h2:first-child { margin-top: 0; }
.learn-rules h3 { font-size: 1rem; margin-top: 18px; scroll-margin-top: 70px; }
.learn-rules p, .learn-rules li { line-height: 1.6; }
.learn-rules ul, .learn-rules ol { padding-left: 22px; }

.learn-hl { color: var(--accent); }
.learn-clash { color: var(--danger); }

.learn-circle {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 16px 0 24px;
  padding: 18px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
}
.learn-circle svg { max-width: 420px; width: 100%; height: auto; }

.learn-legend { display: flex; gap: 22px; margin-top: 10px; font-size: var(--fs-label); color: var(--muted); flex-wrap: wrap; justify-content: center; }
.learn-legend__item { display: inline-flex; align-items: center; gap: 6px; }
.learn-legend__swatch { display: inline-block; width: 22px; height: 3px; border-radius: 2px; }
.learn-legend__swatch--harmony { background: var(--accent); }
.learn-legend__swatch--clash { background: transparent; border-top: 2px dashed var(--danger); height: 0; }

.learn-table { width: 100%; border-collapse: collapse; margin: 8px 0 16px; font-size: 0.92rem; }
.learn-table th, .learn-table td { padding: 7px 10px; border-bottom: 1px solid var(--hairline); text-align: left; }
.learn-table th { background: var(--surface-2); font-weight: 600; }
.learn-table tr:last-child td { border-bottom: none; }

.learn-tip {
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  border-left: 3px solid var(--accent);
  padding: 12px 16px;
  margin: 20px 0;
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 0.92rem;
}

.learn-credit { margin-top: 24px; }
.learn-credit a { text-decoration: underline; }

/* ---------------------------------------------------------------- bots page */

.bots { padding: 16px 0 40px; }
.bots h1 { font-size: var(--fs-title); }
.bots > p.muted { margin: 0 0 20px; max-width: 60ch; }

.bots-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--gap);
}

.bot-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 18px;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  background: var(--surface);
}

.bot-card__head { display: flex; align-items: center; gap: 12px; }

.bot-card__level {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 16%, var(--surface));
  color: var(--accent);
  font-weight: 700;
  font-size: 1.1rem;
}

.bot-card__head h3 { font-size: 1.05rem; }

.bot-card__personality { margin: 0; line-height: 1.5; min-height: 2.6em; }

/* A six-segment meter, filled to the level's rung — a legitimate sequence
   device since the levels really are an ascending ladder (see RATINGS in
   bots.js), not decoration bolted onto arbitrary content. */
.bot-meter { display: flex; gap: 4px; }
.bot-meter__seg {
  flex: 1 1 0;
  height: 4px;
  border-radius: 2px;
  background: var(--hairline);
}
.bot-meter__seg.is-filled { background: var(--accent); }

.bot-card__foot {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.bot-card__rating { font-size: var(--fs-label); }

/* ----------------------------------------------------------- developers page */

.dev { padding: 16px 0 40px; max-width: 640px; }
.dev h1 { font-size: var(--fs-title); margin-bottom: 14px; }
.dev p { line-height: 1.6; }
.dev p a { text-decoration: underline; }

.dev-code {
  margin: 18px 0;
  padding: 16px 18px;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  background: var(--surface-2);
  overflow-x: auto;
  font: 0.85rem/1.7 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: pre;
}

/* -------------------------------------------------------------------- lab gate */

.lab-gate {
  max-width: 420px;
  margin: 40px auto;
  text-align: center;
}
.lab-gate h2 { font-size: 1.15rem; }
.lab-gate .game__field { text-align: left; margin: 16px 0 10px; }
.lab-gate .btn { width: 100%; }

/* ------------------------------------------------------------------ ratings */

.rated-toggle { margin-top: 6px; }

.rated-tag, .rated-badge {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: var(--fs-label);
  font-weight: 600;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--surface-2));
}

.game__rated { display: flex; align-items: center; gap: 8px; font-size: var(--fs-label); }

.rated-detail { font-variant-numeric: tabular-nums; font-weight: 600; }

.strip__rating { color: var(--muted); font-weight: 500; font-variant-numeric: tabular-nums; }

.rated-toggle[hidden], .game__rated[hidden] { display: none; }

/* ------------------------------------------------------------------ touch
   Hover effects are wrapped in (hover: hover) and (pointer: fine) above, so a tap never leaves a
   lifted or brightened piece behind. Buttons and the board take taps without the double-tap
   zoom delay; the game board and hands never start a scroll or a text/image selection, because
   a finger on a piece is a drag (board.js). */
html { -webkit-tap-highlight-color: transparent; }
.btn, .chip, .pb-tile, .pb-cell, .pb-piece { touch-action: manipulation; }
body.page--game .pb-wrap, body.page--game .pb-tile, body.page--game .pb-piece { touch-action: none; }
.pb-wrap, .pb-hand, .pb-tile, .pb-piece {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
.pb-tile__img, .pb-piece__img { -webkit-user-drag: none; user-drag: none; }
.pb-tile:active:not(:disabled), .pb-piece:active { transform: scale(0.96); }

/* Drag and drop (board.js): a ghost follows the pointer, the real piece dims, the point under
   the pointer lights up when it is a legal target. */
.pb-ghost {
  position: fixed;
  z-index: 100;
  pointer-events: none;
  border-radius: 50%;
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.45);
  opacity: 0.92;
}
.pb-ghost img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; }
.pb-piece--dragging { opacity: 0.3; }
.pb-cell--drop::after { transform: scale(1.3); background: color-mix(in srgb, var(--accent) 50%, transparent); }

/* ------------------------------------------------------------------ lobby */

.lobby {
  display: grid;
  gap: 20px;
  padding: 16px 0 8px;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "pair" "side" "seeks" "live";
}
.lobby__pair { grid-area: pair; min-width: 0; }
.lobby__seeks { grid-area: seeks; min-width: 0; }
.lobby__side { grid-area: side; }
.lobby__live { grid-area: live; min-width: 0; }
@media (min-width: 900px) {
  .lobby {
    grid-template-columns: minmax(0, 1fr) 320px;
    grid-template-areas: "pair side" "seeks side" "live live";
    column-gap: 32px;
    padding-top: 28px;
  }
}
.lobby__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.lobby h1 { font-size: var(--fs-title); margin: 0; }
.lobby__hint { margin: 4px 0 14px; }
.lobby__sub { font-size: 1.05rem; margin: 20px 0 8px; }

/* Quick pairing: one tap on a time control starts a match. */
.pairing-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
@media (min-width: 560px) { .pairing-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.pair-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 92px;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  cursor: pointer;
  transition: transform 0.1s ease, border-color 0.15s ease, background-color 0.15s ease;
}
.pair-tile b { font-size: 1.6rem; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.pair-tile span { color: var(--muted); font-size: var(--fs-label); }
.pair-tile--custom b { font-size: 1.2rem; }
.pair-tile:active { transform: scale(0.97); }
@media (max-width: 559px) {
  /* a phone: the grid and the first button should share the first screen */
  .lobby { gap: 14px; padding-top: 10px; }
  .lobby__hint { display: none; }
  .pair-tile { min-height: 76px; }
  .pair-tile b { font-size: 1.4rem; }
  .lobby__head { margin-bottom: 10px; }
}
@media (hover: hover) and (pointer: fine) {
  .pair-tile:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--surface-2)); }
}

.lobby__side { display: flex; flex-direction: column; gap: 10px; align-self: start; }
.lobby-btn { min-height: 56px; width: 100%; font-size: 1.05rem; }
.lobby-learn {
  display: block;
  padding: 12px 14px;
  border: 1px dashed var(--hairline);
  border-radius: var(--radius);
  text-decoration: none;
}

.qm-empty { margin: 4px 0; }
.lobby .qm-seeks { margin: 0; }
.lobby .qm-seek-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: 12px;
  padding: 0 12px;
  font-size: 0.9rem;
}
.qm-who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.qm-tc { font-variant-numeric: tabular-nums; }

/* Live games: a strip of mini boards, two to a phone screen, scrolling sideways for more. */
.live-strip { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 8px; scroll-snap-type: x proximity; }
.live-strip > p { margin: 0; }
.live-strip .live-card {
  flex: 0 0 calc(50% - 6px);
  max-width: 190px;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  margin: 0;
  padding: 10px;
  scroll-snap-align: start;
}
.live-card__names { display: flex; flex-direction: column; gap: 2px; font-weight: 600; font-size: 0.85rem; min-width: 0; }
.live-card__who { display: flex; align-items: center; gap: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.live-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; }
.live-dot--p1 { background: var(--pb-p1); }
.live-dot--p2 { background: var(--pb-p2); }
.live-card__moves { font-size: 0.75rem; }
.mini-board { width: 100%; aspect-ratio: 1; display: block; }
.mini-bg { fill: var(--surface-2); stroke: var(--hairline); stroke-width: 0.2; }
.mini-red { fill: var(--garden-red); opacity: 0.85; }
.mini-white { fill: var(--garden-white); }
.mini-gate { fill: var(--garden-red); opacity: 0.85; }
.mini-p1, .mini-p2 { stroke: var(--surface); stroke-width: 0.18; }
.mini-p1 { fill: var(--pb-p1); }
.mini-p2 { fill: var(--pb-p2); }
.mini-growing { fill-opacity: 0.55; stroke: currentColor; stroke-width: 0.14; stroke-dasharray: 0.3 0.25; }
.rated-toggle .chip.is-blocked { opacity: 0.55; }

/* ----------------------------------------------------------- top-bar nav
   A real navigation bar on wide windows; the hamburger menu keeps the same links everywhere. */
.topbar__nav { display: none; align-items: center; gap: 4px; margin-left: 8px; }
.topbar__nav a {
  padding: 8px 12px;
  border-radius: 8px;
  color: var(--muted);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.95rem;
}
.topbar__nav a[aria-current="page"] { color: var(--text); background: var(--surface-2); }
@media (hover: hover) and (pointer: fine) {
  .topbar__nav a:hover { color: var(--text); }
}
@media (min-width: 900px) {
  .topbar__nav { display: flex; }     /* the menu button stays: it holds the theme, sound and sign-out entries */
}
body[data-chrome="lab"] .topbar__nav { display: none; }
@media (max-width: 480px) {
  .topbar__name { display: none; }
  .topbar__right .chip { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  [data-account-slot] > button.chip { display: none; }      /* "Sign in" moves into the menu */
}

/* ------------------------------------------------------- profile and account */

.profile, .account { max-width: 720px; margin: 0 auto; padding: 24px 0 32px; }
.profile h2 { font-size: 1.1rem; margin: 24px 0 10px; }
.profile__head { display: flex; align-items: center; gap: 16px; }
.profile__head h1 { margin: 0; font-size: var(--fs-title); }
.profile__head p { margin: 2px 0 0; }
.profile__avatar {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 22%, var(--surface-2));
  border: 2px solid var(--accent);
  color: var(--accent);
  font-size: 1.8rem;
  font-weight: 700;
}

/* One card per rating category, the number first. A trailing "?" marks a provisional rating. */
.profile-ratings {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
@media (min-width: 560px) { .profile-ratings { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.rating-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--surface);
}
.rating-cat { color: var(--muted); font-size: var(--fs-label); text-transform: capitalize; }
.rating-num { font-size: 1.7rem; font-variant-numeric: tabular-nums; line-height: 1.1; }

.profile-games { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--hairline); }
.profile-games li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 8em;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  padding: 6px 4px;
  border-bottom: 1px solid var(--hairline);
}
.pg-opp { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none; }
.pg-mode { font-size: var(--fs-label); text-transform: capitalize; }
.pg-result { justify-self: end; white-space: nowrap; font-weight: 700; font-size: var(--fs-label); text-transform: uppercase; letter-spacing: 0.04em; }
.pg-result--win { color: var(--accent); }
.pg-result--loss { color: var(--danger); }
.pg-result--draw, .pg-result--in-progress { color: var(--muted); }
@media (hover: hover) and (pointer: fine) {
  .profile-games li:hover { background: var(--surface-2); }
}

.account h1 { font-size: var(--fs-title); margin-bottom: 6px; }
.account .menu-list { max-width: 360px; margin-top: 14px; }
.account-error {
  padding: 10px 14px;
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  color: var(--danger);
}
.account-form { display: flex; gap: 8px; max-width: 420px; margin-top: 14px; }
.account-form input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--tap);
  padding: 0 12px;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 16px;            /* iOS zooms into smaller inputs */
}
.account-form input:invalid:not(:placeholder-shown), .account-form input:user-invalid { border-color: var(--danger); }
.account-msg { min-height: 1.4em; margin-top: 8px; }

/* ------------------------------------------------ connection pill, in-app note */
.conn-pill {
  grid-area: board;
  align-self: start;
  justify-self: center;
  z-index: 20;
  margin-top: 6px;
  padding: 4px 14px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: var(--fs-label);
  font-weight: 700;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  pointer-events: none;
}
.conn-pill[hidden] { display: none; }
.inapp-note {
  margin: 10px 0;
  padding: 10px 12px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  font-size: var(--fs-label);
}

/* The owner-only console (/admin): phone first, existing tokens only. */
.con { max-width: 720px; margin: 0 auto; padding: 16px 16px 40px; overflow-wrap: anywhere; }
.con h1 { font-size: var(--fs-title); margin-bottom: 6px; }
.con h2 { font-size: 1.1rem; margin: 24px 0 10px; }
.admin-sub { font-size: var(--fs-label); color: var(--muted); text-transform: uppercase; margin: 14px 0 6px; }
.admin-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--radius); padding: 10px 12px; margin-bottom: 8px; }
.admin-card h3 { font-size: var(--fs-body); margin: 0 0 4px; }
.admin-line { margin: 2px 0; font-size: var(--fs-label); }
.admin-plain { color: var(--text); }
.admin-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.admin-token { display: flex; align-items: center; gap: 8px; margin-top: 6px; font-size: var(--fs-label); }
.admin-grow { flex: 1; min-width: 0; }
.admin-danger { color: var(--danger); }
.admin-audit { padding: 6px 0; border-bottom: 1px solid var(--hairline); font-size: var(--fs-label); }
