/* Killer Sudoku — app styles.
 *
 * The board is the whole product, so the layout is built around one rule from
 * the blueprint: the grid scales to the SHORTER of the viewport's two axes, so
 * it is never cropped on a tall phone or stretched on a wide monitor. Everything
 * else is arranged around whatever space that leaves.
 */

:root {
  --bg: #f4f6f4;
  --surface: #ffffff;
  --surface-2: #ecefec;
  --ink: #16201a;
  --ink-soft: #5b6a60;
  --ink-faint: #8b9a90;
  --line: #cfd8d2;
  --line-strong: #2c3a32;
  --accent: #2f6f4e;
  --accent-soft: #dbeae1;
  --accent-ink: #ffffff;
  --custom: #8a4fbd;
  --custom-soft: #efe3f8;
  --danger: #b3352f;
  --danger-soft: #fadedc;
  --given: #16201a;
  --entered: #2f6f4e;
  /* Board colors.
   *
   * Declared as literal hex here and again in the dark block rather than as
   * var() chains, because the settings dialog reads them back with
   * getPropertyValue to seed its color inputs and that returns a var() chain
   * unresolved. An override from settings is set inline on the root element,
   * which wins over both blocks. */
  --cell: #ffffff;
  --cell-sel: #dbeae1;
  --cell-same: #dbeae1;
  --cell-peer: #ecefec;
  --cell-wrong: #fadedc;
  --paint-mark: #ffd54a;

  /* The cell palette. Light enough that digits and pencil marks stay readable
     on top: the color is a note about a cell, not a substitute for it. */
  --swatch-1: #fff2b2;
  --swatch-2: #ffe0c4;
  --swatch-3: #ffd7e3;
  --swatch-4: #e7dbf8;
  --swatch-5: #d5e7ff;
  --swatch-6: #cdeee8;
  --swatch-7: #d9f0d1;
  --swatch-8: #e3e7e5;
  --paint-ink: #16201a;
  --digit-ink: #2f6f4e;
  --digit-wrong: #b3352f;
  --pencil-ink: #8b9a90;
  --cage-sum-ink: #5b6a60;
  /* The nine tally dots under each key on the number pad. Grey by default
     because the dots are a count, not a signal — but it is a setting, so a
     player who wants them to stand out can have that. */
  --pad-dot: #8b9a90;
  --cage-line: #8b9a90;
  /* The custom cage palette.
     Eight hues, one per cage, cycled — a cage's outline, its box in the panel
     and its combination row under the pad are all drawn in the same one, which
     is the only thing that says which cage a row of combinations belongs to
     when several cover the same cells. Saturated here and light in the dark
     scheme, because both have to read as a dashed hairline. */
  --custom-1: #8a4fbd;
  --custom-2: #b8228f;
  --custom-3: #c2410c;
  --custom-4: #9a6700;
  --custom-5: #4d7c0f;
  --custom-6: #0f766e;
  --custom-7: #0369a1;
  --custom-8: #4338ca;
  --rule-line: #cfd8d2;
  --rule-box: #2c3a32;

  --radius: 10px;
  --shadow: 0 1px 2px rgba(16, 32, 24, .06), 0 8px 24px rgba(16, 32, 24, .06);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #10150f;
    --surface: #182018;
    --surface-2: #212b21;
    --ink: #e8efe9;
    --ink-soft: #a3b3a8;
    --ink-faint: #6f8177;
    --line: #33423a;
    --line-strong: #cddbd2;
    --accent: #6fc394;
    --accent-soft: #1d3227;
    --accent-ink: #06170e;
    --custom: #c79bf0;
    --custom-soft: #2c2138;
    --danger: #f08a84;
    --danger-soft: #3a1f1d;
    --given: #e8efe9;
    --entered: #6fc394;

    --cell: #182018;
    --cell-sel: #1d3227;
    --cell-same: #1d3227;
    --cell-peer: #212b21;
    --cell-wrong: #3a1f1d;
    --paint-mark: #e2ae2b;

    --swatch-1: #4a4224;
    --swatch-2: #4a3826;
    --swatch-3: #4a2c36;
    --swatch-4: #382c4c;
    --swatch-5: #26364c;
    --swatch-6: #22423e;
    --swatch-7: #2a4029;
    --swatch-8: #363c39;
    --paint-ink: #16201a;
    --digit-ink: #6fc394;
    --digit-wrong: #f08a84;
    --pencil-ink: #6f8177;
    --cage-sum-ink: #a3b3a8;
    --pad-dot: #6f8177;
    --cage-line: #6f8177;
    --custom-1: #c79bf0;
    --custom-2: #f2a0d6;
    --custom-3: #f5a97a;
    --custom-4: #ddb765;
    --custom-5: #b3d67d;
    --custom-6: #6fd0c2;
    --custom-7: #7cc0f0;
    --custom-8: #a6abf7;
    --rule-line: #33423a;
    --rule-box: #cddbd2;
    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .35);
  }
}

* { box-sizing: border-box; }

/* The UA's `[hidden] { display: none }` is a bare type-less rule, so any class
   that sets display beats it — .form label, .pause-overlay and .name-form all
   did, and each one silently showed something meant to be hidden. Said once
   here rather than remembered at every call site. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  min-height: 100%;
}

/* The board's landscape width is partly a vw measurement, and vw counts the
   scrollbar that the content box does not. Reserving the gutter keeps the
   content width the same whether the page scrolls or not, so the board and the
   columns beside it do not shift by a scrollbar's width when the panel grows
   past the bottom of the window. */
html { scrollbar-gutter: stable; }

body {
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--accent);
  color: var(--accent-ink);
  padding: .6rem 1rem;
  z-index: 100;
}
.skip-link:focus { left: 0; }

