:root {
  color-scheme: light dark;
  --bg: #f6f8fb;
  --fg: #101a2b;
  --muted: #4a5a72;
  --card: #ffffff;
  --line: #c9d4e2;
  --accent: #15803d;
  --board: #0f1b2a;
  --pad-size: 2.9rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0a1120;
    --fg: #e4ebf5;
    --muted: #9babc2;
    --card: #121d30;
    --line: #26364f;
    --accent: #4ade80;
  }
}

/* Touch devices get larger direction-pad targets, since a finger is wider
   than a mouse pointer and the pad sits right under the board. */
@media (pointer: coarse) {
  :root { --pad-size: 3.4rem; }
}

* { box-sizing: border-box; }

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

main {
  max-width: 40rem;
  margin: 0 auto;
  padding: 1rem 1rem 2rem;
}

h1 { font-size: 1.55rem; margin: 0 0 0.3rem; }
h2 { font-size: 1rem; margin: 1.2rem 0 0.35rem; }

.lead { margin: 0 0 0.9rem; color: var(--muted); }

/* The message shown when JavaScript is switched off (v1.44), written in a
   <noscript> block at the top of the page. Without it the page simply did
   nothing, with no hint that the game needs a script. It is a note, not an
   alarm: card background, a line in the accent colour down its left edge and
   body-coloured text, so it reads plainly in both colour schemes. */
.warn {
  margin: 0 0 0.9rem;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 0.5rem;
  background: var(--card);
  font-size: 0.85rem;
  color: var(--fg);
}

.hud {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.5rem;
}

/* One figure of the score bar. .stat-wide marks the two figures whose words
   are a whole sentence rather than a short label (the speed line and the
   rules line); that class only does anything on a phone, where the bar is a
   grid: see the narrow-screen rules at the foot of this file. */
.stat {
  margin: 0;
  padding: 0.3rem 0.6rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  font-size: 0.9rem;
  color: var(--muted);
}

.stat span {
  font-weight: 700;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
}

/* The live "to beat" note that sits after the score. It is written by
   listnav.js from the two figures the score bar already shows (this job lived
   in a file called gap.js until v1.45, and that name was left in this comment
   until v1.88). Since v2.8 it speaks from the first moment of a run rather
   than waiting for a score: where the setting has no best yet it says so, and
   otherwise it names the figure to beat. Kept muted like the rest of the stat
   so the digits stay the loudest thing in the bar, except in the two states
   worth noticing: the line takes the page's body colour — the colour the
   figures themselves use — when the run has drawn exactly level with its best
   (v1.88), and the accent colour once it has passed it. So the accent is
   still used only by the run that has passed its best, and the counts of what
   is still to do stay quiet. No tabular figures here, since it is words
   rather than a number, and it may wrap after the score on a narrow phone
   rather than squeeze the digits. This comment claimed the note was empty
   until a run had a score to compare until v2.9, when the v2.8 change was
   read back into it. */
.stat .gap {
  font-weight: 600;
  color: var(--muted);
  font-variant-numeric: normal;
}

.stat .gap.is-even { color: var(--fg); }

.stat .gap.is-ahead { color: var(--accent); }

/* The settings sit below the board and pad now, so the board and its Start
   button are the first thing a player meets. The gap keeps them a clear group
   of their own rather than crowding the buttons above. */
.settings {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.6rem;
  align-items: center;
  margin: 1rem 0 0.7rem;
}

.settings label { font-size: 0.9rem; color: var(--muted); }

.settings select {
  font: inherit;
  max-width: 100%;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--card);
  color: var(--fg);
}

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

.settings .check {
  display: flex;
  flex-basis: 100%;
  align-items: flex-start;
  gap: 0.45rem;
  cursor: pointer;
}

.settings .check input[type="checkbox"] {
  width: 1.1rem;
  height: 1.1rem;
  margin: 0.15rem 0 0;
  flex: 0 0 auto;
  accent-color: var(--accent);
}

.settings .check input[type="checkbox"]:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.settings-hint {
  flex-basis: 100%;
  margin: 0;
  font-size: 0.8rem;
  color: var(--muted);
}

/* The stage hugs the canvas at its own size (320, 400 or 560 px, set by the
   board in app.js) and only shrinks when the page is narrower than the board.
   So a wide screen draws the bigger boards genuinely bigger, at one screen
   pixel per board pixel, instead of squeezing every board into one frame.
   It sits right under the score bar, above the pad and the settings. */