/* --- Top bar --------------------------------------------------------------- */

.topbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .55rem clamp(.7rem, 3vw, 1.5rem);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 20;
  flex-wrap: wrap;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  font-weight: 700;
  color: inherit;
  text-decoration: none;
  letter-spacing: -.01em;
}

.brand-mark {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 7px;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 800;
  font-size: .95rem;
}

/* What the nav used to be. The menu is on the play page now; this only pushes
   the account, the build number and the cog to the far end of the bar. */
.topbar-gap { flex: 1; }

.topbar-account { display: flex; align-items: center; gap: .5rem; font-size: .9rem; }
.topbar-account .who { color: var(--ink-soft); }
.tier-chip {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-weight: 700;
  padding: .12rem .42rem;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--ink-soft);
}
.tier-chip.premium { background: var(--accent-soft); color: var(--accent); }

.build-chip {
  font-size: .7rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
  letter-spacing: .03em;
}

.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border-radius: 8px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
}
.icon-btn:hover { background: var(--surface-2); color: var(--ink); border-color: var(--line); }

/* Sign in, as the one glyph in the bar that is asking for something.
   Filled in the accent color rather than outlined like the cog and the
   sign-out beside it, and ringed once every few seconds: an account is what
   raises the custom-cage allowance and unlocks Battle Mode, and the word it
   replaced was a grey button the same weight as everything else up here. */
.icon-btn.signin {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  animation: signin-pulse 3.2s ease-out infinite;
}
.icon-btn.signin:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  filter: brightness(1.08);
}

@keyframes signin-pulse {
  0%, 70%, 100% { box-shadow: 0 0 0 0 var(--accent-soft); }
  35% { box-shadow: 0 0 0 6px var(--accent-soft); }
}

@media (prefers-reduced-motion: reduce) {
  /* Still the loudest thing in the bar, just not moving. */
  .icon-btn.signin { animation: none; box-shadow: 0 0 0 3px var(--accent-soft); }
}

/* --- Shared ---------------------------------------------------------------- */

.app { padding: clamp(.7rem, 2.5vw, 1.5rem); }

.wrap { max-width: 62rem; margin: 0 auto; }

h1 { font-size: clamp(1.5rem, 4vw, 2.1rem); letter-spacing: -.02em; margin: .2rem 0 .4rem; }
h2 { font-size: 1.15rem; letter-spacing: -.01em; margin: 1.6rem 0 .6rem; }
h3 { font-size: 1rem; margin: 1.1rem 0 .4rem; }
p { margin: .5rem 0; }
.lede { color: var(--ink-soft); font-size: 1.02rem; max-width: 46rem; }

.btn {
  font: inherit;
  font-size: .93rem;
  font-weight: 600;
  padding: .5rem .9rem;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: .4rem;
}
/* A link wearing .btn is a button, and a button is not underlined. It only
   ever showed on the handful of anchors styled this way — "Resume", "Back to
   the puzzles" — and now on every item of the menu that moved down here. */
a.btn { text-decoration: none; }
/* The plain button's hover. It is more specific than the rules that fill a
   button in — `.btn:hover:not(:disabled)` scores a class more than `.btn.primary`
   — so every filled variant below has to name its own background again or lose
   it to this one on hover. `.btn.primary` did not, and brightening a background
   it no longer had left white text on near-white: the largest button on the page,
   "New puzzle" on a solved board, went unreadable under the pointer. */
.btn:hover:not(:disabled) { background: var(--surface-2); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn.primary:hover:not(:disabled) { background: var(--accent); filter: brightness(1.06); }
.btn.subtle { border-color: transparent; background: transparent; color: var(--ink-soft); }
.btn.subtle:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }
.btn.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.btn.on:hover:not(:disabled) { background: var(--accent-soft); filter: brightness(1.06); }
.btn.sm { padding: .3rem .55rem; font-size: .85rem; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem;
  box-shadow: var(--shadow);
}

.muted { color: var(--ink-soft); }
.tiny { font-size: .82rem; }
.stack { display: grid; gap: .8rem; }
.row { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }

.notice {
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  background: var(--surface);
  border-radius: 8px;
  padding: .7rem .9rem;
  font-size: .92rem;
}
.notice.locked { border-left-color: var(--ink-faint); }

/* The game in progress. It was a sentence with a link in it and read as small
   print; it is the one thing on this page a returning player is looking for. */
.notice.resume {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  border: 2px solid var(--accent);
  border-left-width: 2px;
  background: var(--accent-soft);
  padding: .9rem 1rem;
  margin-bottom: 1rem;
  box-shadow: var(--shadow);
}
.notice.resume .what { display: grid; gap: .1rem; }
.notice.resume strong { font-size: 1.05rem; letter-spacing: -.01em; }
.notice.resume .detail { color: var(--ink-soft); font-size: .88rem; }
.notice.resume .btn { white-space: nowrap; }
.notice.bad { border-left-color: var(--danger); background: var(--danger-soft); }

.toast {
  position: fixed;
  left: 50%;
  bottom: 1.2rem;
  transform: translateX(-50%);
  background: var(--line-strong);
  color: var(--bg);
  padding: .55rem 1rem;
  border-radius: 999px;
  font-size: .9rem;
  z-index: 60;
  box-shadow: var(--shadow);
  max-width: 90vw;
}

/* --- Home ------------------------------------------------------------------ */

.tier-grid {
  display: grid;
  gap: .7rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  margin-top: 1rem;
}

.tier-card {
  text-align: left;
  padding: .9rem 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  cursor: pointer;
  font: inherit;
  color: inherit;
  display: grid;
  gap: .15rem;
}
.tier-card:hover { border-color: var(--accent); background: var(--accent-soft); }
.tier-card strong { font-size: 1.05rem; letter-spacing: -.01em; }
.tier-card .desc { color: var(--ink-soft); font-size: .86rem; }
.tier-card .pool { color: var(--ink-faint); font-size: .78rem; }
.tier-card:disabled { opacity: .5; cursor: not-allowed; }

/* The app's menu, which used to be the top bar's. Wide targets that wrap, so
   the four of them are one row on a tablet and two on a phone — and so none of
   them is the 1rem of text a nav link was. */
.home-nav {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: .5rem;
}
.home-nav .btn {
  justify-content: center;
  padding: .6rem .8rem;
  font-weight: 600;
}

/* --- Play view ------------------------------------------------------------- */

/* Three columns in landscape: HUD, board, controls. Stacked in portrait.
 *
 * The HUD is a column of its own rather than a line above the board because
 * anything above the board costs the board that much height, and on a landscape
 * screen height is the only axis the board is short of. */
.play-layout {
  display: grid;
  gap: .8rem;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  justify-content: center;
  max-width: 76rem;
  margin: 0 auto;
}

/* Pad, tools and selection panel, stacked. Below the landscape breakpoint this
 * simply falls under the board, which is the phone layout. */
.side { display: grid; gap: .7rem; width: 100%; align-content: start; }

.board-area { display: grid; gap: .6rem; justify-items: center; }

/* The default is the portrait/narrow case: the board takes the full width it
 * can get and the controls follow underneath. 46rem caps it on a tablet so the
 * digits do not end up the size of a thumb. */
.board-frame {
  width: min(100%, 46rem);
  aspect-ratio: 1;
  min-width: 15rem;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  touch-action: none;
  /* The board is full of SVG text — cage sums, digits, pencil marks — and a
     long press on text is a selection gesture. The browser starts one, cancels
     the pointer, and the hold that was meant to commit a cell is lost.
     touch-action alone does not stop it; these do. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  position: relative;
}

/* Portrait handhelds: the board maximizes horizontal space, so the page padding
 * is pulled almost to nothing and the frame runs edge to edge. */
@media (orientation: portrait) and (max-width: 62rem) {
  .app { padding-left: .3rem; padding-right: .3rem; }
  .board-frame { width: min(calc(100vw - .8rem), 46rem); }
}

/* Landscape: the board maximizes vertical space instead. 100svh less the top
 * bar and the page padding is the whole of what is available; 54vw stops it
 * crowding out the controls on a short-but-wide window, and the 52rem cap is
 * the "unless the screen is really large" case — past that a bigger board is
 * just a longer eye movement between one cell and the next. */
@media (orientation: landscape) and (min-width: 48rem) {
  /* Deliberately not width:fit-content. Shrink-wrapping a grid whose middle
     track holds a board sized from min(100svh…, 54vw, …) measures that board
     against the viewport rather than the space left over, and the grid ends up
     wider than the window. justify-content centers the tracks without an
     intrinsic pass over the board. */
  .play-layout {
    grid-template-columns: auto minmax(0, auto) minmax(16rem, 22rem);
    gap: .9rem;
  }
  .board-frame { width: min(calc(100svh - 5.5rem), 54vw, 52rem); }

  /* The pad, tools and panel together run far past the bottom of the window —
     a cage with a dozen combinations does it on its own. They scroll inside
     their own column so the board never moves, and the HUD and board are stuck
     to the top for the case where the page scrolls anyway. */
  .side {
    max-height: calc(100svh - 5rem);
    overflow-y: auto;
    scrollbar-gutter: stable;
    padding-right: .2rem;
  }
  .hud, .board-area { position: sticky; top: .5rem; }
}

.board-frame svg { display: block; width: 100%; height: 100%; }
.board-zoomer { width: 100%; height: 100%; transform-origin: 0 0; transition: filter .18s ease; }

/* Ctrl-drag pans a zoomed board; the cursor is the only affordance for it, so
   it changes for the whole gesture rather than only while a modifier is down. */
.board-frame.panning { cursor: grabbing; }

/* Paused. The blur is on .board-zoomer rather than the frame so the overlay,
   which is a sibling, stays sharp on top of it. */
.board-frame.paused .board-zoomer { filter: blur(9px); }

.pause-overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--surface);
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  z-index: 2;
}

.pause-card {
  display: grid;
  gap: .7rem;
  justify-items: center;
  padding: 1.1rem 1.6rem;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.pause-card strong { font-size: 1.15rem; letter-spacing: .02em; }

.hud .timer.paused { color: var(--ink-faint); }

/* --- Finishing a puzzle ----------------------------------------------------
 *
 * The confetti, fireworks and stars are drawn on one canvas over the whole
 * page (lib/celebrate.js); everything here is the frame it happens in, plus
 * the board's own flourish, which is CSS because it is one element pulsing
 * rather than three hundred particles moving.
 *
 * Gold is spelled out rather than taken from a token: none of the app's
 * colors are celebratory, and this has to read as gold in both schemes.
 */
.celebration {
  position: fixed;
  inset: 0;
  /* Above the board and the top bar, below the toast at 60 — the toast says
     the time and the mistake count, which is the part worth reading. */
  z-index: 50;
  /* Nothing on this layer is clickable, and a full-viewport canvas that ate
     pointer events would make the finished board untouchable while it ran. */
  pointer-events: none;
}

/* The board's own moment: a glow that swells and settles back. */
.board-frame.celebrating {
  border-color: #f4b400;
  animation: board-glow 2.6s cubic-bezier(.2, .7, .3, 1) forwards;
}

/* And a flash of light out of the middle of it, which is where the confetti
   comes from — clipped by the frame's own overflow, so the burst is the
   board's rather than the page's. */
.board-frame.celebrating::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  border-radius: inherit;
  /* Light enough to read through. The grid the player just finished is the
     thing they want to look at, and a flash that hides it for half a second
     takes that away at the exact moment it is earned. */
  background: radial-gradient(circle at 50% 50%, rgba(255, 213, 74, .42), rgba(255, 157, 63, .16) 42%, rgba(255, 157, 63, 0) 68%);
  animation: board-flash 1.1s ease-out forwards;
}