.stage {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin: 0.6rem auto 0;
}

canvas {
  display: block;
  margin: 0 auto;
  width: auto;
  max-width: 100%;
  height: auto;
  background: var(--board);
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  touch-action: none;
}

/* The fallback line written inside the <canvas> element in index.html (v1.44),
   which a browser shows only when it cannot draw canvas graphics. The board is
   dark in both colour schemes, so that text needs a light colour of its own
   instead of the page's body colour, or it would be invisible. */
canvas { color: #cbd5e1; }

.overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0.9rem;
  overflow: auto;
  border-radius: 0.75rem;
  background: rgba(7, 13, 24, 0.85);
  color: #eaf1fb;
}

.overlay-title { margin: 0; font-size: 1.15rem; font-weight: 700; }
.overlay-hint { margin: 0; font-size: 0.85rem; color: #b9c6d9; max-width: 20rem; }

/* Best-score list on the overlay: capped so the board is never buried in text.
   The rows themselves scroll rather than the box being cut off, so every
   setting played stays reachable however long the visit runs. The list is a
   focus stop of its own (tabindex in the markup) so those rows can be reached
   from the keyboard too, and it shows the same focus ring as the rest of the
   page when it has the focus. */
.best-list {
  width: 100%;
  max-width: 21rem;
  margin: 0;
  padding: 0.45rem 0.6rem;
  border: 1px solid rgba(185, 198, 217, 0.35);
  border-radius: 0.6rem;
  text-align: left;
}

.best-title {
  margin: 0 0 0.25rem;
  font-size: 0.72rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #b9c6d9;
}

.best-list ul {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 9.5rem;
  overflow-y: auto;
}

.best-list ul:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 0.3rem;
}

/* One row per setting: the score in a fixed left column, the board and rules
   beside it. The label is allowed to shrink (min-width 0) and to break a long
   word, so a narrow overlay cannot push the row wider than its box; it wraps
   onto a second line rather than spilling out or overlapping the score. */
.best-item {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
  min-width: 0;
  font-size: 0.8rem;
  color: #dbe5f2;
}

.best-score {
  flex: 0 0 auto;
  min-width: 2.2rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #bbf7d0;
}

.best-label {
  flex: 1 1 auto;
  min-width: 0;
  line-height: 1.28;
  overflow-wrap: break-word;
}

/* The row of the setting the player is on right now. The overlay is always
   dark, so this uses its own bright green rather than the page accent, which
   is a dark green in the light scheme and would all but vanish here. The only
   width this adds is the small marker before the label, and the label can
   still wrap and shrink, so a narrow row behaves exactly as before. */
.best-item.is-current {
  background: rgba(74, 222, 128, 0.14);
  border-radius: 0.3rem;
}

.best-item.is-current .best-label {
  color: #ffffff;
  font-weight: 600;
}

.best-item.is-current .best-label::before {
  content: "\25B8  ";
  color: #4ade80;
}

/* The line under the best-list rows that says what that small marker means
   (v5.2). Until then the marker before a row was drawn but never explained on
   the overlay itself; a player had to open the Details disclosure to learn
   that it names the setting they are on now. It is static markup, so it
   carries no state and no script writes it: it sits inside the best-list box,
   which the page hides whenever the list is empty, so it never shows without
   rows above it. It is set a shade smaller than the rows and in the same
   muted overlay grey as the box's title, so it reads as a caption rather than
   as another row. The glyph is written as a character in the markup, the same
   one the marker's own rule draws above. */
.best-legend {
  margin: 0.3rem 0 0;
  font-size: 0.7rem;
  color: #b9c6d9;
}

button {
  font: inherit;
  padding: 0.5rem 0.9rem;
  border-radius: 0.5rem;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--fg);
  cursor: pointer;
}

button:hover { border-color: var(--accent); }

button:focus-visible,
select:focus-visible,
canvas:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.pad {
  display: grid;
  grid-template-columns: repeat(3, var(--pad-size));
  grid-template-rows: repeat(2, var(--pad-size));
  gap: 0.35rem;
  justify-content: center;
  margin: 0.8rem auto 0;
}

.pad button { padding: 0; font-size: 1.05rem; line-height: 1; }
.pad-up { grid-column: 2; grid-row: 1; }
.pad-left { grid-column: 1; grid-row: 2; }
.pad-down { grid-column: 2; grid-row: 2; }
.pad-right { grid-column: 3; grid-row: 2; }

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
  margin-top: 0.8rem;
}