@keyframes board-glow {
  0% { box-shadow: var(--shadow); }
  18% { box-shadow: 0 0 0 3px rgba(255, 213, 74, .85), 0 0 46px 12px rgba(244, 180, 0, .5); }
  100% { box-shadow: var(--shadow); }
}

@keyframes board-flash {
  0% { opacity: 0; transform: scale(.35); }
  22% { opacity: 1; }
  100% { opacity: 0; transform: scale(1.7); }
}

/* The solved panel arriving under the board. Small, but the block appears out
   of nothing otherwise, in the one place the player is about to look. */
.panel-block.solved { animation: solved-in .45s ease-out both; }

@keyframes solved-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

/* Reduced motion. The canvas never gets made at all — celebrate() checks the
   same query — so what is left is the glow, and it loses the movement: no
   expanding flash, no rise on the panel, just light coming up and going. */
@media (prefers-reduced-motion: reduce) {
  .board-frame.celebrating::after { animation: none; opacity: 0; }
  .board-frame.celebrating { animation: board-glow-still 2.6s ease-out forwards; }
  .panel-block.solved { animation: solved-fade .45s ease-out both; }

  @keyframes board-glow-still {
    0% { box-shadow: var(--shadow); }
    18% { box-shadow: 0 0 0 3px rgba(255, 213, 74, .7); }
    100% { box-shadow: var(--shadow); }
  }
  @keyframes solved-fade {
    from { opacity: 0; }
    to { opacity: 1; }
  }
}

/* Board internals. Every color here is a settings-controlled token. */
.cell-bg { fill: var(--cell); }

/* A cell the player has colored. The state fill is drawn over the swatch at
   partial opacity so a selection or a peer highlight reads without hiding it,
   and at no opacity at all when the cell is in none of those states. */
.cell-swatch { fill: var(--cell); }
.cell-swatch.swatch-1 { fill: var(--swatch-1); }
.cell-swatch.swatch-2 { fill: var(--swatch-2); }
.cell-swatch.swatch-3 { fill: var(--swatch-3); }
.cell-swatch.swatch-4 { fill: var(--swatch-4); }
.cell-swatch.swatch-5 { fill: var(--swatch-5); }
.cell-swatch.swatch-6 { fill: var(--swatch-6); }
.cell-swatch.swatch-7 { fill: var(--swatch-7); }
.cell-swatch.swatch-8 { fill: var(--swatch-8); }

.cell-bg.tinted { fill-opacity: 0; }
.cell-bg.tinted.sel,
.cell-bg.tinted.same,
.cell-bg.tinted.peer { fill-opacity: .5; }
.cell-bg.tinted.wrong { fill-opacity: .62; }
.cell-bg.sel { fill: var(--cell-sel); }
.cell-bg.peer { fill: var(--cell-peer); }
.cell-bg.same { fill: var(--cell-same); opacity: .55; }
.cell-bg.wrong { fill: var(--cell-wrong); }

.grid-line { stroke: var(--rule-line); stroke-width: 1; }
.grid-line.box { stroke: var(--rule-box); stroke-width: 2.4; }

.cage-edge { stroke: var(--cage-line); stroke-width: 1.4; stroke-dasharray: 4 3; fill: none; stroke-linecap: round; }
.cage-sum { fill: var(--cage-sum-ink); font-size: 8.5px; font-weight: 700; }

/* Thinner than the puzzle cages above: a player's own cage is an annotation
   over the puzzle, not part of it, and at 1.8 it read as the louder of the
   two. The short dashes and its own color still tell them apart. */
.custom-edge { stroke: var(--cage-ink, var(--custom-1)); stroke-width: 1; stroke-dasharray: 2 3; fill: none; stroke-linecap: round; }

/* One custom cage's color, set once on whatever wears its class — the SVG
   outline, the panel box, the combination row — and read from --cage-ink by
   every rule that draws part of it. */
.cc-1 { --cage-ink: var(--custom-1); }
.cc-2 { --cage-ink: var(--custom-2); }
.cc-3 { --cage-ink: var(--custom-3); }
.cc-4 { --cage-ink: var(--custom-4); }
.cc-5 { --cage-ink: var(--custom-5); }
.cc-6 { --cage-ink: var(--custom-6); }
.cc-7 { --cage-ink: var(--custom-7); }
.cc-8 { --cage-ink: var(--custom-8); }

/* A cage whose digits do not make its sum, on a board with every cell filled.
   The outline says it, because the cells inside can each be perfectly legal. */
.cage-edge.bad { stroke: var(--digit-wrong); stroke-width: 2.2; stroke-dasharray: 5 2.5; }
.cage-sum.bad { fill: var(--digit-wrong); }

.digit { font-size: 24px; font-weight: 500; text-anchor: middle; fill: var(--digit-ink); }
.digit.wrong { fill: var(--digit-wrong); }
.pencil { font-size: 8px; text-anchor: middle; fill: var(--pencil-ink); }

/* Every copy of the highlighted digit — the one the painter is loaded with, or
   the one asked about by pressing a number with nothing selected. Answers and
   pencil notes are marked the same way, because both are copies of the digit
   and the question is where it already is. It sits behind the digit, which is
   why it is drawn first and needs no opacity of its own; the digit itself goes
   dark so it still reads against it. An answer gets a disc and a pencil note a
   lozenge, wider than it is tall — the columns of notes have half again as much
   room between them as the rows do, and a shape that uses it is far easier to
   pick out of a full cell than a circle small enough to clear both. */
.digit-mark, .pencil-mark { fill: var(--paint-mark); }
.digit.hot { font-weight: 800; fill: var(--paint-ink); }
.pencil.hot { font-weight: 800; fill: var(--paint-ink); }

/* --- Controls -------------------------------------------------------------- */

/* The digits, the eraser, undo and redo, and the totals for the selection, as
   one block at the top of the control column. Everything in here is wanted for
   the whole of a solve, so nothing that is wanted occasionally — the tools, the
   swatches, the panel — is allowed to push it about: they all sit below it. */
.pad-group {
  display: grid;
  gap: .4rem;
  width: 100%;
}

/* Six across, two deep: 1-6, then 7-9 and the three keys that are a glyph
   rather than a digit. Five across was exactly two rows of digits-plus-eraser,
   and there was no eleventh slot for Undo to go in without a third row. */
.pad {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: .35rem;
  width: min(100%, 30rem);
  justify-self: center;
}

.pad button {
  font: inherit;
  font-size: 1.25rem;
  font-weight: 600;
  aspect-ratio: 1.5;
  /* A sixth column makes each key narrower, and the aspect ratio makes it
     shorter with it. The floor is what a finger needs; the ceiling is what the
     board wants back. */
  min-height: 2.3rem;
  max-height: 3rem;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.pad button {
  /* A long press is a gesture here, so the callout, the text selection and the
     double-tap zoom the browser would otherwise offer all have to go.
     touch-action must be `none`, not `manipulation`: manipulation still lets the
     browser treat the press as the start of a scroll, and a finger resting on a
     button drifts a pixel or two — at which point the gesture is taken over and
     pointercancel kills the hold before it has run its half second. */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.pad button:hover { background: var(--accent-soft); border-color: var(--accent); }
/* A key that draws its job instead of spelling it: the eraser, undo, redo. */
.pad button.glyph { display: grid; place-items: center; color: var(--ink-soft); }
.pad button.glyph svg { display: block; }
.pad button.glyph:hover { color: var(--ink); }

/* Pencil mode shrinks the digits to the size they will be written at, so the
   pad shows which of its two jobs a press is about to do without being read. */
.pad.pencil button:not(.glyph) { font-size: .95rem; font-weight: 500; color: var(--ink-soft); }

/* Nine dots under each digit, one per copy of it the board still wants.
   They go inside the key rather than beside it: the pad is already the widest
   thing in the control column and the board wants every pixel back, so the
   count is laid into space the key was not using — the padding shifts the
   digit up by the strip's height instead of the key growing by it. */
.pad button.digit { position: relative; padding-bottom: .42rem; }

.pad-dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: .16rem;
  display: flex;
  justify-content: center;
  /* In rem, like everything below, and for a reason worth writing down: the
     narrowest a key ever gets is its `aspect-ratio` against its `min-height`,
     both fixed above, which is 3.45rem however narrow the control column is
     squeezed. The nine dots and their gaps come to 3.06rem. Give either of them
     in pixels instead and a browser set to a smaller default font shrinks the
     key without shrinking the row, and the tally grows out through the sides of
     the button it belongs to. */
  gap: .125rem;
  /* The key owns every gesture on it, including the long press that loads the
     painter; the dots must not be able to swallow one. */
  pointer-events: none;
}

/* Three threes rather than a line of nine. Nine evenly spaced dots read as a
   dashed underline, and counting what is left of one means counting dots;
   broken into groups the count is read the way tally marks are, and the groups
   are the same three the board itself is built out of. */
.pad-dots i:nth-child(3n + 1):not(:first-child) { margin-left: .1875rem; }

.pad-dots i {
  width: .1875rem;
  height: .1875rem;
  border-radius: 50%;
  background: var(--pad-dot);
}

/* A placed copy. Hidden rather than removed so the nine keep their positions
   and the row empties from the right instead of shuffling under the digit. */
.pad-dots i.spent { visibility: hidden; }

/* All nine placed. Greyed, not disabled: the digit may still be needed — to
   take a wrong entry back out, or to pencil with — and a key that refuses the
   press would be wrong about the board as often as it was right. */
.pad button.digit.done:not(.painting) { color: var(--ink-faint); }

/* The painter is loaded: the button stays lit the way it looks under the
   cursor, because the digit is, in effect, still being pressed. */
.pad button.painting {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.pad button.painting:hover { background: var(--accent); filter: brightness(1.06); }
/* The one place the token is not used: these dots sit on a filled accent key,
   where a grey chosen against the page reads as dirt. They take the key's own
   ink instead, softened so they still read as a tally rather than as text. */
.pad button.painting .pad-dots i { background: currentColor; opacity: .55; }

/* Looking for a digit rather than writing one: outlined, not filled, so it
   never reads as the painter being loaded. */
.pad button.finding {
  border-color: var(--paint-mark);
  box-shadow: inset 0 0 0 2px var(--paint-mark);
  font-weight: 800;
}
.board-frame.painting { cursor: cell; }

/* What stands where the pad did once the board is solved. It keeps the pad's
   width and centring so the page does not jump as the two swap. */
.pad-done {
  width: min(100%, 30rem);
  justify-self: center;
  display: grid;
  gap: .5rem;
  animation: solved-in .45s ease-out both;
}

/* Every button here fills the column it is laid into, and .btn is a flex box
   that packs its label to the start — so the labels sat hard against the left
   edge of buttons several times wider than the words in them. Centred here
   rather than on .btn itself: elsewhere a full-width button's label is meant to
   start at the left, beside whatever else is on the row. */
.pad-done .btn { justify-content: center; text-align: center; }

/* Deliberately the largest target on the page: the pad it replaces was five
   buttons wide and the eye is already here. */
.pad-done .btn.big {
  font-size: 1.15rem;
  font-weight: 600;
  padding: .9rem 1rem;
  border-radius: 10px;
}

/* "New puzzle" and "Same puzzle" side by side, the new one the wider. */
.pad-done .solved-choice {
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: .4rem;
}
.pad-done .solved-choice:has(> :only-child) { grid-template-columns: 1fr; }

/* The tiers, as many to a row as fit. A single row of five overflowed the
   narrow control column in the landscape layout and set the whole column
   scrolling sideways; equal columns that wrap keep them one block at any
   width. */
.pad-done .tier-choice {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.5rem, 1fr));
  gap: .4rem;
  margin-top: 0;
}