/* The Details disclosure under How to play (added in v1.42). With no rule of
   its own it used the browser's default marker and read as ordinary body text,
   so a reader had nothing to say it could be opened. It is now drawn like the
   page's own small buttons: same card background, line and radius, a pointer
   cursor, the accent colour on hover and a visible focus ring for the
   keyboard, and it is only as wide as its own words (fit-content) so it does
   not look like a heading. The marker is tinted with the accent so the closed
   line still reads as something to press. The list inside keeps the browser's
   own indentation and spacing: no fact or line of it was changed. */
details > summary {
  width: fit-content;
  max-width: 100%;
  padding: 0.35rem 0.6rem;
  margin: 0.3rem 0 0.2rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--card);
  color: var(--fg);
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
}

details > summary:hover { border-color: var(--accent); }

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

details > summary::marker { color: var(--accent); }

details[open] > summary { margin-bottom: 0.5rem; }

details > summary::after {
  content: " \2014 press to open";
  font-weight: 400;
  font-size: 0.8rem;
  color: var(--muted);
}

details[open] > summary::after {
  content: " \2014 press to close";
}

.about { margin-top: 1.4rem; color: var(--muted); font-size: 0.92rem; }
.about ul { padding-left: 1.1rem; margin: 0.3rem 0; }
.about li { margin: 0.2rem 0; }
.about a, footer a { color: var(--accent); }

footer {
  text-align: center;
  padding: 0 1rem 2rem;
  color: var(--muted);
  font-size: 0.85rem;
}

.hidden { display: none; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* A phone, or any window this narrow: the overlay is only as wide as the
   board, so a best-score row has very little room beside its number and the
   rows used to wrap into a ragged block. Give back some of the padding, step
   the row type down a little and narrow the number column, so a row of board
   and rules usually fits on one line — and if it still does not, it wraps at
   the middle dot rather than squashing the number. Nothing else changes: the
   board, the pad, the score bar and the buttons keep their usual sizes. */
@media (max-width: 28rem) {
  .overlay { padding: 0.6rem; gap: 0.45rem; }
  .overlay-hint { font-size: 0.78rem; max-width: 100%; }
  .best-list { padding: 0.35rem 0.5rem; max-width: 100%; }
  .best-title { font-size: 0.66rem; }
  /* The caption steps down with the rows beside it (v5.2), so it never reads
     larger than the thing it explains. */
  .best-legend { font-size: 0.66rem; }
  .best-item { font-size: 0.76rem; gap: 0.4rem; }
  .best-score { min-width: 1.9rem; }

  /* The first screen on a phone (v1.57). The board and its Start button are
     the point of the game page, but on a phone the words above the board used
     to push the direction pad and the two buttons below the fold, so a player
     had to scroll before they could use them. The rules below give back some
     of that height: less page padding, a smaller heading and intro line, less
     space around the board, the pad and the buttons, and a score bar that
     becomes two tidy columns instead of three or four ragged rows. Only the
     chrome shrinks — the board keeps its full width for the board, the pad
     buttons keep their fingertip size and every control keeps its type size
     for the numbers — so nothing about the game or the way it is played
     changes. Screens wider than 28rem are untouched. */
  main { padding: 0.8rem 0.8rem 1.6rem; }
  h1 { font-size: 1.3rem; }
  .lead { font-size: 0.92rem; margin-bottom: 0.6rem; }
  .hud {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.3rem;
    margin-bottom: 0.4rem;
  }
  .stat { padding: 0.25rem 0.45rem; font-size: 0.8rem; }
  /* The two figures whose value is a whole sentence rather than a short
     figure — the speed line and the rules line, which can name three rules at
     once — take a row of their own across both columns (v5.1). In a half
     column each of them wrapped into several lines and stretched its row,
     leaving the short figure beside it with a tall empty cell and the bar
     reading ragged. Spanning is the whole change: their padding, type and
     wording are exactly what they were, and the pairs above and below them
     (Score with Level, Dots eaten with the count to the next level, Best here
     with Best level here) still sit two to a row. Wider screens never see
     this rule: there the bar is a flex row and a full row means nothing. */
  .hud .stat-wide { grid-column: 1 / -1; }
  .stage { margin-top: 0.45rem; }
  .pad { gap: 0.3rem; margin-top: 0.55rem; }
  .actions { margin-top: 0.55rem; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