@media (prefers-reduced-motion: reduce) {
  .pad-done { animation: solved-fade .45s ease-out both; }
}

/* The painter's instructions. Above the pad rather than in a toast over the
   board, which covered the very cells being painted. */
.painter-hint {
  border: 1px solid var(--accent);
  border-radius: 8px;
  background: var(--accent-soft);
  color: var(--ink);
  padding: .5rem .65rem;
  font-size: .82rem;
  line-height: 1.45;
}
.painter-hint strong { font-variant-numeric: tabular-nums; }


/* --- The sum row under the pad ---------------------------------------------
 *
 * Sums and combinations sit directly under the digits they are about, rather
 * than in the panel below them: choosing between 1 4 and 2 3 and then pressing
 * one of them is one movement now instead of a look down the column and back.
 *
 * One row per answer. A selection can have several at once — the prebuilt cage
 * it lies in, and each custom cage drawn over it — and each row is bordered in
 * that cage's own color, which is the same color its outline is drawn in. */
.sums-strip { display: grid; gap: .35rem; width: 100%; }

.sum-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .3rem;
  padding: .4rem .5rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
}
.sum-row.custom {
  border-color: var(--cage-ink);
  background: color-mix(in srgb, var(--cage-ink) 9%, var(--surface));
}
/* The label and what follows it are one flowing line, not a label and a block
   beneath it: "∑ = 14" and the combinations that make 14 belong on the same
   line, and a nested flex container took a line of its own the moment its
   contents were wider than the space left beside the label. `display: contents`
   drops the wrapper so its buttons wrap in the row itself. */
.sum-row > .combos { display: contents; }
.sum-row.custom .combo { border-color: var(--cage-ink); color: var(--cage-ink); }
.sum-row.custom .combo.off { border-color: var(--line); color: var(--ink-faint); }
.sum-row .combo-fold { margin-top: 0; flex: 1 1 100%; }
.sum-row.custom .combo-fold > summary:focus-visible { outline-color: var(--cage-ink); }

.sum-eq { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.sum-eq b { font-size: 1.15rem; letter-spacing: -.01em; }
.sum-eq small { font-weight: 500; font-size: .82rem; color: var(--ink-soft); margin-left: .25rem; }
.sum-row.custom .sum-eq { color: var(--cage-ink); }
/* The selected cages' combined total, boxed so it reads as the answer and not
   as one more of the totals offered after it. */
.sum-eq .sum-total {
  display: inline-block;
  border: 2px solid var(--ink);
  border-radius: 6px;
  padding: 0 .35rem;
  margin-right: .15rem;
  line-height: 1.35;
}

/* The offer: every total this selection could be caged at, as plain clickable
   numbers. Deliberately not buttons to look at — forty bordered boxes read as
   a wall, and these are a list to run an eye along, not controls to weigh up.
   Each one is still a real button, so it is reachable by keyboard. */
.sum-picks { display: contents; }
.sum-pick {
  font: inherit;
  font-variant-numeric: tabular-nums;
  border: 0;
  background: none;
  padding: .05rem .2rem;
  border-radius: 4px;
  color: var(--accent);
  cursor: pointer;
}
.sum-pick:hover { background: var(--accent-soft); }
.sum-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; }
.sum-picks .more { padding-left: .15rem; }

.tools { display: flex; flex-wrap: wrap; gap: .35rem; width: 100%; justify-content: center; }

/* A tool is an icon and nothing else. Square, so a row of nine of them fits
   across a phone — and across the narrow control column of the landscape
   layout — where the same row of words wrapped onto three lines. What each one
   does is on its title and its aria-label, never only in the glyph. */
.btn.icon {
  display: inline-grid;
  place-items: center;
  padding: .35rem;
  line-height: 0;
}
.btn.icon svg { display: block; }
.btn.icon.on { color: var(--accent); }

.palette {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  justify-content: center;
  padding: .45rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
}

.swatch {
  width: 2rem;
  height: 2rem;
  border-radius: 7px;
  border: 1px solid var(--line);
  cursor: pointer;
  padding: 0;
  font: inherit;
  color: var(--ink-soft);
}
.swatch:hover { border-color: var(--line-strong); transform: scale(1.06); }
.swatch.swatch-1 { background: var(--swatch-1); }
.swatch.swatch-2 { background: var(--swatch-2); }
.swatch.swatch-3 { background: var(--swatch-3); }
.swatch.swatch-4 { background: var(--swatch-4); }
.swatch.swatch-5 { background: var(--swatch-5); }
.swatch.swatch-6 { background: var(--swatch-6); }
.swatch.swatch-7 { background: var(--swatch-7); }
.swatch.swatch-8 { background: var(--swatch-8); }
.swatch.none { background: var(--surface); font-size: 1.1rem; line-height: 1; }

/* Portrait: one line above the board. Landscape: a column beside it. */
.hud {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: .6rem;
  width: 100%;
  font-size: .9rem;
  flex-wrap: wrap;
}
.hud .timer { font-variant-numeric: tabular-nums; font-weight: 700; font-size: 1.05rem; }
.hud .meta { color: var(--ink-soft); }
.hud .meta.tier { text-transform: capitalize; }
.hud .mistakes.has { color: var(--danger); font-weight: 600; }

/* No connection. Marked rather than merely noted, because it changes what the
   game can do — no hints, a weaker mistake check, a solve recorded later — and
   a player who has not noticed will read all three as things being broken. */
.hud .meta.offline {
  color: var(--danger);
  font-weight: 600;
  font-size: .78rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  border: 1px solid var(--danger);
  border-radius: 999px;
  padding: 0 .45rem;
  line-height: 1.5;
}

/* Snapshot markers: boxed numbers, then "here" while an older board is shown.
   They live at the end of the tool row, after the camera that takes them, and
   are ruled off from it so a row of numbered boxes does not read as four more
   tools. */
.marks { display: flex; flex-wrap: wrap; gap: .25rem; align-items: center; }

.tools .marks {
  margin-left: .2rem;
  padding-left: .45rem;
  border-left: 1px solid var(--line);
}

.mark {
  font: inherit;
  font-size: .8rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  min-width: 1.5rem;
  height: 1.5rem;
  padding: 0 .3rem;
  border-radius: 5px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.mark:hover { border-color: var(--accent); color: var(--accent); }
.mark.on { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
/* Ahead of where the player is standing. Still reachable — it is the redo
   direction — but on borrowed time: the next edit discards it. */
.mark.future {
  border-color: var(--line);
  color: var(--ink-faint);
  background: var(--surface-2);
  font-weight: 500;
}
.mark.future:hover { border-color: var(--ink-faint); color: var(--ink-soft); }

.mark.here { font-size: .72rem; font-weight: 600; border-style: dashed; color: var(--ink-soft); }
.mark.here:hover { color: var(--accent); }

.icon-btn:disabled { opacity: .35; cursor: not-allowed; }
.icon-btn:disabled:hover { background: transparent; border-color: transparent; color: var(--ink-soft); }

@media (orientation: landscape) and (min-width: 48rem) {
  .hud {
    flex-direction: column;
    align-items: flex-start;
    gap: .15rem;
    width: auto;
    min-width: 5.5rem;
    padding-top: .1rem;
  }
  .hud .timer { font-size: 1.5rem; line-height: 1.1; }
  .hud .meta.tier { font-weight: 600; color: var(--ink); }
  .hud .mistakes { margin-top: .35rem; }
}

/* --- Selection panel ------------------------------------------------------- */

.panel { display: grid; gap: .8rem; }

.panel-block {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  padding: .8rem;
}
.panel-block.custom {
  border-color: var(--cage-ink, var(--custom));
  background: color-mix(in srgb, var(--cage-ink, var(--custom)) 11%, var(--surface));
}

.panel-title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem;
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-faint);
  font-weight: 700;
  margin-bottom: .45rem;
}
.panel-block.custom .panel-title { color: var(--cage-ink, var(--custom)); }

.sum-line { font-size: 1.4rem; font-weight: 700; letter-spacing: -.02em; }
.sum-line small { font-size: .8rem; font-weight: 500; color: var(--ink-soft); margin-left: .3rem; }

.combos { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .5rem; }

/* A combination button is ON by default — every combination starts possible —
 * so the lit state is the plain one and switching a combination off grays it
 * out. That way a cage the player has worked on reads at a glance: what is left
 * in strong ink is what is still in play. */
.combo {
  font: inherit;
  font-size: .84rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: .06em;
  padding: .25rem .5rem;
  border-radius: 6px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.combo:hover { border-color: var(--accent); color: var(--accent); }
.combo.off {
  border-color: var(--line);
  background: var(--surface-2);
  color: var(--ink-faint);
  font-weight: 500;
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.combo.off:hover { color: var(--ink-soft); }
.panel-block.custom .combo { border-color: var(--cage-ink, var(--custom)); color: var(--cage-ink, var(--custom)); }
.panel-block.custom .combo.off { border-color: var(--line); color: var(--ink-faint); }
/* The sum choices are a row of numbers, not digit sets: no strike-through, and
   the unchosen ones stay quiet rather than reading as ruled out. */
.combos.sums .combo { letter-spacing: 0; padding: .2rem .45rem; text-decoration: none; }
.combo.dim { opacity: .38; }

/* A folded list of combinations, for a cage loose enough to make one total
   dozens of ways. Closed to start: the count is the useful part, and the
   buttons are there for anyone who wants to look. */
.combo-fold { margin-top: .5rem; }
.combo-fold > summary {
  cursor: pointer;
  font-size: .8rem;
  color: var(--ink-soft);
  padding: .1rem 0;
}
.combo-fold > summary:hover { color: var(--ink); }
.combo-fold > summary:focus-visible { outline: 2px solid var(--cage-ink, var(--custom)); outline-offset: 2px; border-radius: 4px; }
.combo-fold > .combos { margin-top: .35rem; }
.combo-fold > p { margin: .4rem 0 0; }

/* Typing the total, for cages whose row of choices is long enough to be a
   hunt. The field is deliberately narrow — it takes two digits at most. */
.sum-choices { gap: .4rem; }
.sum-entry { align-items: center; gap: .4rem; margin-top: .5rem; }
.sum-input {
  font: inherit;
  width: 4.5rem;
  padding: .2rem .4rem;
  border-radius: 7px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
}
.sum-input:focus-visible { outline: 2px solid var(--cage-ink, var(--custom)); outline-offset: 1px; }
.panel-block.custom .sum-input:focus-visible { border-color: var(--cage-ink, var(--custom)); }
.sum-note { margin: 0; color: var(--danger); }
.sum-choices .combos.sums { margin-top: .1rem; }

.panel-title .title-actions { display: inline-flex; align-items: center; gap: .4rem; }
.cage-delete { margin-top: .6rem; }
.sum-line.muted { color: var(--ink-faint); }

.name-form input { font: inherit; padding: .3rem .5rem; border-radius: 7px; border: 1px solid var(--line); background: var(--surface); color: var(--ink); }

.sum-button {
  font: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  color: inherit;
  background: none;
  border: 1px dashed transparent;
  border-radius: 6px;
  padding: 0 .25rem;
  margin-left: -.25rem;
  cursor: pointer;
}
.sum-button:hover, .sum-button.open { border-color: var(--cage-ink, var(--custom)); }

.panel-block.custom.draft { border-style: dashed; }

.panel-block.solved { border-color: var(--accent); background: var(--accent-soft); }
.panel-block.solved .panel-title { color: var(--accent); }
.tier-choice { margin-top: .5rem; }

.btn.danger { color: var(--danger); border-color: var(--danger); }
.btn.danger:hover:not(:disabled) { background: var(--danger-soft); }
.btn.danger-solid { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn.danger-solid:hover:not(:disabled) { background: var(--danger); filter: brightness(1.08); }
.btn.icon-only { padding: .1rem .45rem; font-size: 1.3rem; line-height: 1; }

.link {
  font: inherit;
  font-size: .85rem;
  background: none;
  border: 0;
  padding: 0;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}
.link:hover { color: var(--accent); text-decoration: underline; }

.panel-block p.why { margin-top: .4rem; }

.custom-cage-list { display: grid; gap: .3rem; margin-top: .5rem; }
.custom-cage-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  font-size: .85rem;
  padding: .25rem .4rem .25rem .55rem;
  border-radius: 6px;
  background: var(--surface);
  /* A stripe in the cage's own color: the list doubles as the legend for the
     outlines on the board and the combination rows under the pad. */
  border-left: 3px solid var(--cage-ink, var(--custom));
}
.custom-cage-row .link { color: var(--cage-ink, var(--ink)); font-weight: 600; }

/* --- Modals ---------------------------------------------------------------- */

dialog.modal {
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
  width: min(94vw, 34rem);
  max-height: min(88svh, 46rem);
  overflow: hidden;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
}
dialog.modal::backdrop { background: rgba(8, 16, 12, .45); }
dialog.confirm { width: min(94vw, 26rem); }

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  padding: .7rem .9rem;
  border-bottom: 1px solid var(--line);
}
.modal-head h2 { margin: 0; font-size: 1.05rem; }

.modal-body { padding: .9rem; overflow-y: auto; }
.modal-body p { margin: 0; }

.modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .5rem;
  padding: .7rem .9rem;
  border-top: 1px solid var(--line);
}
.modal-foot .build-note { margin-right: auto; }

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

.settings { display: grid; gap: 1rem; }
.settings-group .panel-title { margin-bottom: .3rem; }
.settings-list { display: grid; gap: .1rem; }

.settings-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: .6rem;
  padding: .25rem .3rem;
  border-radius: 7px;
}
.settings-row:hover { background: var(--surface-2); }
.settings-label { font-size: .9rem; }

.settings-row input[type="color"] {
  width: 2.2rem;
  height: 1.6rem;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: none;
  cursor: pointer;
}
.settings-row input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
.settings-row input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 3px; }
.settings-row.switch {
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  cursor: pointer;
  padding: .4rem .3rem;
}
.settings-row.switch input { width: 1.05rem; height: 1.05rem; margin-top: .15rem; accent-color: var(--accent); }
.settings-row.switch .settings-label { display: grid; gap: .1rem; }
.settings-row.switch small { color: var(--ink-soft); }

.settings-row .btn:disabled { opacity: 0; pointer-events: none; }
.settings-row:hover .btn:disabled { opacity: .3; }

/* --- Battle ---------------------------------------------------------------- */

.versus { display: grid; gap: .5rem; width: 100%; max-width: 46rem; }
.racer { display: grid; gap: .2rem; }
.racer .label { display: flex; justify-content: space-between; font-size: .85rem; }
.bar { height: .55rem; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.bar > span { display: block; height: 100%; background: var(--accent); width: 0; transition: width .35s ease; }
.racer.them .bar > span { background: var(--custom); }

/* --- Admin ----------------------------------------------------------------- */

/* Fourteen bars, so a trend reads without pulling in a chart library. */
.sparkline {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 3rem;
  padding: .2rem 0;
}
.sparkline .spark {
  flex: 1;
  min-width: 4px;
  background: var(--accent);
  border-radius: 2px 2px 0 0;
  opacity: .8;
}
.sparkline .spark:hover { opacity: 1; }

/* --- Tables ---------------------------------------------------------------- */

table.plain { width: 100%; border-collapse: collapse; font-size: .92rem; }
table.plain th, table.plain td { text-align: left; padding: .45rem .5rem; border-bottom: 1px solid var(--line); }
table.plain th { color: var(--ink-faint); font-size: .78rem; text-transform: uppercase; letter-spacing: .05em; }
table.plain td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* --- Forms ----------------------------------------------------------------- */

.form { display: grid; gap: .6rem; max-width: 22rem; }
.form label { display: grid; gap: .2rem; font-size: .87rem; color: var(--ink-soft); }
.form input {
  font: inherit;
  padding: .5rem .6rem;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
}
.form input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.err { color: var(--danger); font-size: .87rem; }

/* --- How to play ----------------------------------------------------------- */

.terms { display: grid; gap: .7rem; }
.term { border-left: 3px solid var(--accent); padding-left: .8rem; }
.term dt { font-weight: 700; }
.term dd { margin: .15rem 0 0; color: var(--ink-soft); font-size: .93rem; }

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

@media (max-width: 40rem) {
  .brand-text { display: none; }
}
