/* Metro Lines: a light, flat transit-map look (inspired by classic subway diagrams). */
/* Themes. Day is the base: every token is set here, for the page and for any element that carries a theme of
   its own (a swatch in the theme picker). Each theme below then redefines only its colors. The player picks one
   in Settings (src/features.js sets data-theme on <html>). */
:root, [data-theme] {
  --bg: #efece4;
  --paper: #f8f6f0; /* eggshell, not white: white paper and bright lines read as plastic */
  --card: #fdfcf8;
  --ink: #23221f;
  /* Hand-drawn, the land's edges: its ledge and shoreline. Ink by day; after dark, a deep
     shade of the paper, so the land's side faces read as shadow, not glow. */
  --edge: var(--ink);
  /* How strongly the water's white ripples show: firm on light water, soft on dark. */
  --ripple-strength: 0.9;
  --muted: #8b877d;
  --water: #a9d2e4;
  --park: #c6e0a4;
  --park-patch: #bbd998; /* a protected area's patches (UI.drawParkDetail) */
  /* Mountains from above, foot to summit: stone greys that lighten as they climb, then snow. */
  --mountain-low: #c9c7bf;
  --mountain-mid: #d5d3cc;
  --mountain-high: #e2e1db;
  --snow: #ffffff;
  --bridge: #ffffff;
  --bridge-edge: #b9b3a6;
  /* The lines' colors, by name: a map's line carries one (src/palette.js). Colored pencil, not plastic: muted,
     and far enough apart that any two read as two lines. Each theme redefines all eight. */
  --line-red: #c35141; --line-orange: #da8240; --line-yellow: #e3b756; --line-green: #488c58;
  /* Warnings and failures (the editor's "No route found", a publish that failed): each theme's red, a little
     toward its ink so small text still reads. */
  --warning: color-mix(in srgb, var(--line-red) 85%, var(--ink));
  --line-sky: #6aaacf; --line-blue: #3963a1; --line-purple: #73549c;
  --line-pink: #e09baa;
  /* A locked tram line: no player's color, halfway between the ink and the paper, in every theme. */
  --line-tram: color-mix(in srgb, var(--ink) 45%, var(--paper));
  /* Corners: boxy, like subway signage. Controls, cards and dialogs, and small marks like bars and tags. */
  --radius: 5px;
  --radius-lg: 8px;
  --radius-sm: 3px;
  /* Medals, by share of the optimal score (src/results.js). */
  --medal-bronze: #b87333;
  --medal-silver: #a8a9ad;
  --medal-gold: #d4a72c;
  --medal-optimal: #6cc4ea; /* a light diamond blue */
  /* Surfaces and states. Every color below is a token, so a theme only has to redefine this list. */
  --on-ink: #ffffff; /* text and marks on an ink-filled button, tag, toast or switch */
  --link: color-mix(in srgb, var(--ink) 75%, transparent); /* quiet text links; full ink on hover */
  --accent: #1f8fc4; /* links, focus rings, "today", your bar: a deep diamond blue, never a rail line's color */
  --ink-hover: #3d3b36; /* an ink-filled button under the pointer */
  --hover: #ebe7de; /* a card or plain button under the pointer */
  --inactive: #d8d4ca; /* off: a switch's track, an unrated star, another player's bar */
  /* The inside of stops, hubs and line ends: the paper itself, so they read as hollow rings on every theme (they
     were a shade lighter, white on Original Light; Russell, 2026-10-25). */
  --stop: var(--paper);
  --window: #ffffff; /* train and logo car windows */
  --foam: #ffffff; /* a ship's wake */
  --knob: #ffffff;
  /* Small cities' buildings (src/cities.js), mixed from the theme's paper and ink: a pale roof, a shaded side face,
     and windows. Dark themes light some of them (see .building-window.lit). */
  --building-roof: color-mix(in srgb, var(--paper) 86%, var(--ink));
  --building-face: color-mix(in srgb, var(--paper) 66%, var(--ink));
  --building-edge: color-mix(in srgb, var(--paper) 55%, var(--ink));
  --building-window: color-mix(in srgb, var(--paper) 45%, var(--ink));
  --building-lit: var(--building-window);
  /* Trees on protected land (src/trees.js): one muted green, a little deeper than the theme's park color, with a
     faintly darker band for the crown's side, and the trunk in that darker green too. */
  /* Landmarks (src/landmarks.js): a runway's asphalt and marks. */
  --runway: color-mix(in srgb, var(--paper) 45%, var(--ink));
  --runway-mark: var(--paper);
  --tree-top: color-mix(in srgb, var(--park) 70%, #3f7f45);
  --tree-side: color-mix(in srgb, var(--park) 62%, #2e6136);
  color-scheme: light;
}

/* Night: a harbor city after dark. Navy paper, clear deep-blue water, pine-green parks, and lines bright as city
   lights: coral, electric blue, amber, aqua green, violet and tangerine. */
[data-theme="night"] {
  --bg: #12233a;
  --paper: #0c1a2b;
  --card: #172c46;
  --ink: #e3eef6;
  --muted: #8ba5ba;
  --water: #0d5584;
  --park: #1b4630;
  --park-patch: #173d2a;
  --mountain-low: #1d2f48;
  --mountain-mid: #253954;
  --mountain-high: #2e4461;
  --snow: #9db5c9;
  --bridge: #1b3350;
  --bridge-edge: #3c5978;
  --line-red: #ff5d6c; --line-orange: #ff9f43; --line-yellow: #ffc53d; --line-green: #2ed3a4;
  --line-sky: #78d9ff; --line-blue: #4d8dff; --line-purple: #c27dff;
  --line-pink: #ffbbd2;
  --on-ink: #0c1a2b;
  --accent: #2ee6d6;
  --ink-hover: #ffffff;
  --hover: #1d3655;
  --inactive: #29415f;
  --window: #fff1b8;
  --foam: #5fb3c4;
  --knob: #e3eef6;
  color-scheme: dark;
  --edge: #050a11;
  --ripple-strength: 0.35;
}

/* Moonberry: Blossom after dark. Plum paper, dusky periwinkle water, deep mint parks, mauve hills, and
   Blossom's pastel lines brightened to glow: rose, periwinkle, butter, mint, lilac and peach. */
[data-theme="moonberry"] {
  --bg: #251a29;
  --paper: #1c1420;
  --card: #2d2132;
  --ink: #f6e8ef;
  --muted: #b59eb0;
  --water: #2e3b63;
  --park: #263f38;
  --park-patch: #223832;
  --mountain-low: #2e2236;
  --mountain-mid: #3a2c43;
  --mountain-high: #473651;
  --snow: #c9b3cf;
  --bridge: #33263a;
  --bridge-edge: #5a4462;
  --line-red: #ff836f; --line-orange: #ffa07a; --line-yellow: #ffd166; --line-green: #7fd1a0;
  --line-sky: #9ce5ff; --line-blue: #8fa2ff; --line-purple: #c99bff;
  --line-pink: #ffcedf;
  --on-ink: #1c1420;
  --accent: #ff8fb5;
  --ink-hover: #ffffff;
  --hover: #352840;
  --inactive: #42324b;
  --window: #fff0f5;
  --foam: #9aa6e0;
  --knob: #f6e8ef;
  color-scheme: dark;
  --edge: #0b080d;
  --ripple-strength: 0.35;
}

/* Sunset: dusk over the Hudson. Muted apricot paper blushing pink, low lavender water, dusty sage parks, peach
   hills, dusky plum ink and a terracotta accent; the lines a shade deeper and softer than Day's, as in the low light. */
[data-theme="sunset"] {
  --bg: #efdccd;
  --paper: #f6e8dc;
  --card: #faf1e9;
  --ink: #3b2f3f;
  --muted: #94807e;
  --water: #bfbacd;
  --park: #d3d4b3;
  --park-patch: #c8caa6;
  --mountain-low: #e6cfc0;
  --mountain-mid: #ecd9cc;
  --mountain-high: #f2e4d9;
  --snow: #fbf3ed;
  --bridge: #faf1e9;
  --bridge-edge: #c9ad9f;
  --line-red: #b44c3e; --line-orange: #cc7c41; --line-yellow: #d6ae57; --line-green: #448153;
  --line-sky: #67a0c2; --line-blue: #355b93; --line-purple: #694d8f;
  --line-pink: #d393a1;
  --on-ink: #f6e8dc;
  --accent: #c4683f;
  --ink-hover: #54465a;
  --hover: #f1e0d3;
  --inactive: #e3cfc2;
  --window: #ffe2c4;
  --foam: #ffffff;
  --knob: #fbf4ee;
  color-scheme: light;
}

/* Retro: a warm 70s print. Peach paper, sea-glass water, golden parks, and sun-baked lines: vermilion,
   denim, marigold, teal, berry and tangerine. */
[data-theme="retro"] {
  --bg: #f4d9bd;
  --paper: #fbe8d4;
  --card: #fff4e8;
  --ink: #4a2618;
  --muted: #a46f53;
  --water: #a3cfc8;
  --park: #e0cf8c;
  --park-patch: #d4c27c;
  --mountain-low: #e3b996;
  --mountain-mid: #ecc9aa;
  --mountain-high: #f4dac3;
  --snow: #fff6ec;
  --bridge: #fff4e8;
  --bridge-edge: #c98d68;
  --line-red: #e04a2a; --line-orange: #f07a2c; --line-yellow: #f2a51f; --line-green: #3c8e52;
  --line-sky: #5fabd6; --line-blue: #2c5f8a; --line-purple: #7550a4;
  --line-pink: #e697a8;
  --on-ink: #fff4e8;
  --accent: #d0581f;
  --ink-hover: #6b3b26;
  --hover: #f8e0c8;
  --inactive: #efd0b2;
  --window: #ffe9b8;
  --foam: #fff4e8;
  --knob: #fff4e8;
  color-scheme: light;
}

/* Blueprint: the city's planning sheet. Cobalt paper, white line-work, paler blue water, and lines in
   chalky pastels. */
[data-theme="blueprint"] {
  --bg: #1a4580;
  --paper: #1f4f8f;
  --card: #255a9e;
  --ink: #f2f7ff;
  --muted: #a9c3e6;
  --water: #3a7cc4;
  --park: #2f7186;
  --park-patch: #2b687c;
  --mountain-low: #2a5d9c;
  --mountain-mid: #3468a8;
  --mountain-high: #3e73b4;
  --snow: #c9dcf5;
  --bridge: #2a5fa3;
  --bridge-edge: #8fb2e0;
  --line-red: #ff8a80; --line-orange: #ffb380; --line-yellow: #ffe082; --line-green: #a5e3a0;
  --line-sky: #b4f1ff; --line-blue: #8fd3ff; --line-purple: #d1b3ff;
  --line-pink: #ffd3e1;
  --on-ink: #1f4f8f;
  --accent: #ffe082;
  --ink-hover: #ffffff;
  --hover: #2b62a8;
  --inactive: #3a6aa6;
  --window: #fff7d0;
  --foam: #cfe3fa;
  --knob: #f2f7ff;
  color-scheme: dark;
  --edge: #0c2039;
  --ripple-strength: 0.35;
}

/* Neon: a rainy night downtown. Near-black paper, deep blue water, and lines like signs: hot pink, cyan,
   neon yellow, green, violet and orange. */
[data-theme="neon"] {
  --bg: #13111d;
  --paper: #0b0a12;
  --card: #1b1828;
  --ink: #f4f0ff;
  --muted: #9890b3;
  --water: #142a4a;
  --park: #142e26;
  --park-patch: #11281f;
  --mountain-low: #1c1a2a;
  --mountain-mid: #242235;
  --mountain-high: #2d2b41;
  --snow: #8f8aa8;
  --bridge: #1c1a2b;
  --bridge-edge: #46405f;
  --line-red: #ff4833; --line-orange: #ff8a1f; --line-yellow: #ffe83d; --line-green: #39ff88;
  --line-sky: #5fe0ff; --line-blue: #3b8cff; --line-purple: #a259ff;
  --line-pink: #ffb7d4;
  --on-ink: #0b0a12;
  --accent: #ff6bd6;
  --ink-hover: #ffffff;
  --hover: #221f33;
  --inactive: #2d2a42;
  --window: #fff0a8;
  --foam: #4cc9f0;
  --knob: #f4f0ff;
  color-scheme: dark;
  --edge: #040407;
  --ripple-strength: 0.35;
}

/* Blossom: soft and airy. Blush paper, powder-blue water, mint parks, lavender hills, and pastel lines
   (rose, periwinkle, butter, sage, lilac, peach) still bold enough to read. */
[data-theme="blossom"] {
  --bg: #fbeef2;
  --paper: #fff8fa;
  --card: #ffffff;
  --ink: #4a3540;
  --muted: #a08c97;
  --water: #cfe3f6;
  --park: #d9eed6;
  --park-patch: #cbe6c7;
  --mountain-low: #e1d8e8;
  --mountain-mid: #e9e3ef;
  --mountain-high: #f2eef6;
  --snow: #ffffff;
  --bridge: #ffffff;
  --bridge-edge: #dcc5d0;
  --line-red: #ef7361; --line-orange: #f3976a; --line-yellow: #f0bf4c; --line-green: #6db389;
  --line-sky: #8cd0f8; --line-blue: #7a8fe0; --line-purple: #b389df;
  --line-pink: #fcb2c2;
  --on-ink: #ffffff;
  --accent: #d0648e;
  --ink-hover: #644a58;
  --hover: #f8e5ec;
  --inactive: #ecdae2;
  --window: #ffffff;
  --foam: #ffffff;
  --knob: #ffffff;
  color-scheme: light;
}

/* Charcoal: a sodium-lit tunnel. Brown-charcoal paper like soot-stained brick, amber highlights and windows
   like the glow of old tunnel lamps, teal-slate water, sooty olive parks, and the bold lines of a subway map. */
[data-theme="charcoal"] {
  --bg: #26221e;
  --paper: #1e1b18;
  --card: #2e2a25;
  --ink: #ece4d8;
  --muted: #a39887;
  --water: #29474a;
  --park: #323a24;
  --park-patch: #2b3220;
  --mountain-low: #2e2a26;
  --mountain-mid: #37322d;
  --mountain-high: #403a34;
  --snow: #b4aa9b;
  --bridge: #2e2a25;
  --bridge-edge: #5a5248;
  --line-red: #ee352e; --line-orange: #ff6319; --line-yellow: #fccc0a; --line-green: #00a650;
  --line-sky: #4cbcf6; --line-blue: #2f6fde; --line-purple: #8752c7;
  --line-pink: #ff9ab4;
  --on-ink: #1e1b18;
  --accent: #f2a03a;
  --ink-hover: #fff8ee;
  --hover: #35302a;
  --inactive: #3d3730;
  --window: #ffb54d;
  --foam: #7f9a98;
  --knob: #ece4d8;
  color-scheme: dark;
  --edge: #0c0b0a;
  --ripple-strength: 0.35;
}

/* Grove: earth tones. Parchment paper, sage water, moss parks, sandstone hills, and lines in terracotta,
   slate, ochre, forest, plum and burnt orange. */
[data-theme="grove"] {
  --bg: #e8dfcc;
  --paper: #f4eddf;
  --card: #fbf7ee;
  --ink: #3a2e21;
  --muted: #8a7860;
  --water: #9fc1b8;
  --park: #b9c98e;
  --park-patch: #aabd7e;
  --mountain-low: #c9b99d;
  --mountain-mid: #d5c7ad;
  --mountain-high: #e1d6c1;
  --snow: #f8f4eb;
  --bridge: #fbf7ee;
  --bridge-edge: #b3a083;
  --line-red: #b5432f; --line-orange: #d9822b; --line-yellow: #d4ac35; --line-green: #3f7a45;
  --line-sky: #64a2c5; --line-blue: #3f6a8e; --line-purple: #6b4d92;
  --line-pink: #d693a2;
  --on-ink: #fbf7ee;
  --accent: #2f7f7a;
  --ink-hover: #5a4a38;
  --hover: #ece3d1;
  --inactive: #dcd0b9;
  --window: #fff0c6;
  --foam: #eef4f1;
  --knob: #fbf7ee;
  color-scheme: light;
}

/* Color-blind friendly (the "colorBlind" setting, .color-blind on <html>): the Okabe–Ito colors, one for each of the
   palette's names (red, blue, yellow, green, purple, orange, sky), so the lines keep their names; pink, which
   Okabe–Ito lacks, is a pale pink, far lighter than the purple. Under
   simulated protan, deutan and tritan vision their closest pair is 10.9 ΔE00 apart, against 0.4 to 4.7 for the
   themes' own colors (blue against purple). They are the same in every theme, but Blueprint's paper is blue, so its
   blue line is the sky blue (3.5:1 on it, not 1.6:1). An ink outline on every line (.line-casing) lifts the yellow
   and orange off light paper, and each line's number is on its ends and its card. See docs/design.md. */
:root.color-blind {
  --line-cb-red: #d55e00;
  --line-cb-blue: #0072b2;
  --line-cb-yellow: #f0e442;
  --line-cb-green: #009e73;
  --line-cb-purple: #cc79a7;
  --line-cb-orange: #e69f00;
  --line-cb-sky: #56b4e9;
  --line-cb-pink: #f5c3dc;
}
/* Blueprint's blue line is the sky blue, so its sky line goes paler still. */
:root.color-blind[data-theme="blueprint"] { --line-cb-blue: #56b4e9; --line-cb-sky: #b8e4f8; }

* { box-sizing: border-box; }

/* Phones: the page behaves like an app. It scrolls, but never zooms (no pinch, no double-tap: see also the
   viewport tag and src/app.js, for Safari), taps act at once with no grey flash (buttons press in on their own),
   and landscape keeps the font size it was given. */
/* Buttons: quick taps (undo, undo, undo) never zoom either. */
button, a { touch-action: manipulation; }
html { touch-action: pan-x pan-y; -webkit-tap-highlight-color: transparent; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
/* The page is the same paper as the map. A drag never bounces the page or pulls to refresh. */
body {
  overscroll-behavior: none;
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 16px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif;
}

/* One centered column: title, map, then everything to play with below the map. */
/* The game is exactly one screen tall, so every button stays in view. Everything keeps its natural
   height except the map, which takes the space left over (see .board-wrap). A very short window scrolls. */
/* On a phone with a notch or a home bar, the padding grows to keep clear of them (viewport-fit=cover, index.html). */
.game {
  max-width: 600px; height: 100dvh; min-height: 520px; margin: 0 auto; display: flex; flex-direction: column;
  padding: max(10px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right)) max(28px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
}
/* The bottom's wider padding keeps the buttons off the screen's edge. */
/* On a computer's wide screen, a wider column, so the map can grow into the screen's height instead of leaving bands
   above and below it, while a 300px ad still fits each side on a laptop (about 1500px wide). */
@media (min-width: 900px) and (orientation: landscape) {
  .game { max-width: 820px; }
  /* The title row, line cards and buttons as wide as the map (--board-width, app.js), not the whole column. */
  .game > .title-row, .game > .chips, .game > .actions { width: 100%; max-width: var(--board-width, none); margin-inline: auto; }
}
/* Clicks and drags play the game, so they never select text (Safari needs the -webkit- form).
   The dialogs are for reading, so their text stays selectable. iOS: no long-press callout either. */
.game { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.how-to { -webkit-user-select: text; user-select: text; }
.game > * { flex: none; }
/* The logo sits centered on top. The help button keeps to the right edge. */
/* The header is kept tight, so the board gets as much of the screen's height as it can. */
.title-row { position: relative; z-index: 20; text-align: center; margin: 0 0 8px; } /* over the board; the line cards sit close under it */
.title-row { --icon-size: 36px; --icon-gap: 6px; }
.top-buttons { position: absolute; right: 0; top: 50%; transform: translateY(-50%); display: flex; gap: var(--icon-gap); }
.top-buttons.start { right: auto; left: 0; }
.icon-button svg { display: block; width: 20px; height: 20px; margin: auto; fill: currentColor; }
h1 { margin: 0; font-size: 26px; letter-spacing: -0.02em; }
.logo { font-size: 32px; font-weight: 400; }

/* The logo (src/logo.js): "metro" in metro lines, and the second word in Barlow, a type drawn from road signs, with
   corners a little round like the lines'. Self-hosted (fonts/, OFL), and used only for that word. */
@font-face { font-family: Barlow; font-weight: 500; font-display: swap; src: url("../fonts/barlow-500.woff2") format("woff2"); }
/* On a narrow phone the logo shrinks to fit between the buttons on each side. */
#logo { display: block; margin: 0 auto; overflow: visible; max-width: calc(100vw - 4 * (var(--icon-size) + var(--icon-gap)) - 48px); height: auto; }
/* The logo is a link to this very page: clicking it reloads, on the same puzzle. */
.logo-link { display: inline-block; color: inherit; text-decoration: none; border-radius: var(--radius); }
.logo-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.logo-line { fill: none; stroke: var(--ink); stroke-width: 6.5; stroke-linecap: round; stroke-linejoin: round; }
.logo-type { fill: var(--tint, var(--ink)); transition: fill 0.4s; font-family: Barlow, system-ui, -apple-system, "Segoe UI", sans-serif; font-weight: 500; }
/* The car that lays the line on load: the line's color, a paper outline, a window at each end. */
/* The logo's hubs: the board's bullseye, with strokes to suit its thinner lines. */
/* The underline's ends: a solid start dot, an end ring, and the dot that fills the ring when the car arrives. */
.logo-start, .logo-reached { fill: var(--tint, var(--ink)); transition: fill 0.4s; }
.logo-end { fill: var(--paper); stroke: var(--tint, var(--ink)); stroke-width: 3; transition: stroke 0.4s; }
/* A car takes the color it is given as it rides (--car-color), and eases into each new one. */
.logo-car rect { fill: var(--tint, var(--ink)); stroke: var(--paper); stroke-width: 1.8; transition: fill 0.4s; }
/* A car's lines and hubs take its color (--tint) while it rides, and ease back to black when it arrives. */
.logo-tinted .logo-line { stroke: var(--tint, var(--ink)); transition: stroke 0.4s; }
/* Hand-drawn, the route is charcoal (src/logo.js, sketchRoute), shown where its path, stroked in this mask, is. */
.logo-track { fill: var(--tint, var(--ink)); transition: fill 0.4s; }
.logo-route-reveal { fill: none; stroke: white; stroke-width: 12; stroke-linecap: butt; stroke-linejoin: round; }
.logo-car .logo-window { fill: var(--window); stroke: none; opacity: 0.85; }
/* Read by screen readers, never seen. */
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.meta { margin: 0; font-size: 13px; line-height: 1.3; color: var(--muted); }
.icon-button {
  width: var(--icon-size); height: var(--icon-size); border: 0; border-radius: var(--radius);
  background: none; color: var(--ink); font: inherit; font-size: 22px; font-weight: 700; line-height: 1; cursor: pointer;
}

/* The live score sits at the right end of the buttons' bar (see .actions), by Submit and the view button, where the
   pointer already is: as tall as the buttons, the same gap from the view button as the buttons are from each other. */
.score-main { display: grid; justify-items: start; align-items: center; }
.score-line { display: flex; align-items: center; gap: 6px; }
#score { font-size: 44px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; }
#score-unit { display: contents; color: var(--muted); } /* the icon itself is the row's item (see .people) */
#score-unit .people { --size: 32px; }
/* A small link under a score. It keeps its space while hidden, so the lines below never jump. */
.sub-link { border: 0; padding: 0; background: none; font: inherit; font-size: 13px; font-weight: 600; color: var(--link); cursor: pointer; visibility: hidden; }
.sub-link.shown { visibility: visible; }
/* A yes/no dialog: a short question, then a quiet "no" beside the dark "yes". */
.confirm { max-width: 340px; }
.confirm p { margin: 4px 0 0; color: var(--muted); }
/* A "Don't show this again" box under a confirm's words. */
.dont-show { display: flex; align-items: center; gap: 8px; margin-top: 12px; color: var(--muted); font-size: 14px; cursor: pointer; }
.dont-show input { width: 16px; height: 16px; margin: 0; accent-color: var(--ink); }
/* Publish's fields: a label over each text box, and a note under them (a missing name, or what went wrong). */
.publish-field { display: grid; gap: 4px; margin-top: 12px; font-size: 13px; font-weight: 600; color: var(--ink); }
.publish-field small { font-weight: 400; color: var(--muted); }
.publish-field input, .publish-link {
  width: 100%; box-sizing: border-box; padding: 9px 10px; border: 0; border-radius: var(--radius); background: var(--card);
  color: var(--ink); font: inherit; font-size: 15px; font-weight: 400; box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--ink) 18%, transparent);
}
.publish-link { margin-top: 12px; font-size: 13px; }
.publish-note:empty { display: none; }
.publish-note.warning { font-weight: 600; color: var(--warning); }
/* The welcome: the demo map on top, then the title, a sentence, and its two buttons side by side. */
.welcome { text-align: center; }
/* The title's logo, on its own row under "Welcome to". */
.welcome-logo { display: block; margin: 6px auto 0; max-width: 100%; height: auto; overflow: visible; }
.welcome-banner { display: block; width: 100%; height: auto; margin: 4px 0 14px; border-radius: var(--radius-lg); background: var(--paper); }
/* Each line lays its track in turn, then the dot fills its end. */
.welcome-banner .line { stroke-dasharray: 1; animation: welcome-lay 0.7s ease-in-out calc(0.2s + var(--order) * 0.45s) backwards; }
.welcome-banner .terminal-reached { transform-box: fill-box; transform-origin: center; animation: welcome-reach 0.25s ease-out calc(0.9s + var(--order) * 0.45s) backwards; }
.welcome-banner .banner-car { animation: welcome-car 0.35s cubic-bezier(0.3, 1.6, 0.5, 1) calc(2.1s + var(--order) * 0.12s) backwards; }
.welcome-banner.still * { animation: none; }
@keyframes welcome-lay { from { stroke-dashoffset: 1; } }
@keyframes welcome-reach { from { transform: scale(0); } }
@keyframes welcome-car { from { transform: scale(0); opacity: 0; } }
.actions > button.secondary { background: var(--card); color: var(--ink); box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--ink) 18%, transparent); }
/* Reset, undo and redo: compact icon buttons. Everything in the bar, the score too, is the same gap apart. */
.actions .history-button { flex: none; width: 46px; height: 46px; padding: 0; display: grid; place-items: center; }
/* The view button (Best, Optimal or Yours): its arrow over its word. */
/* A little wider than the other tools, so "Optimal" sits with room either side, not pressed to its edges. */
.actions .view-button { width: 60px; align-content: center; gap: 3px; font-size: 11px; font-weight: 600; letter-spacing: 0.01em; }
.view-button svg { width: 16px; height: 16px; }
/* Below your best, Best lights up in the accent color (the bar's only one), and sends out one ring as it does. */
.actions > button.view-button.ready { background: var(--accent); color: var(--on-ink); box-shadow: none; transition-duration: 0.3s; }
.actions .view-button.nudge { animation: view-nudge 0.9s ease-out; }
@keyframes view-nudge {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); }
  100% { box-shadow: 0 0 0 12px transparent; }
}
@media (hover: hover) { .actions > button.view-button.ready:hover { background: var(--accent); filter: brightness(1.1); } }
/* Optimal and Yours (after Submit), and the results' View Optimal: a ring as they appear, then one every few
   seconds, so the way to the best network stays easy to find without flashing. */
.actions .view-button.viewing:not(.nudge), #results-view { animation: view-pulse 6s ease-out infinite; }
@keyframes view-pulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); }
  15%, 100% { box-shadow: 0 0 0 12px transparent; }
}
#results-view { background: var(--accent); color: var(--on-ink); border-color: var(--accent); }
@media (hover: hover) { #results-view:hover { background: var(--accent); filter: brightness(1.1); } }
@media (prefers-reduced-motion: reduce) { .actions .view-button.nudge, .actions .view-button.viewing, #results-view { animation: none; } }
.history-button svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- Line chips: one slim card per line, side by side. Its tiles left, with its points beside it ---------- */
.chips { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 8px; margin-bottom: 12px; }
@media (max-width: 520px) {
  .chips { grid-auto-flow: row; grid-template-columns: 1fr 1fr; }
}
/* A chip never grows past its column (min-width: 0), so with many lines nothing spills into its neighbor. */
.chip {
  display: flex; align-items: center; gap: 8px; min-width: 0; padding: 5px 8px;
  border-radius: var(--radius); transition: background-color 0.2s; color: var(--ink);
}
/* A short diagonal stroke, like a piece of line on the map. */
.swatch { width: 16px; height: 6px; margin: 0 1px; border-radius: 3px; background: var(--chip-color); flex: none; transform: rotate(-45deg); }
/* The selected line's chip: a light wash of its color. */
.chip.selected { background: color-mix(in srgb, var(--chip-color) 12%, transparent); }
/* In the game a card selects its line (src/app.js); the editor's cards are not buttons. */
.chip[role="button"] { cursor: pointer; }
.chip[role="button"]:not(.selected):hover { background: color-mix(in srgb, var(--chip-color) 6%, transparent); }
.chip[role="button"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Hand-drawn, the wash is a charcoal stroke across the card (app.js, UI.charcoalBand), under its text. */
.sketched .chip { isolation: isolate; }
.sketched .chip.selected { background: none; }
.sketched .chip.selected::before {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: color-mix(in srgb, var(--chip-color) 22%, transparent);
  -webkit-mask: var(--chip-charcoal) center / 100% 100% no-repeat; mask: var(--chip-charcoal) center / 100% 100% no-repeat;
}
.line-score { margin-left: auto; display: flex; align-items: baseline; gap: 3px; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--chip-color); }
/* The passengers icon. In a row aligned by baseline its box's bottom edge sits on the numbers' baseline, but the
   figures end 5 units above that edge (of 24), so it is shifted down by that much: the figures' feet stand on the
   baseline. Each place sets its size with --size. */
.people { --size: 15px; width: var(--size); height: var(--size); translate: 0 calc(var(--size) * 5 / 24); flex: none; fill: currentColor; }
.line-score.pending { opacity: 0.45; }
/* Tiles left: a track of slanted segments, one per tile in the budget (--budget). Each tile is a slot up to 10px
   wide with its segment drawn in its middle 60%, so the gaps shrink with the segments and a long budget on a narrow
   card still fits one row (at least 16px of it). The padding leaves room for the slant at each end. Used tiles fade; a preview (.pending)
   shows the tiles a step would spend, or a cut give back, at half strength. The points sit at the card's right end. */
.budget { display: flex; align-items: center; flex: 0 1 calc(var(--budget) * 10px + 8px); min-width: 16px; height: 12px; padding: 0 4px; }
.tile { position: relative; flex: 1 1 0; min-width: 0; max-width: 10px; height: 100%; transition: opacity 0.2s; }
.tile::before { content: ''; position: absolute; inset: 0 20%; border-radius: 2px; background: var(--chip-color); transform: skewX(-30deg); }
/* Hand-drawn, each tick is a small piece of a charcoal track stroke (app.js, UI.charcoalTick), its rough ends its own. */
.sketched .tile::before { inset: 0 10%; border-radius: 0; -webkit-mask: var(--tick) center / 100% 100% no-repeat; mask: var(--tick) center / 100% 100% no-repeat; }
.tile.used { opacity: 0.2; }
.tile.pending { opacity: 0.55; }
.tiles-left { flex: none; font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--chip-color); }
/* The count (left / budget) always shows. On a narrow card (many lines on a mid-width screen) the track shrinks
   first, and on a very narrow one it gives way: the count still says what is left. */
.chip { container-type: inline-size; position: relative; padding-top: 16px; }
/* The line's name over its card's row: small, faint, in capitals (its own line, so the row never wraps). */
.chip-name {
  position: absolute; top: 4px; left: 8px; right: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 9px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--chip-color); opacity: 0.7; line-height: 1.1;
}
/* Color-blind friendly: the line's number, as on its ends. */
.color-blind .chip-name[data-number]::before { content: attr(data-number) " · "; }
/* Out of tiles (app.js, jitter): each tile of the line's track jumps about on its own (--jx1, --jy1, --jx2, --jy2:
   random offsets), and its tiles left shake side to side. */
.budget .tile.jitter { animation: tile-jitter 0.36s linear var(--jdelay, 0ms); }
@keyframes tile-jitter {
  25% { translate: var(--jx1) var(--jy1); } 50% { translate: var(--jx2) var(--jy2); }
  75% { translate: calc(var(--jx1) * -0.6) calc(var(--jy2) * -0.6); }
}
.tiles-left.shake { animation: tiles-shake 0.4s ease-in-out; }
@keyframes tiles-shake { 20% { translate: -4px; } 40% { translate: 4px; } 60% { translate: -3px; } 80% { translate: 2px; } }
@media (prefers-reduced-motion: reduce) { .budget .tile.jitter, .tiles-left.shake { animation: none; } }
@container (max-width: 100px) { .budget { display: none; } }

/* ---------- Buttons ---------- */
.actions { display: flex; gap: 8px; margin-top: 12px; margin-bottom: auto; }
/* The tools (reset, undo, redo) as small outlined icons, then Submit, the one dark button, taking the rest of the
   row, then the score. Submit fades until every line is finished. */
/* One width whatever it holds (--score-width: a two-digit score; the best of any map is 55), so Submit never changes
   size as you play. */
.actions .score-main { --score-width: 98px; flex: none; width: var(--score-width); }
.actions > button {
  flex: 1; font: inherit; font-size: 14px; padding: 9px 14px; border: 0; border-radius: var(--radius);
  background: var(--ink); color: var(--on-ink); cursor: pointer;
}
.actions > button:disabled { opacity: 0.35; cursor: default; }

/* Map key: two columns, drawn with the same classes as the board. */
/* The map key: groups of rows, each a symbol, its name with what it does, and its value on the right. */
.legend { margin: 0 0 14px; }
.legend ul { list-style: none; margin: 0; padding: 2px 14px; background: var(--card); border-radius: var(--radius-lg); }
.legend li { display: grid; grid-template-columns: 30px 1fr auto; align-items: center; gap: 12px; padding: 7px 0; font-size: 15px; line-height: 1.25; }
.legend li + li { border-top: 1px solid color-mix(in srgb, var(--ink) 8%, transparent); }
.legend li[hidden] { display: none; }
.legend svg[data-icon] { width: 30px; height: 30px; overflow: visible; }
.legend b { display: block; font-weight: 600; }
.legend small { display: block; font-size: 13px; color: var(--muted); }
.legend-value { display: flex; align-items: baseline; gap: 3px; font-weight: 700; font-variant-numeric: tabular-nums; }
.legend-value .people { --size: 16px; fill: var(--muted); }
/* The track row's value: today's line colors, as short diagonal swatches. */
.legend-lines { gap: 2px; align-items: center; }

/* How to play: a wide guide in sections, each under a heading and set apart by a dashed rule. A demo shows its map
   beside its words; on a narrow screen, above them. */
.how-to.guide { width: min(580px, calc(100vw - 32px)); max-width: none; }
.guide-goal { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: center; margin-top: 4px; }
.guide-picture { display: block; width: 100%; height: auto; border-radius: var(--radius-lg); }
@media (max-width: 480px) { .guide-goal { grid-template-columns: minmax(0, 1fr); } }
.guide-lead { margin: 4px 0 8px; font-size: 15px; }
/* The tutorial link is the guide's one big button, styled like Submit. */
.guide-tutorial {
  display: inline-block; margin-top: 6px; padding: 12px 26px; border-radius: var(--radius);
  background: var(--ink); color: var(--on-ink); font-size: 17px; font-weight: 700; text-decoration: none;
}
.guide-tutorial:hover { background: var(--ink-hover); }
/* The guide's last line: the about page, quietly. */
.guide-about { margin: 18px 0 0; font-size: 13px; text-align: center; }
.guide-about a { color: var(--muted); }
.guide-section { margin-top: 18px; padding-top: 16px; border-top: 2px dashed color-mix(in srgb, var(--muted) 30%, transparent); font-size: 14px; }
.guide-section h3 { margin: 0 0 8px; font-size: 17px; }
.guide-section p { margin: 0 0 8px; }
.guide .demos { margin: 8px 0 0; }
.guide .demo { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-areas: "map words" "readout words"; column-gap: 14px; align-items: center; }
.guide .demo-map { grid-area: map; }
.guide .demo-readout { grid-area: readout; }
.guide .demo figcaption { grid-area: words; font-size: 14px; }
@media (max-width: 480px) { .guide .demo { grid-template-columns: minmax(0, 1fr); grid-template-areas: "map" "readout" "words"; } }
.guide-stops { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.guide-stops li { display: grid; grid-template-columns: 28px 1fr auto; align-items: center; gap: 10px; }
.guide-stops svg { width: 26px; height: 26px; }
.guide-stops small { color: var(--muted); }
.guide-value { display: flex; align-items: baseline; gap: 2px; font-weight: 700; font-variant-numeric: tabular-nums; }
.guide-value .people { --size: 15px; fill: var(--muted); }
.guide-tips { margin: 0; padding-left: 18px; display: grid; gap: 4px; }
.guide .how-to-close { margin-top: 18px; }
/* A dialog takes focus itself when it opens or is clicked. Its own outline adds nothing: the controls inside
   still show theirs. */
dialog:focus, dialog:focus-visible { outline: none; }
/* Each dialog focuses its title on open (tabindex=-1, autofocus), so no button starts ringed. The title shows no ring. */
dialog h2[tabindex="-1"]:focus { outline: none; }
.how-to { max-width: 420px; max-height: 90dvh; overflow-y: auto; border: 0; border-radius: var(--radius-lg); padding: 20px; background: var(--bg); color: var(--ink); }
/* How to play: animated example maps in one column that scrolls. */
.demos { display: flex; flex-direction: column; gap: 12px; margin: 10px 0 12px; }
.demos:empty { display: none; }
.demo { margin: 0; padding: 10px; background: var(--card); border-radius: var(--radius-lg); font-size: 13px; }
.demo strong { display: block; margin-bottom: 2px; font-size: 14px; }
.demo-map { display: block; width: 100%; height: auto; }
.demo-readout { display: flex; gap: 14px; margin: 6px 2px; font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; }
.demo-readout > span { display: flex; align-items: center; gap: 6px; color: var(--chip-color); }
.demo-riders { display: flex; align-items: baseline; gap: 2px; }
.demo-riders.pending { opacity: 0.45; }
.demo-riders .people { --size: 14px; }
.demo-cursor { fill: none; stroke-linecap: round; transition: transform 0.28s ease-in-out, opacity 0.2s; }
.demo-cursor-halo { stroke: var(--paper); stroke-width: 5; }
.demo-cursor-mark { stroke: var(--ink); stroke-width: 2; }
.demo-cursor.hidden { opacity: 0; }
/* Dragging back along a line, the cursor keeps one steady pace (one tick a tile). */
.demo-cursor.dragging { transition-duration: 0.32s, 0.2s; transition-timing-function: linear; }
.demo-click { fill: none; stroke: var(--ink); stroke-width: 2; transform-box: fill-box; transform-origin: center; animation: demo-click 0.45s ease-out forwards; }
@keyframes demo-click { from { transform: scale(0.4); opacity: 0.6; } to { transform: scale(1.3); opacity: 0; } }
/* "+3": the passengers a step onto a stop adds. It floats up and fades. */
.demo-gain, .gain { fill: var(--ink); font: 700 15px system-ui, sans-serif; text-anchor: middle; animation: demo-gain 0.6s ease-out forwards; }
@keyframes demo-gain { from { transform: translateY(6px); opacity: 0; } 30% { opacity: 1; } to { transform: translateY(-6px); opacity: 0; } }
/* On the board: "+5" and the passengers icon. A paper-colored halo keeps them readable over lines and water. */
.gain { paint-order: stroke; stroke: var(--paper); stroke-width: 4px; stroke-linejoin: round; }
.gain text { text-anchor: start; }
.gain .people { overflow: visible; }
/* A stop's value, shown when clicked: it fades in, holds long enough to read, and fades out. */
.gain.stop-value { animation: stop-value 1.6s ease-out forwards; }
@keyframes stop-value { from { opacity: 0; transform: translateY(4px); } 12%, 80% { opacity: 1; transform: none; } to { opacity: 0; } }
.demo-blocked { fill: none; stroke: var(--line-red); stroke-width: 4; stroke-linecap: round; }
/* The close button stays in the top-right corner while the dialog scrolls. */
.dialog-x {
  position: sticky; top: 0; float: right; z-index: 1; width: 32px; height: 32px; margin: -6px -6px 0 8px;
  border: 0; border-radius: var(--radius); background: var(--card); color: var(--ink); font: 400 22px/1 system-ui, sans-serif;
  cursor: pointer; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
}
.how-to::backdrop { background: rgba(30, 25, 15, 0.35); }
.how-to h2 { margin: 0 0 6px; font-size: 20px; outline: none; }
/* Settings: one row per setting, a label on the left and an on/off switch on the right. */
/* Settings sit straight on the dialog. The switches under a master switch are indented beneath it. */
.settings { margin: 0 0 14px; }
/* A section's small label above its settings, like the dev menu's. */
.settings-label { margin: 12px 0 6px 4px; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
/* The master switch sits apart from the switches it governs, which grey out while it is off. */
.setting-master { border-bottom: 1px solid color-mix(in srgb, var(--muted) 25%, transparent); }
.setting-master ~ .setting { padding-left: 24px; }
.setting:has(:disabled) { opacity: 0.45; cursor: default; }
/* The theme picker: two rows of five swatches (light, then dark), one per theme, painted in that theme's own colors (its paper and three lines), with
   its name under it. The chosen one is ringed; the radio itself is hidden but still takes the keyboard. */
.themes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: 12px; gap: 8px; padding: 6px 4px 10px; }
.theme-option { display: grid; justify-items: center; align-content: start; gap: 5px; font-size: 12px; line-height: 1.2; text-align: center; color: var(--muted); cursor: pointer; }
.theme-option:has(:checked) { color: var(--ink); font-weight: 600; }
.theme-swatch { display: block; width: 100%; max-width: 52px; aspect-ratio: 1; border-radius: var(--radius-lg); }
.theme-paint {
  display: block; width: 100%; height: 100%; border-radius: inherit; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 14%, transparent);
  background: linear-gradient(135deg, transparent 28%, var(--line-red) 28% 37%, transparent 37% 45%, var(--line-blue) 45% 54%,
    transparent 54% 62%, var(--line-yellow) 62% 71%, transparent 71%), var(--paper);
}
.theme-option:has(:checked) .theme-swatch { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ink); }
.theme-option:has(:focus-visible) .theme-swatch { outline: 2px solid var(--accent); outline-offset: 5px; }
.setting { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 4px; font-size: 14px; cursor: pointer; }
.setting small { display: block; color: var(--muted); font-size: 12px; }
.setting-slider input[type="range"] { width: 45%; accent-color: var(--ink); cursor: pointer; }
/* Your name: a plain field on the setting's right, in the card's color. */
.setting-text { cursor: default; }
.setting-text input {
  width: 45%; min-width: 0; height: 34px; padding: 0 10px; border: 0; border-radius: var(--radius); background: var(--card);
  color: var(--ink); font: inherit; font-size: 14px;
}
.setting-text input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The music switch in the top bar: sound waves while on, a cross while off. */
.music-toggle svg path:not(:first-child) { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; }
.music-toggle[aria-pressed="true"] .music-off, .music-toggle[aria-pressed="false"] .music-waves { display: none; }
input[role="switch"] {
  appearance: none; position: relative; flex: none; width: 40px; height: 24px; margin: 0;
  border-radius: 999px; background: var(--inactive); cursor: pointer; transition: background-color 0.15s;
}
input[role="switch"]::before {
  content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--knob); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); transition: translate 0.15s ease-out;
}
input[role="switch"]:checked { background: var(--ink); }
input[role="switch"]:checked::before { translate: 16px 0; background: var(--on-ink); }
input[role="switch"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.how-to-close { display: block; margin: 4px auto 0; padding: 8px 20px; border: 0; border-radius: var(--radius); background: var(--ink); color: var(--on-ink); font: inherit; font-size: 14px; cursor: pointer; }

/* The map's area takes the map's own shape (--aspect, set in app.js from src/board.js) at the column's full width, and shrinks
   when the screen is too short for that. It is a size container, so the map inside is as large as the smaller
   of its width and height. A wide map so leaves no empty band above and below itself: the height it does not
   need goes evenly above the map and below the buttons (the auto margins), and the logo stays at the top. */
.game > .board-wrap { touch-action: none; flex: 0 1 auto; aspect-ratio: var(--aspect, 1); min-height: 0; margin-top: auto; position: relative; container-type: size; display: flex; align-items: center; justify-content: center; }
/* Toast: short messages over the top of the map. Hidden when empty. */
.toast {
  position: absolute; top: 14px; left: 50%; transform: translateX(-50%); margin: 0;
  padding: 8px 14px; border-radius: var(--radius); background: var(--ink); color: var(--on-ink);
  font-size: 14px; font-weight: 600; width: max-content; max-width: calc(100% - 28px); text-align: center; pointer-events: none;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
}
.toast:empty { display: none; }

/* ---------- Board ---------- */
/* The board keeps the map's shape (--aspect, its width over its height, set in app.js from src/board.js) and grows as large
   as the map's area allows in both directions. The frame drawn around it sits 6 units outside, so a small
   inset keeps it inside the area. */
.board-frame { position: relative; width: calc(min(100cqw, 100cqh * var(--aspect, 1)) - 8px); }
/* The terrain layer sits under the board, which draws only the network over it (position keeps it on top). */
/* Each of the board's layers is painted on its own (will-change), so what moves on one (a train, a plane, a ripple's
   frame) never makes the browser paint the others again: the terrain's masks and filters are painted once. */
.terrain, .board, .trains { will-change: transform; }
.terrain { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.board {
  position: relative;
  display: block;
  width: 100%;
  height: auto;
  touch-action: none;
  user-select: none;
  cursor: crosshair;
}
/* A plain ruled border around the map, like a printed subway map. */
/* The frame stays over the tally (z-index 5), so its line and rounded corners stay crisp under the overlay. */
.frame { position: absolute; inset: 0; z-index: 6; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.frame rect { fill: none; stroke: var(--ink); stroke-width: 2; }
/* A thin ring round the map in the selected line's color (--glow, set in app.js). */
.frame .frame-glow { stroke: var(--glow, transparent); stroke-width: 2.5; opacity: 0.55; transition: stroke 0.3s; }
/* Faint lines between two tiles of land, or two of water (gridPath in src/board.js). The Grid lines setting hides them. */
.no-grid .grid { display: none; }
/* The grid at its darkest; Settings' "Grid darkness" fades it (--grid-strength, 0 to 1, src/features.js). */
.grid { fill: none; stroke: color-mix(in srgb, var(--ink) 14%, transparent); stroke-width: 1; pointer-events: none; opacity: var(--grid-strength, 0.35); }
/* Trains and short effects like "+3" run over the map in their own layers, so the board's redraws leave them alone. */
.trains { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; -webkit-user-select: none; user-select: none; }
.train-track { fill: none; stroke: none; }
.train-body { fill: var(--train-color); stroke: var(--paper); stroke-width: 1.8; }
.train-window { fill: var(--window); opacity: 0.85; }
/* Hand-drawn, a car's body (src/trains.js, SKETCH_CAR) in its line's color, under the pen lines. */
.boat-window { fill: var(--window); opacity: 0.85; }
/* Planes and ships (src/arrivals.js): dark like the stop signs, with a paper outline over lines and water. */
/* Passengers (src/passengers.js): tiny people around hubs, with a paper outline over lines. CSS walks them:
   they pop in to wait, walk into the hub and shrink away to board, and walk off fading when they get off. */
.passenger { fill: var(--ink); stroke: var(--paper); stroke-width: 0.9; paint-order: stroke; transition: opacity 0.3s; }
.passenger-figure { transition: scale 0.3s cubic-bezier(0.3, 1.6, 0.5, 1); }
.passenger.arriving { opacity: 0; }
.passenger.arriving .passenger-figure { scale: 0.3; }
.passenger.boarding { opacity: 0; transition: transform 0.55s ease-in, opacity 0.55s ease-in; }
.passenger.boarding .passenger-figure { scale: 0.4; transition: scale 0.55s ease-in; }
.passenger.leaving { transition: transform 1.5s ease-out, opacity 1.5s ease-in; }
.passenger.leaving.gone { opacity: 0; }
.arrival-body { fill: var(--ink); stroke: var(--paper); stroke-width: 1.4; stroke-linejoin: round; }
.arrival-shadow { fill: var(--ink); }
.arrival-cabin { fill: var(--on-ink); opacity: 0.95; }
/* The ferry's funnel, on its deckhouse: the hull's color again. */
.arrival-funnel { fill: var(--ink); }
.arrival-window { fill: var(--ink); opacity: 0.8; }
.arrival-container { fill: color-mix(in srgb, var(--ink) 70%, var(--paper)); stroke: var(--ink); stroke-width: 0.5; }
.arrival-route { fill: none; stroke: none; }
/* The shore: soft waves of water along the coast (UI.drawLand's shore). Not drawn for now. The swell
   below (each wave breathing between half and full height) is off: to turn it on, give .shore
   "animation: 5.2s ease-in-out infinite" and .shore.x / .shore.y "animation-name: swell-x" / "swell-y". */
.shore { fill: var(--water); }
@keyframes swell-x { 0%, 100% { scale: 0.5 1; } 50% { scale: 1 1; } }
@keyframes swell-y { 0%, 100% { scale: 1 0.5; } 50% { scale: 1 1; } }
@media (prefers-reduced-motion: reduce) { .shore { display: none; } }
/* Only the board itself takes the pointer. Its shapes are redrawn on each move, and a shape removed
   under the cursor makes the cursor flicker to the default arrow. Input maps the pointer to a cell anyway. */
#board * { pointer-events: none; }
.paper { fill: var(--paper); }
.water { fill: var(--water); }
.park { fill: var(--park); }
/* Protected land, top down (UI.drawParkDetail): plain rectangles of a second green, and a thin stream. */
.park-patch { fill: var(--park-patch); }
.park-pond { fill: var(--water); }
.park-stream { fill: none; stroke: var(--water); stroke-linecap: round; stroke-linejoin: round; }
/* Mountains, top down (UI.drawRelief): bands of stone that climb from the range's foot to a snowy summit,
   each band a color its filter floods in. A line tunnels through them (see .tunnel-*). */
.mountain { fill: var(--mountain-low); }
.mountain-low { flood-color: var(--mountain-low); }
.mountain-mid { flood-color: var(--mountain-mid); }
.mountain-high { flood-color: var(--mountain-high); }
.mountain-snow { flood-color: var(--snow); }
/* A tunnel is masked out of its line and drawn back as dots, as wide as the line (see tunnelMask). */
/* A tram line's ties: short bars across the middle of its track, in the paper's color. */
.tram-rails { fill: none; stroke: var(--paper); stroke-width: 5; stroke-dasharray: 1.6 5; pointer-events: none; }
.tunnel-cut { fill: none; stroke: black; stroke-width: 14; }
.tunnel-dots { fill: none; stroke: white; stroke-width: 9; stroke-linecap: round; stroke-dasharray: 0 15; }

.line { fill: none; stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; }
/* A line's outline, under it: only in the color-blind friendly look. */
.line-casing { display: none; fill: none; stroke: var(--ink); stroke-width: 12; stroke-linecap: round; stroke-linejoin: round; }
.color-blind .line-casing { display: inline; }
/* A line's number on its start and end, in ink with a paper halo, so it reads on any line color. */
.terminal-number {
  display: none; fill: var(--ink); stroke: var(--paper); stroke-width: 3; paint-order: stroke; font-size: 12px; font-weight: 800;
  text-anchor: middle; dominant-baseline: central; pointer-events: none;
}
.color-blind .terminal-number { display: inline; }
/* The best known network: full-width lines at low opacity, under the player's own lines. */
.bridge-edge { fill: none; stroke: var(--bridge-edge); stroke-width: 19; stroke-linecap: round; stroke-linejoin: round; }
.bridge-deck { fill: none; stroke: var(--bridge); stroke-width: 16; stroke-linecap: round; stroke-linejoin: round; }
/* Trees: seen from above at a slant, like the buildings. */
/* Landmarks: a runway's asphalt with its white marks, and the buildings they bring, in the cities' colors. */
.runway-strip { fill: var(--runway); }
.runway-centerline { fill: none; stroke: var(--runway-mark); stroke-width: 0.9; stroke-dasharray: 3 2.5; }
.runway-threshold { fill: none; stroke: var(--runway-mark); stroke-width: 1.1; }
/* Realistic water: the living water's canvas (src/water-gl.js), over the terrain and under the lines. */
.water-gl { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.tree-top { fill: var(--tree-top); }
.tree-side { fill: var(--tree-side); }
.tree-trunk { fill: var(--tree-side); }
/* Small cities: blocks seen from above at a slant. Placed by the board (settle); once placed, a move to make way
   for a line overshoots a little and settles back, a wobble. It is feedback, so it stays on with animations off;
   for players who ask for less motion, it is a plain glide. At night, some windows glow. */
.city-settled .building { transition: transform 0.6s cubic-bezier(0.34, 1.8, 0.5, 1); }
@media (prefers-reduced-motion: reduce) { .city-settled .building { transition: transform 0.3s ease-out; } }
.building-roof { fill: var(--building-roof); stroke: var(--building-edge); stroke-width: 0.8; }
.building-face { fill: var(--building-face); stroke: var(--building-edge); stroke-width: 0.8; }
.building-window { fill: var(--building-window); }
.building-window.lit { fill: var(--building-lit); }
[data-theme="night"], [data-theme="moonberry"], [data-theme="blueprint"], [data-theme="neon"], [data-theme="charcoal"] {
  --building-lit: #f5c869;
  /* On a dark park, trees take a lighter green, so they still read. */
  --tree-top: color-mix(in srgb, var(--park) 55%, #5f9c5d);
  --tree-side: color-mix(in srgb, var(--park) 60%, #3f7444);
}
/* An overpass: a raised deck, drawn over the line that passes under it, with the line along it back on top. */
.overpass { fill: var(--bridge); stroke: var(--bridge-edge); stroke-width: 2.5; stroke-linejoin: round; }
.overpass-line { stroke-linecap: butt; }

/* Stops are small dots, hubs are bullseyes, as on a subway diagram. */
.station { fill: var(--stop); stroke: var(--muted); stroke-width: 1.8; }
.station.served { stroke: var(--ink); stroke-width: 2.2; }
.harbor, .airfield { fill: var(--stop); stroke: var(--muted); stroke-width: 2.2; }
.harbor.served, .airfield.served { stroke: var(--ink); stroke-width: 2.6; }
/* A park & ride (an experimental mechanic) sits on the edge of protected land: the same ring, with a parking sign's P. */
.park-ride { fill: var(--stop); stroke: var(--muted); stroke-width: 2.2; }
.park-ride.served { stroke: var(--ink); stroke-width: 2.6; }
.parking { fill: none; stroke: var(--muted); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.parking.served { stroke: var(--ink); }
/* A park & ride visit from a tile of water: the deck under both legs of its U. */
.bridge-edge.u-deck { stroke-width: 31; }
.bridge-deck.u-deck { stroke-width: 28; }
/* The archive's mark for airfields: the plane, with a flight's arc over it. */
.flight-mark { fill: none; stroke: var(--muted); stroke-width: 1.4; stroke-dasharray: 1 2.5; stroke-linecap: round; }
/* A link between two ports, muted until a line takes it, then in that line's color: a flight is a dotted curve
   through the air, a ferry a light dotted wake over the water (pale blue, so it reads as water, not as track). */
.flight, .ferry { fill: none; stroke: var(--muted); stroke-width: 2.5; stroke-dasharray: 2 7; stroke-linecap: round; stroke-linejoin: round; opacity: 0.7; }
.ferry { stroke: color-mix(in srgb, var(--water), #fff 60%); stroke-width: 2.5; stroke-dasharray: 0.5 6; opacity: 1; }
.flight.owned { stroke-width: 3.5; opacity: 1; }
.ferry.owned { stroke-width: 3; }
/* A port's sign: a plane, or a ferry. A same-color round stroke softens every tip. */
.plane, .boat { fill: var(--muted); stroke: var(--muted); stroke-width: 1; stroke-linejoin: round; }
/* A ferry's windows are holes, so its outline stays thin, to keep them open. */
.boat { fill-rule: evenodd; stroke-width: 0.5; }
.plane.served, .boat.served { fill: var(--ink); stroke: var(--ink); }
/* The ferry sign (UI.drawFerry), in 2.5D: the faces toward us in the boat's color (a line's, when one takes its link),
   the ends to the bow lighter, the tops lighter still, the windows the paper's. */
.ferry-sign { --boat-ink: var(--muted); }
.ferry-sign.served { --boat-ink: var(--ink); }
.ferry-side { fill: var(--boat, var(--boat-ink)); }
.ferry-end { fill: color-mix(in srgb, var(--boat, var(--boat-ink)) 72%, var(--paper)); }
.ferry-top { fill: color-mix(in srgb, var(--boat, var(--boat-ink)) 45%, var(--paper)); }
.ferry-window { fill: var(--paper); }
.hub-outer { fill: var(--stop); stroke: var(--ink); stroke-width: 3; }
.hub-inner { fill: none; stroke: var(--ink); stroke-width: 2.4; }
/* Hand-drawn (src/sketches.js, UI.drawSketch): a sketch's paper silhouette under its pen ink. A building's paper is
   the land's, so it hides the grid behind it; a ring's is a stop's. */
.sketch-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.sketch-paper { fill: var(--stop); }
.building .sketch-paper { fill: var(--paper); }
.sketch-ink { fill: var(--ink); }
/* A hand-drawn station is ink black, served or not, like the buildings round it. */
.station-ink { fill: var(--ink); }
/* The hand-drawn frame's strokes are ink, not the plain frame's outline; the grid's are faint. */
.frame .frame-sketch .sketch-ink { fill: var(--ink); stroke: none; }
.thickets { pointer-events: none; }
/* Hand-drawn, the ledge under land, over the water (src/board.js, drawLedge): solid ink. */
/* Hand-drawn, the thin ink line where land meets the water (src/board.js, drawShoreline). */
.shoreline { fill: var(--edge); pointer-events: none; }
/* Hand-drawn, the ink outline round each line (UI.outlineFilter): ink, not the edge, so it's light after dark. */
.outline-ink { flood-color: var(--ink); }
.ledge { fill: var(--edge); pointer-events: none; }
/* Hand-drawn, the water's ripples (src/board.js, ripplesOf): white pen marks, faint, so they read as a lighter blue. */
.ripples { opacity: var(--ripple-strength); pointer-events: none; }
.boat-ink { fill: var(--muted); }
.boat-ink.served { fill: var(--ink); }
.boat-lines { fill: var(--paper); }
/* A hand-drawn line's newest step draws in, growing from where it starts (src/board.js, drawSketchedRun). */
.track-grow { transform-box: fill-box; transform-origin: left center; animation: track-grow 0.18s ease-out; }
@keyframes track-grow { from { transform: scaleX(0); } }
@media (prefers-reduced-motion: reduce) { .track-grow { animation: none; } }
.frame .frame-glow.frame-sketch .sketch-ink { fill: var(--glow, transparent); transition: fill 0.3s; }
.frame .frame-glow.frame-sketch { opacity: 0.55; }
/* The hand-drawn selected ring boils: its frames take turns, one at a time (three frames: each shows a third of
   --boil-period; --boil-name picks another count's keyframes). With less motion, the first frame stays. */
.selected-tip-sketch { opacity: 0.5; pointer-events: none; }
.boil-frame:not(:first-child) { visibility: hidden; }
@media (prefers-reduced-motion: no-preference) {
  .boil-frame { animation: var(--boil-name, boil) var(--boil-period) step-end infinite; }
}
@keyframes boil { 0% { visibility: visible; } 33.333%, 100% { visibility: hidden; } }
/* Two frames (--boil-name: boil-2, a ferry's two boats): each shows half the time. */
@keyframes boil-2 { 0% { visibility: visible; } 50%, 100% { visibility: hidden; } }
/* The water's ripples' frames, each a layer of its own (drawRipples), take turns by opacity, not visibility: each is painted once, so
   none is ever blank while it repaints. With less motion, the first frame stays. */
.ripple-frame.boil-frame { visibility: visible; will-change: opacity; }
.ripple-frame:not(.ripple-first) { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  .ripple-frame.boil-frame { animation-name: ripple-boil; }
}
@keyframes ripple-boil { 0% { opacity: 1; } 33.333%, 100% { opacity: 0; } }
.grid-sketch .sketch-ink { fill: color-mix(in srgb, var(--ink) 30%, transparent); }
/* No outline: a line joins its start dot with no gap. */
.terminal-start { stroke: none; }
.terminal-end { fill: var(--stop); stroke-width: 5; }
/* A finished line: a dot in its end ring. Still a ring, so its end never looks like its start. */
.terminal-reached { stroke: none; }
/* Under a drawing finger: where the line's tip is (src/board.js, render). */
.tip-halo {
  fill: color-mix(in srgb, var(--halo) 22%, transparent); stroke: color-mix(in srgb, var(--halo) 60%, transparent); stroke-width: 2; pointer-events: none;
  transform-box: fill-box; transform-origin: center; animation: halo-grow 0.22s ease-out both;
}
/* It grows out of the selected ring (--from: that ring's size, as a share of the halo's). */
@keyframes halo-grow { from { transform: scale(var(--from)); opacity: 0.4; } }
@media (prefers-reduced-motion: reduce) { .tip-halo { animation: none; } }
/* Previews (src/board.js, drawGhosts): faint track a click would lay; dotted, track a move takes away; a ✕ where
   a step is refused. */
.ghost { fill: none; stroke: var(--ghost); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; opacity: 0.3; pointer-events: none; }
.ghost-cut { stroke-dasharray: 0.1 14; opacity: 0.5; }
.ghost-refused { fill: none; stroke: var(--muted); stroke-width: 3; stroke-linecap: round; opacity: 0.7; pointer-events: none; }
/* The selected line's tip: a soft ring around it (see render). */
.selected-tip { fill: none; stroke-width: 3; opacity: 0.45; pointer-events: none; }

.pop { transform-box: fill-box; transform-origin: center; animation: pop 0.35s cubic-bezier(0.3, 1.6, 0.5, 1); }
@keyframes pop { from { transform: scale(0.5); } to { transform: scale(1); } }

/* ---------- Archives: a puzzle browser. A tab per set, a toolbar, then its puzzles as a grid of cards or a list ---------- */
.archives { width: min(880px, calc(100vw - 32px)); max-width: none; height: 90dvh; } /* room for a grid of maps. A fixed height: switching sets never resizes it */
/* The toolbar: sort on the left, then Filter, and the grid/list switch at the right end. */
.archive-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.archive-toolbar select {
  height: 34px; padding: 0 10px; border: 0; border-radius: var(--radius); background: var(--card); color: var(--ink);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.archive-toolbar select:disabled { opacity: 0.5; cursor: default; }
.archive-tool {
  display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 10px; border: 0; border-radius: var(--radius);
  background: var(--card); color: var(--muted); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.archive-tool svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.archive-tool:is([aria-pressed="true"], [aria-expanded="true"], .active) { background: var(--ink); color: var(--on-ink); }
.archive-layouts { display: flex; gap: 3px; margin-left: auto; }
.archive-create { text-decoration: none; color: var(--ink); }
/* On a phone the toolbar is full: Create keeps only its pencil. */
@media (max-width: 480px) { .archive-create span { display: none; } }
/* The filters: a chip per feature, in one of three states. Any: outlined. Must have: filled, with a check. Hidden:
   dimmed and struck through. */
.archive-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: -2px 0 12px; }
.archive-filters[hidden] { display: none; }
.archive-chip {
  padding: 5px 10px; border: 1.5px solid var(--ink); border-radius: 999px; background: none; color: var(--ink);
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
}
.archive-chip.required { background: var(--ink); color: var(--on-ink); }
.archive-chip.required::before { content: '✓ '; }
.archive-chip.hidden { border-color: var(--muted); color: var(--muted); text-decoration: line-through; opacity: 0.7; }
/* The status chips on their own row, above the features'. */
.archive-filter-break { flex-basis: 100%; height: 0; }
.archive-grid.switching { animation: archive-switch 0.25s ease-out; }
@keyframes archive-switch { from { opacity: 0; translate: 0 6px; } }
@media (prefers-reduced-motion: reduce) { .tab-highlight.sliding, .archive-grid.switching { transition: none; animation: none; } }
/* Difficulty tabs: one segmented control, every segment the same width. Name on top, size and lines under it. */
/* Tabs: one segmented control, every segment the same width (the archives' sets, the guide's sections). */
.tabs { position: relative; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 3px; margin: 4px 0 12px; padding: 4px; border-radius: var(--radius-lg); background: var(--card); }
/* The selected tab's background: one shape that slides to whichever tab is chosen (src/archives.js). */
.tab-highlight { position: absolute; top: 4px; bottom: 4px; left: 0; border-radius: var(--radius); background: var(--ink); pointer-events: none; }
.tab-highlight.sliding { transition: translate 0.28s cubic-bezier(0.2, 0.8, 0.2, 1), width 0.28s cubic-bezier(0.2, 0.8, 0.2, 1); }
.tab { position: relative; display: grid; justify-items: center; gap: 1px; padding: 7px 6px; border: 0; border-radius: var(--radius); background: none; color: var(--muted); font: inherit; font-size: 14px; font-weight: 700; cursor: pointer; white-space: nowrap; transition: color 0.2s ease-out; }
.tab small { font-size: 11px; font-weight: 500; opacity: 0.8; white-space: normal; text-wrap: balance; line-height: 1.25; text-align: center; }
/* However many tabs there are, they share the row: each may shrink, its subtitle wraps, and a long name ends in "…".
   The dialog never scrolls sideways. */
.tab { min-width: 0; padding-inline: 4px; }
.tab > span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
.tab[aria-selected="true"] { color: var(--on-ink); }
/* The special stops a map has: their symbols alone, in the card's words. */
.archive-kinds { display: flex; gap: 6px; margin-top: 4px; }
.archive-kinds:empty { display: none; }
.stop-icon { width: 15px; height: 15px; overflow: visible; }
/* A terrain's swatch, outlined so a pale one still shows on the card. After the last swatch, a little more room
   sets the terrain apart from the things on the map. */
.terrain-swatch { fill: none; stroke: color-mix(in srgb, var(--ink) 18%, transparent); stroke-width: 1.2; }
.archive-kinds .stop-icon:has(.terrain-swatch) + .stop-icon:not(:has(.terrain-swatch)) { margin-left: 6px; }
.tram-icon { fill: none; stroke: var(--line-tram); stroke-width: 6; stroke-linecap: round; }
/* The grid: as many columns of cards as fit, each with its map large on top. Section headings span the row. */
.archive-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(min(190px, 40vw), 1fr)); }
.archive-grid > :is(.archive-section, .archive-empty) { grid-column: 1 / -1; }
.archive-grid > li { display: flex; }
.archive-card {
  display: grid; align-content: start; gap: 10px; width: 100%; padding: 10px 10px 12px;
  border: 0; border-radius: var(--radius-lg); background: var(--card); color: var(--ink); font: inherit; text-align: left; cursor: pointer;
}
.archive-card.current { box-shadow: inset 0 0 0 2px var(--ink); }
.archive-preview { display: block; order: -1; width: 100%; height: auto; aspect-ratio: 1; }
/* The list: one puzzle per row, its words on the left and a small map on the right. */
.archive-grid.list { grid-template-columns: 1fr; gap: 8px; }
.archive-grid.list .archive-card { grid-template-columns: 1fr 88px; align-items: center; gap: 14px; padding: 12px 12px 12px 16px; }
.archive-grid.list .archive-preview { order: 0; width: 88px; height: 88px; }
.archive-words { display: grid; gap: 1px; min-width: 0; }
.archive-words strong { font-size: 16px; }
.archive-subtitle { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
.archive-words small { color: var(--muted); font-size: 12px; }
.archive-tags { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 6px; }
.archive-tags:empty { display: none; }
/* Players' totals: plays, average stars and difficulty, quiet under the rest. */
.archive-words .archive-stats { margin-top: 4px; font-size: 11px; color: var(--muted); }
.archive-stats:empty { display: none; }
.archive-section { display: flex; align-items: center; gap: 6px; margin: 10px 0 -2px 4px; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
/* A small round "i" by a section's heading, as light as the heading. */
.section-info {
  display: grid; place-items: center; width: 16px; height: 16px; padding: 0; border: 1.5px solid currentColor; border-radius: 50%;
  background: none; color: inherit; font: italic 700 10px/1 Georgia, serif; letter-spacing: 0; text-transform: none; cursor: pointer;
}
.section-info:hover { color: var(--ink); }
.archive-section:first-child { margin-top: 2px; }
.archive-empty { padding: 28px 12px; text-align: center; color: var(--muted); font-size: 14px; background: var(--card); border-radius: var(--radius-lg); }
.archive-tag { padding: 1px 6px; border-radius: var(--radius-sm); font-size: 10px; font-weight: 600; background: var(--bg); color: var(--muted); }
.archive-tag.playing { background: var(--ink); color: var(--on-ink); }
/* Where you are with a map: new is accented, in progress outlined, submitted quiet with a check. */
.archive-tag.new { background: color-mix(in srgb, var(--accent) 18%, var(--card)); color: var(--ink); }
.archive-tag.started { background: none; box-shadow: inset 0 0 0 1px var(--muted); color: var(--ink); }
.archive-tag.submitted::before { content: '✓ '; }
.archive-tag.today { background: var(--accent); color: var(--on-ink); }
/* A played day: your score at the bottom, and the card outlined and tinted in your medal's color. */
.archive-score { display: flex; align-items: baseline; gap: 5px; margin-top: 6px; font-size: 15px; font-variant-numeric: tabular-nums; }
.archive-score .people { fill: var(--muted); }
.archive-score .medal { align-self: center; }
.archive-card.scored { box-shadow: inset 0 0 0 2px var(--medal-color); background: color-mix(in srgb, var(--medal-color) 12%, var(--card)); }

/* ---------- Dev menu (trials only): the DEV button in the bottom-left corner opens a small card above it ---------- */
body:not(.dev) .dev-fab { display: none; }
.dev-fab {
  position: fixed; left: 12px; bottom: 12px; z-index: 10; padding: 7px 12px; border: 0; border-radius: var(--radius);
  background: var(--ink); color: var(--on-ink); font: 700 11px/1 system-ui, sans-serif; letter-spacing: 0.08em; cursor: pointer;
  box-shadow: 0 4px 14px rgba(30, 25, 15, 0.22);
}
/* A popover: it sits in the top layer, and closes on Esc or a click outside. It fades and rises in. */
.dev-panel {
  position: fixed; inset: auto auto 50px 12px; margin: 0; width: min(290px, calc(100vw - 24px)); padding: 14px;
  /* No taller than the window above its button: the rest scrolls, and a scroll stays in the panel. */
  max-height: calc(100dvh - 62px); overflow-y: auto; overscroll-behavior: contain;
  border: 0; border-radius: var(--radius-lg); background: var(--card); color: var(--ink); font-size: 13px;
  box-shadow: 0 14px 40px rgba(30, 25, 15, 0.22), 0 0 0 1px color-mix(in srgb, var(--ink) 6%, transparent);
  transition: opacity 0.15s, translate 0.15s, display 0.15s allow-discrete, overlay 0.15s allow-discrete;
}
.dev-panel:popover-open { display: grid; gap: 14px; }
.dev-panel:not(:popover-open) { opacity: 0; translate: 0 6px; }
@starting-style { .dev-panel:popover-open { opacity: 0; translate: 0 6px; } }
.dev-head { display: grid; gap: 2px; }
.dev-head strong { font-size: 15px; }
.dev-info { color: var(--muted); font-size: 12px; }
.dev-section { display: grid; gap: 6px; }
/* Rating (src/rating-input.js). Stars: grey, with gold drawn over each up to its fill (whole, or half). Hovering
   previews a value. Difficulty: five dots on a line, the picked one filled, greener-gold to red as they climb, with
   its ends and middle named underneath. */
.dev-rating { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.rating-stars { display: inline-flex; border-radius: var(--radius); cursor: pointer; touch-action: manipulation; }
.rating-star { position: relative; padding: 0 2px; font-size: 26px; line-height: 1; color: var(--inactive); }
.rating-star::after {
  content: '★'; position: absolute; inset: 0; padding: inherit; color: var(--medal-gold);
  clip-path: inset(0 calc(100% - var(--fill, 0%)) 0 0);
}
.rating-scale {
  --dot: 18px; position: relative; display: grid; grid-template-columns: repeat(5, 44px); justify-items: center; row-gap: 4px;
}
/* The line joining the dots, from the first dot's centre to the last's. */
.rating-scale::before {
  content: ''; position: absolute; top: calc(var(--dot) / 2 + 13px - 1px); left: 22px; right: 22px; height: 2px;
  background: var(--inactive); border-radius: 1px;
}
.rating-level {
  position: relative; width: 44px; height: 44px; padding: 0; border: 0; background: none; cursor: pointer;
  margin-bottom: -13px; touch-action: manipulation;
}
.rating-level::after {
  content: ''; position: absolute; left: 50%; top: 13px; width: var(--dot); height: var(--dot); translate: -50% 0;
  box-sizing: border-box; border: 2px solid var(--inactive); border-radius: 50%; background: var(--card);
  transition: background 0.15s, border-color 0.15s, scale 0.15s;
}
/* A picked dot's color runs green (Easy), through yellow (Medium), to red (Hard): halfway between at the two
   "fairly"s. */
.rating-level.on::after { background: var(--picked); border-color: var(--picked); scale: 1.15; }
.rating-level:nth-child(1) { --picked: var(--line-green); }
.rating-level:nth-child(2) { --picked: color-mix(in srgb, var(--line-green), var(--medal-gold)); }
.rating-level:nth-child(3) { --picked: var(--medal-gold); }
.rating-level:nth-child(4) { --picked: color-mix(in srgb, var(--medal-gold), #d9534f); }
.rating-level:nth-child(5) { --picked: #d9534f; }
.rating-name { grid-row: 2; grid-column: var(--column); font-size: 12px; font-weight: 600; color: var(--muted); white-space: nowrap; }
@media (hover: hover) { .rating-level:not(.on):hover::after { border-color: var(--muted); } }
.dev-scale-ends { margin-left: 6px; font-weight: 500; letter-spacing: 0; text-transform: none; }
#dev-copy-ratings { font: inherit; font-size: 12px; padding: 6px 10px; border: 0; border-radius: var(--radius); background: var(--bg); color: var(--ink); cursor: pointer; }
.dev-label { margin: 0; font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.dev-row { display: flex; gap: 6px; }
/* The review card on a pool's candidate: bottom right, out of the game's way. */
.dev-review {
  position: fixed; inset: auto 12px 12px auto; z-index: 20; display: grid; gap: 6px; padding: 8px;
  border-radius: var(--radius-lg); background: var(--card); color: var(--ink); font-size: 12px;
  box-shadow: 0 6px 20px rgba(30, 25, 15, 0.18), 0 0 0 1px color-mix(in srgb, var(--ink) 6%, transparent);
}
.dev-review[hidden] { display: none; }
.dev-review-info { padding: 0 2px; color: var(--muted); white-space: nowrap; }
.dev-review-buttons { display: flex; gap: 4px; }
.dev-review select {
  flex: 1; padding: 4px 6px; border: 0; border-radius: 7px; background: var(--hover); color: inherit; font: inherit; cursor: pointer;
}
.dev-review button {
  padding: 4px 8px; border: 0; border-radius: 7px; background: var(--hover); color: inherit; font: inherit; cursor: pointer;
}
.dev-review button:disabled { opacity: 0.4; cursor: default; }
.dev-review .dev-review-approve { background: #2e7d4f; color: #fff; }
.dev-review .dev-review-reject { background: #c0392b; color: #fff; }
.dev-review-buttons[hidden], .dev-review-why[hidden] { display: none; }
.dev-review-why { display: grid; gap: 6px; width: 260px; }
.dev-review-why input {
  padding: 6px 8px; border: 1px solid color-mix(in srgb, var(--ink) 15%, transparent); border-radius: 7px;
  background: var(--paper, transparent); color: inherit; font: inherit;
}
.dev-review-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.dev-review-chips button { padding: 2px 7px; font-size: 11px; }
.dev-review-why .dev-review-buttons { justify-content: flex-end; }

/* The daily queue and the pool: a row per day or candidate, its buttons at the end. Under three weeks shows red. */
.dev-queue { display: grid; gap: 2px; max-height: 260px; overflow-y: auto; }
.dev-queue summary { cursor: pointer; font-weight: 600; padding: 2px 0; }
.dev-queue-runway { font-weight: 600; padding-bottom: 2px; }
.dev-warn { color: #c0392b; }
.dev-queue-row { display: flex; align-items: center; gap: 4px; font-size: 12px; }
.dev-queue-row span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dev-queue-row.dev-current span { font-weight: 700; }
.dev-queue-row button {
  flex: none; padding: 2px 6px; border: 0; border-radius: 6px; background: var(--hover); color: inherit; font: inherit; cursor: pointer;
}
.dev-row button {
  flex: 1; padding: 7px 10px; border: 0; border-radius: var(--radius); background: var(--bg); color: var(--ink);
  font: inherit; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.dev-row button:disabled { opacity: 0.4; cursor: default; }
/* Extras: one row per switch, like the settings. Vanilla first; it greys out the rest. */
.dev-features { display: grid; padding: 2px 0; border-radius: var(--radius-lg); background: var(--bg); }
.dev-switch { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 6px 10px; cursor: pointer; }
.dev-switch:first-child { border-bottom: 1px solid color-mix(in srgb, var(--muted) 25%, transparent); }
.dev-switch:has(:disabled) { opacity: 0.4; cursor: default; }
.dev-hint { margin: 0; font-size: 11px; color: var(--muted); }
kbd { padding: 0 5px; border-radius: 4px; background: var(--bg); font: 600 11px ui-monospace, monospace; }

/* ---------- Interaction states ----------
   Hover styles apply only where a real pointer can hover, so touch screens never keep a stuck highlight.
   Every button also presses in slightly, and shows a ring when reached with the keyboard. */
button { transition: background-color 0.12s, box-shadow 0.12s, color 0.12s, transform 0.08s; }
@media (hover: hover) {
  .icon-button:hover, .dialog-x:hover { background: var(--bg); box-shadow: 0 0 0 2px color-mix(in srgb, var(--ink) 8%, transparent); }
  .actions > button:not(:disabled):hover, .how-to-close:hover { background: var(--ink-hover); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18); }
  .actions > button.secondary:hover { background: var(--bg); box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--ink) 30%, transparent); }
  .sub-link:hover { color: var(--ink); }
  .dev-row button:not(:disabled):hover, .archive-card:hover, .tab:not([aria-selected="true"]):hover { background: var(--hover); }
  .dev-fab:hover { background: var(--ink-hover); }
}
button:not(:disabled):active { transform: scale(0.97); }
/* A finger is wider than a cursor: on touch screens the small buttons and links take taps from a little
   around them too, without changing how they look. */
@media (pointer: coarse) {
  :is(.icon-button, .sub-link) { position: relative; } /* .dialog-x is sticky, which holds its ::after already */
  :is(.icon-button, .dialog-x)::after { content: ""; position: absolute; inset: -5px -3px; }
  .sub-link::after { content: ""; position: absolute; inset: -4px -8px; }
}
button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  button { transition: none; }
  button:not(:disabled):active { transform: none; }
}

/* ---------- Results ---------- */
/* The results ease in after the victory lap: the box rises and fades in, the backdrop dims softly. */
.results[open] { animation: results-in 0.35s ease-out; }
.results[open]::backdrop { animation: backdrop-in 0.35s ease-out; }
@keyframes results-in { from { opacity: 0; transform: translateY(12px) scale(0.98); } }
@keyframes backdrop-in { from { opacity: 0; } }
/* After the tally: the results only fade in (no slide), so their score stays where the tally's total glided to. */
.results.from-tally[open], .results.from-tally[open]::backdrop { animation: backdrop-in 0.35s ease-out; }
.results.arriving[open], .results.arriving[open]::backdrop { opacity: 0; animation: none; }
@media (prefers-reduced-motion: reduce) { .results[open], .results[open]::backdrop { animation: none; } }
#score { display: inline-block; transform-origin: left center; }
.results { text-align: center; width: min(400px, calc(100vw - 32px)); }
.results-score { display: grid; justify-items: center; gap: 2px; margin: 8px 0 6px; padding: 12px; background: var(--card); border-radius: var(--radius-lg); }
/* The score's box takes a light wash of your medal's color (data-medal, src/finale.js). */
.results-score[data-medal="bronze"] { background: color-mix(in srgb, var(--medal-bronze) 22%, var(--card)); }
.results-score[data-medal="silver"] { background: color-mix(in srgb, var(--medal-silver) 22%, var(--card)); }
.results-score[data-medal="gold"] { background: color-mix(in srgb, var(--medal-gold) 22%, var(--card)); }
.results-score[data-medal="optimal"] { background: color-mix(in srgb, var(--medal-optimal) 22%, var(--card)); }
.results-score small, .results-heading { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.results-score strong { font-size: 52px; line-height: 1.05; font-variant-numeric: tabular-nums; }
.results-total { display: flex; align-items: baseline; gap: 8px; }
#results-score-unit { display: contents; }
#results-score-unit .people { --size: 38px; fill: var(--muted); }
#results-rank { font-size: 17px; font-weight: 600; }
.results-tiers { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 10px; margin: 0 0 12px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
#results-rank, .results-tiers > span { display: inline-flex; align-items: center; gap: 6px; }
/* Medals: a small dot in the medal's color. */
.medal { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.medal.bronze { background: var(--medal-bronze); }
.medal.silver { background: var(--medal-silver); }
.medal.gold { background: var(--medal-gold); }
.medal.optimal { background: var(--medal-optimal); }
.results-heading { margin: 0 0 6px; }
/* Your stats: four numbers in a row, then a bar per medal (its label, the bar, its count). */
.stats { width: min(400px, calc(100vw - 32px)); }
.stats-numbers { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin: 8px 0 16px; text-align: center; }
.stats-numbers div { display: grid; padding: 10px 4px; background: var(--card); border-radius: var(--radius-lg); }
.stats-numbers dd { margin: 0; font-size: 28px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stats-numbers dt { font-size: 12px; color: var(--muted); }
.stats-medals { display: grid; gap: 6px; margin: 0 0 16px; padding: 0; list-style: none; }
.stats-medals li { display: grid; grid-template-columns: 104px 1fr 2.5ch; align-items: center; gap: 8px; font-size: 14px; }
.stats-medal { display: inline-flex; align-items: center; gap: 6px; }
.stats-bar { height: 12px; border-radius: 999px; background: var(--card); overflow: hidden; }
.stats-bar > span { display: block; height: 100%; min-width: 0; border-radius: inherit; background: var(--ink); }
.stats-medals b { text-align: right; font-variant-numeric: tabular-nums; }
.results-heading small { font-weight: 400; letter-spacing: 0; text-transform: none; }
.results-chart { display: block; width: 100%; height: auto; }
.results-chart .bar { fill: var(--inactive); }
.results-chart .bar.mine { fill: var(--accent); } /* yours, in the color of the "You" over it */
.results-chart .you { fill: var(--accent); font: 700 11px system-ui, sans-serif; text-anchor: middle; }
.results-chart .you-arrow { fill: var(--accent); }
.results-chart .axis { fill: var(--muted); font: 10px system-ui, sans-serif; }
/* "See full stats", under the chart. */
.results-stats-link { margin: 8px 0 0; }
.results-stats-link[hidden] { display: none; }
/* The full stats (src/full-stats.js): the map's facts in a row, players' difficulty, a box per medal, the chart, and
   how it was played, as numbers over their words. */
.full-stats { text-align: center; width: min(460px, calc(100vw - 32px)); }
.full-stats-day { margin: 2px 0 14px; color: var(--muted); font-size: 14px; }
.full-stats-facts, .full-stats-play { display: grid; gap: 6px; margin: 0 0 18px; }
.full-stats-facts { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.full-stats-play { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); margin-top: 4px; }
.full-stats-facts div, .full-stats-play div { display: grid; padding: 8px 4px; }
.full-stats-facts dd, .full-stats-play dd { margin: 0; font-size: 24px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.full-stats-facts dt, .full-stats-play dt { font-size: 12px; color: var(--muted); }
.full-stats-difficulty { margin: 0 0 18px; padding: 12px; background: var(--card); border-radius: var(--radius-lg); }
.full-stats-dots { display: flex; justify-content: center; gap: 8px; margin: 6px 0; }
.full-stats-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--inactive); }
.full-stats-dot.on { background: var(--medal-gold); border-color: var(--medal-gold); }
.full-stats-note { margin: 0; font-size: 13px; color: var(--muted); }
.full-stats-medals { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; margin: 0 0 18px; }
.full-stats-medals div { display: grid; justify-items: center; gap: 4px; padding: 12px 4px; background: var(--card); border-radius: var(--radius-lg); }
.full-stats-medals b { font-size: 22px; font-variant-numeric: tabular-nums; }
.full-stats-medals small { font-size: 12px; color: var(--muted); }
.full-stats-medals .full-stats-note { grid-column: 1 / -1; }
.full-stats .results-chart { margin: 0 0 18px; }
/* Each line's chart: its name in its color over it, a word under it. Its bars are a light wash of its color, and
   yours (and its "You") the color itself (src/results.js, drawChart's tint). */
.full-stats-lines { display: grid; gap: 14px; margin: 0 0 18px; }
.full-stats-line h4 { margin: 0 0 2px; font-size: 13px; font-weight: 700; text-align: left; }
.full-stats .full-stats-line .results-chart { margin: 0 0 2px; }
.full-stats-line .full-stats-note { text-align: left; font-size: 12px; }
.results-chart.line-chart .bar { fill: color-mix(in srgb, var(--bar-tint) 30%, transparent); }
.results-chart.line-chart .bar.mine, .results-chart.line-chart .bar.hover { fill: var(--bar-tint); }
.results-chart.line-chart .you, .results-chart.line-chart .you-arrow { fill: var(--bar-tint); }
.results-chart.line-chart .bar-tag.count text { fill: var(--bar-tint); }
/* A bar under the pointer lights up, and says how many players scored it (src/results.js, drawChart). */
.results-chart .bar.hover { fill: var(--medal-gold); }
.results-chart .bar-hit { fill: transparent; }
.results-chart .bar-tag { pointer-events: none; }
.results-chart .bar-tag rect { fill: var(--card); stroke: color-mix(in srgb, var(--ink) 15%, transparent); stroke-width: 1; }
.results-chart .bar-tag text { font: 600 9px system-ui, sans-serif; text-anchor: middle; }
.results-chart .bar-tag.count text { fill: var(--medal-gold); }
.results-chart .bar-tag.score text { fill: var(--muted); }
/* Share beside View Optimal, under the chart: a little taller than the dialogs' other buttons. */
.results .results-share { margin: 14px 0 0; }
.results .results-share > button { min-height: 46px; font-weight: 600; }
/* Your rating, well below and quiet: no card behind it, small headings, smaller thumbs and dots. */
.results-rate { display: grid; justify-items: center; gap: 4px; margin: 34px 0 0; }
.results-rate[hidden] { display: none; }
.results-rate .results-heading { margin: 6px 0 0; font-size: 9px; }
.results-rate .rating-vote { gap: 10px; }
.results-rate .rating-thumb { width: 32px; height: 32px; border-width: 1.5px; }
.results-rate .rating-thumb svg { width: 15px; height: 15px; }
.results-rate .rating-scale { --dot: 12px; grid-template-columns: repeat(5, 32px); }
.results-rate .rating-level { width: 32px; }
.results-rate .rating-scale::before { left: 16px; right: 16px; }
.results-rate .rating-name { font-size: 10px; }
/* And further below, what's next: a card, as enclose.horse's "Up next" ones (a title, a line under it, an icon at
   its right, a soft tint of the theme's sand). */
.results-next-heading { margin: 30px 0 8px; }
.results-archives {
  display: flex; align-items: center; gap: 12px; width: 100%; padding: 12px 16px; border: 0; border-radius: var(--radius);
  background: color-mix(in srgb, var(--line-yellow) 28%, var(--card)); color: var(--ink); font: inherit; text-align: left; cursor: pointer;
}
.results-archives[hidden] { display: none; }
.results-archives span { display: grid; gap: 2px; flex: 1; }
.results-archives b { font-size: 17px; }
.results-archives small { font-size: 13px; color: var(--muted); }
.results-archives svg { width: 30px; height: 30px; flex: none; fill: currentColor; opacity: 0.75; }
@media (hover: hover) { .results-archives:hover { background: color-mix(in srgb, var(--line-yellow) 40%, var(--card)); } }
.results-rate .rating-star { font-size: 32px; }
/* The thumbs: 44px each (a finger's size), the picked one filled, up in green and down in red. */
.rating-vote { display: inline-flex; gap: 12px; }
.rating-thumb {
  display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 2px solid var(--inactive);
  border-radius: 50%; background: none; color: var(--muted); cursor: pointer; touch-action: manipulation;
}
.rating-thumb svg { width: 22px; height: 22px; fill: currentColor; }
.rating-thumb.down svg { scale: 1 -1; }
.rating-thumb.up[aria-pressed="true"] { --picked: var(--line-green); }
.rating-thumb.down[aria-pressed="true"] { --picked: #d9534f; }
.rating-thumb[aria-pressed="true"] { border-color: var(--picked); background: var(--picked); color: var(--paper); }
@media (hover: hover) { .rating-thumb[aria-pressed="false"]:hover { border-color: var(--muted); } }
.results-difficulty { display: flex; align-items: center; gap: 10px; color: var(--muted); font-size: 12px; }
@media (hover: hover) {
  .archive-card.scored:hover { background: color-mix(in srgb, var(--medal-color) 22%, var(--card)); }
}

/* ---------- Tally: each line's passengers, then the total, over the map after the victory lap ---------- */
.tally {
  position: absolute; inset: 0; z-index: 5; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  /* No backdrop blur: at the frame's rounded corners it smears past the paper's edge. */
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  transition: opacity 0.9s ease-out;
}
.tally[hidden] { display: none; }
.tally:not([hidden]) { animation: tally-appear 0.25s ease-out; }
@keyframes tally-appear { from { opacity: 0; } }
.tally.fading { opacity: 0; }
.tally p { margin: 0; display: flex; align-items: baseline; gap: 10px; opacity: 0; animation: tally-in 0.4s ease-out forwards; }
.tally-line { font-size: 22px; font-weight: 700; }
.tally-line b { min-width: 2ch; text-align: right; font-variant-numeric: tabular-nums; }
.tally-line .people { --size: 22px; fill: var(--muted); }
.tally-swatch { align-self: center; width: 22px; height: 8px; border-radius: 4px; transform: rotate(-45deg); }
.tally-next {
  margin-top: 24px; padding: 14px 32px; border: 0; border-radius: var(--radius); font: inherit; font-size: 18px; font-weight: 600;
  background: var(--ink); color: var(--on-ink); cursor: pointer; opacity: 0; animation: tally-in 0.6s ease-out forwards;
}
@media (hover: hover) { .tally-next:hover { background: var(--ink-hover); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18); } }
.tally-total { margin-top: 10px !important; font-size: 72px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; }
.tally-total .people { --size: 56px; fill: var(--ink); }
@keyframes tally-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ---------- Phones ---------- */
/* A phone held upright: the map spans the screen's width (inside the column's 16px sides), and a wide map is turned
   to stand tall (app.js). The page is exactly one screen and never scrolls: the map shrinks to the height left
   over, and the buttons sit at the bottom, so Reset, Undo and Submit are always in reach. */
@media (max-width: 600px) and (orientation: portrait) {
  html, body { height: 100%; overflow: hidden; }
  .game { height: 100dvh; min-height: 0; padding-bottom: 0; }
  .game > .board-wrap { width: 100%; flex: 0 1 auto; margin-top: 0; }
  .actions {
    /* The auto top margin keeps them at the screen's bottom. */
    margin: auto -16px 0;
    padding: 10px max(16px, env(safe-area-inset-right)) max(12px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
    background: var(--bg); box-shadow: 0 -1px 0 color-mix(in srgb, var(--ink) 10%, transparent);
  }
  .dev-fab { bottom: calc(76px + env(safe-area-inset-bottom)); } /* clear of the pinned buttons */
  /* Smaller squares and score, so the whole bar fits a phone's width, Submit's word included. */
  .actions { gap: 6px; }
  .actions .history-button { width: 42px; height: 42px; }
  .actions .view-button { width: 54px; }
  #score { font-size: 38px; }
  #score-unit .people { --size: 28px; }
  .actions .score-main { --score-width: 86px; }
  .actions #submit { padding-inline: 6px; }
  /* A phone has height to spare and little width, so the bar takes two rows: the tools, the view button and the
     score on top, and Submit under them across the whole width, where a thumb finds it. */
  .game > .actions:not(.editor-actions) { flex-wrap: wrap; row-gap: 10px; }
  .game > .actions:not(.editor-actions) #submit { order: 1; flex-basis: 100%; height: 50px; font-size: 16px; font-weight: 600; }
  .game > .actions:not(.editor-actions) .view-button { margin-left: auto; }
  .dev-fab { bottom: calc(136px + env(safe-area-inset-bottom)); }
}

/* ---------- Tutorial ---------- */
/* The step's hint, above the map. A guided step's line wears a dashed ghost of the tiles still to lay, and a
   glow on the tile to tap (or cut back to). The optimal answer waits until the guided steps end. */
.tutorial-card {
  display: flex; align-items: center; gap: 4px; margin: 0 auto 8px; width: min(100%, 520px); padding: 6px 0 0; color: var(--ink);
}
.tutorial-card[hidden] { display: none; }
/* In the tutorial the prompt sits right under the title and over the map: the spare height goes below the buttons. */
.game > .tutorial-card:not([hidden]) { margin-top: 4px; }
.game:has(> .tutorial-card:not([hidden])) > .board-wrap { margin-top: 0; }
.tutorial-card.single .tutorial-arrow { visibility: hidden; }
/* The mode buttons (Both, Lay, Remove): round buttons stacked in one pill, hanging off the map's right side, centered on it (outside
   the column, so the column stays symmetric); the chosen one filled. A narrow screen has no room beside the map,
   so there they sit in a row above it, named (a phone has no hover to name them). */
.tool-switch {
  position: absolute; left: calc(100% + 12px); top: 50%; transform: translateY(-50%); z-index: 7;
  display: flex; flex-direction: column; gap: 4px; padding: 4px; border-radius: 999px; background: color-mix(in srgb, var(--ink) 8%, transparent);
}
@media (max-width: 760px) {
  .tool-switch { left: 50%; top: auto; bottom: calc(100% + 8px); transform: translateX(-50%); flex-direction: row; }
  .game:has(.tool-switch:not([hidden])) > .board-wrap { margin-top: 52px; }
}
.tool-switch[hidden] { display: none; }
.tool-switch button {
  display: grid; place-items: center; width: 40px; height: 40px; padding: 0; border: 0; border-radius: 50%; background: none;
  color: var(--muted); cursor: pointer;
}
@media (hover: hover) { .tool-switch button[aria-checked="false"]:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 8%, transparent); } }
.tool-switch button[aria-checked="true"] { background: var(--ink); color: var(--on-ink); }
.tool-switch button { position: relative; }
/* Its name, beside it, on hover (to the right; inside the map on a phone, to the left). */
.tool-switch button::after {
  content: attr(data-label); position: absolute; left: calc(100% + 10px); top: 50%; transform: translateY(-50%);
  padding: 4px 8px; border-radius: 6px; background: var(--ink); color: var(--on-ink); font-size: 12px; font-weight: 600;
  white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity 0.15s;
}
@media (max-width: 760px) {
  .tool-switch button { width: auto; height: 34px; padding: 0 12px; display: flex; gap: 6px; border-radius: 999px; }
  .tool-switch button::after {
    content: attr(data-short); position: static; transform: none; opacity: 1; padding: 0; background: none; color: inherit; font-size: 13px;
  }
}
@media (hover: hover) { .tool-switch button:hover::after { opacity: 1; } }
.tool-switch svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.tool-switch button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tutorial-links { display: flex; justify-content: center; gap: 20px; margin-bottom: 4px; }
.tutorial-links .sub-link { visibility: visible; text-decoration: underline; text-underline-offset: 3px; }
.tutorial-body { flex: 1; min-width: 0; display: grid; justify-items: center; gap: 7px; padding: 4px 0; }
/* The hint keeps one height, as many lines as the longest hint wraps to, so the map never resizes between steps. */
.tutorial-hint { --lines: 2; display: grid; align-items: center; min-height: calc(var(--lines) * 1.35em); margin: 0; font-size: 18px; font-weight: 600; line-height: 1.35; text-align: center; }
@media (max-width: 600px) { .tutorial-hint { --lines: 3; } }
/* A smaller nudge under the hint; none, and it takes no room. */
.tutorial-tip { margin: 0; font-size: 14px; line-height: 1.35; color: var(--muted); text-align: center; }
.tutorial-tip:empty { display: none; }
/* The spotlight (src/spotlight.js): the page dimmed but for one part, with a note beside it. */
.spotlight { position: fixed; inset: 0; z-index: 50; cursor: pointer; animation: spotlight-in 0.3s ease-out; }
.spotlight.leaving { animation: spotlight-out 0.2s ease-in forwards; }
@keyframes spotlight-in { from { opacity: 0; } }
@keyframes spotlight-out { to { opacity: 0; } }
.spotlight-dim { position: fixed; inset: 0; width: 100%; height: 100%; fill: rgba(0, 0, 0, 0.72); }
.spotlight-note {
  position: fixed; width: min(320px, calc(100vw - 16px)); padding: 14px 16px; border-radius: var(--radius-lg);
  background: var(--paper); color: var(--ink); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); text-align: center;
}
.spotlight-note p { margin: 0 0 12px; font-size: 16px; font-weight: 600; line-height: 1.35; }
.spotlight-note button {
  padding: 8px 22px; border: 0; border-radius: var(--radius); font: inherit; font-weight: 700;
  background: var(--ink); color: var(--on-ink); cursor: pointer;
}
/* A stop drawn in a hint's words, the size of a capital letter. */
.hint-sign { display: inline-block; width: 1.25em; height: 1.25em; vertical-align: -0.28em; overflow: visible; }
.tutorial-arrow {
  flex: none; display: grid; place-items: center; width: 34px; height: 34px; padding: 0; border: 0; border-radius: var(--radius);
  background: none; color: var(--ink); cursor: pointer;
}
.tutorial-arrow svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.tutorial-arrow:not(:disabled):hover { background: color-mix(in srgb, var(--ink) 8%, transparent); }
.tutorial-arrow:disabled { opacity: 0.2; cursor: default; }
/* A dot per step: an empty ring ahead, filled once done, and a larger ring on the current step. */
.tutorial-dots { display: flex; align-items: center; gap: 7px; margin: 0; padding: 0; list-style: none; }
.tutorial-dots li {
  width: 8px; height: 8px; border-radius: 50%; box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--ink) 35%, transparent);
  transition: background-color 0.3s, box-shadow 0.3s, scale 0.3s cubic-bezier(0.3, 1.6, 0.5, 1);
}
.tutorial-dots li.done { background: var(--ink); box-shadow: none; }
.tutorial-dots li.current { scale: 1.35; box-shadow: inset 0 0 0 1.5px var(--ink); }
/* The tutorial's ghost: the line still to lay, faint, as wide as the track. */
.tutorial-ghost { fill: none; stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; opacity: 0.25; }
/* The tutorial's finger: it fades in on the tip, presses, drags to the next tile, and lifts away. */
.tutorial-finger { pointer-events: none; animation: tutorial-finger 1.8s ease-in-out infinite; }
.tutorial-finger-hand { fill: var(--paper); stroke: var(--ink); stroke-width: 1.4; stroke-linejoin: round; }
@keyframes tutorial-finger {
  0% { opacity: 0; transform: translate(0, 6px) scale(1.1); }
  15% { opacity: 1; transform: translate(0, 0) scale(1); }
  25% { transform: translate(0, 0) scale(0.92); }
  70% { opacity: 1; transform: translate(var(--dx), var(--dy)) scale(0.92); }
  85%, 100% { opacity: 0; transform: translate(var(--dx), var(--dy)) scale(1.1); }
}
@media (prefers-reduced-motion: reduce) { .tutorial-finger { animation: none; } }
/* Where the tutorial's tried move was refused: a ✕ that pops in once, and stays. */
.tutorial-refused {
  fill: none; stroke: #d64b3c; stroke-width: 5; stroke-linecap: round; pointer-events: none;
  transform-box: fill-box; transform-origin: center; animation: tutorial-refused 0.45s cubic-bezier(0.3, 1.6, 0.5, 1) both;
}
@keyframes tutorial-refused { from { opacity: 0; transform: scale(0.3); } to { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .tutorial-refused { animation: none; } }
.tutorial-glow { pointer-events: none; animation: tutorial-glow 1.4s ease-in-out infinite; }
@keyframes tutorial-glow { 0%, 100% { opacity: 0.1; } 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) { .tutorial-glow { animation: none; opacity: 0.22; } }
/* The tutorial's last step: Submit sends out soft rings until it is tapped. */
body.tutorial-submit #submit:not(:disabled) { animation: call-to-submit 1.6s ease-out infinite; }
@keyframes call-to-submit {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ink) 45%, transparent); }
  70%, 100% { box-shadow: 0 0 0 12px transparent; }
}
@media (prefers-reduced-motion: reduce) { body.tutorial-submit #submit:not(:disabled) { animation: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink) 30%, transparent); } }
body.tutorial-guided #show-on-map { visibility: hidden; }

/* ---------- Create: the map editor (?create, src/editor.js) ---------- */
/* The game's page with the same top bar; in place of the puzzle, the map in the making, a line of help, one bar of
   everything (tools | brush | size | actions), and Play. Outside Create, the editor's parts never show. */
:root:not(.creating) .editor-only { display: none !important; }
.creating .game > :is(.chips, .board-wrap, .actions, .tutorial-card, .tool-switch):not(.editor-only) { display: none; }
.editor-board { display: block; width: 100%; height: auto; overflow: visible; cursor: crosshair; touch-action: none; }
/* Where the brush lands: corner brackets round the cells it covers (as a level editor's cursor). */
.editor-cursor { fill: none; stroke: var(--ink); stroke-width: 3; stroke-linecap: square; pointer-events: none; }
.editor-status { min-height: 1.3em; margin: 10px 0 0; font-size: 13px; color: var(--muted); text-align: center; }
/* A warning (src/editor.js): bigger, bold and in ink, so it is not taken for a hint. */
.editor-status.warning { font-size: 16px; font-weight: 600; color: var(--warning); }
/* The bar: square buttons in groups, a thin rule between groups. It wraps onto a second row when narrow. */
/* It may run wider than the game's column (centered on it), up to the window's width less a margin. */
.game > .editor-bar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 6px; margin-top: 10px;
  align-self: center; width: max-content; max-width: calc(100vw - 32px);
}
.editor-group { display: flex; align-items: center; gap: 4px; }
.editor-divider { width: 2px; height: 26px; border-radius: 1px; background: color-mix(in srgb, var(--ink) 30%, transparent); }
.editor-bar button {
  display: grid; place-items: center; width: 40px; height: 40px; padding: 0; border: 0; border-radius: var(--radius);
  background: var(--card); color: var(--ink); cursor: pointer; box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--ink) 14%, transparent);
}
.editor-bar button[aria-checked="true"] { background: var(--accent); color: var(--on-ink); box-shadow: none; }
.editor-bar button:disabled { opacity: 0.35; cursor: default; }
.editor-bar button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.editor-bar svg { width: 26px; height: 26px; overflow: visible; }
.editor-group:not(#editor-tools) svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.editor-brush-cell { fill: currentColor; stroke: none; }
.editor-swatch, .editor-bar .terrain-swatch { stroke: color-mix(in srgb, var(--ink) 25%, transparent); stroke-width: 1; }
.editor-eraser { fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
/* Width and height: a letter and its number, in a box like a button's. */
.editor-size { gap: 6px; color: var(--muted); font-size: 14px; }
.editor-number { display: flex; align-items: center; gap: 4px; height: 40px; padding: 0 6px 0 10px; border-radius: var(--radius); background: var(--card); box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--ink) 14%, transparent); }
.editor-number input { width: 2.6em; border: 0; background: none; color: var(--ink); font: inherit; font-weight: 700; }
/* Each line's card above the map, the game's (.chip), with − before its track and + just after its count (.chip-step). The chips row
   keeps its height while empty, so the map doesn't jump when the first line comes. A tick added pops in. */
.editor-lines { min-height: 36px; }
.back-to-editor[hidden] { display: none; }
.back-to-editor { display: block; margin: 2px auto 0; }
.chip-step {
  flex: none; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%; background: var(--card); color: var(--chip-color);
  font: inherit; font-size: 15px; font-weight: 700; line-height: 1; cursor: pointer; box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--chip-color) 35%, transparent);
}
.chip-step:disabled { opacity: 0.35; cursor: default; }
/* Make tram: a small word on the right of the line's name, once the map is solved. */
.chip-tram {
  position: absolute; top: 2px; right: 6px; padding: 1px 4px; border: 0; border-radius: 4px; background: none; cursor: pointer;
  font: inherit; font-size: 9px; font-weight: 700; letter-spacing: 0.04em; color: var(--muted);
}
.chip-tram:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 8%, transparent); }
.tile.added { animation: tile-added 0.25s ease-out; }
@keyframes tile-added { from { scale: 0; opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .tile.added { animation: none; } }
/* While the solver runs, the network it's trying shows faintly, and the map can't be edited. */
.editor-solving .editor-board .line { opacity: 0.45; }
.editor-solving .editor-board { pointer-events: none; }
.editor-actions { justify-content: center; }
.editor-actions > button { flex: 0 1 220px; }

/* Create's line colors (src/editor.js, pickColor): a popover of swatches above the Line tool or a line's name, as
   the dev panel's (it fades and rises in). A color on another line keeps its true color, with a dot of paper in it
   (fading it read as a different, minty color): picking it swaps the two. Its height is its swatches' (Safari
   otherwise stretched it to the window's bottom, its rows spread apart). */
.editor-palette {
  position: fixed; inset: auto; margin: 0; padding: 10px; border: 0; border-radius: var(--radius-lg);
  background: var(--card); box-shadow: 0 14px 40px rgba(30, 25, 15, 0.22), 0 0 0 1px color-mix(in srgb, var(--ink) 6%, transparent);
  grid-template-columns: repeat(4, 32px); gap: 8px; height: fit-content; align-content: start;
  transition: opacity 0.15s, translate 0.15s, display 0.15s allow-discrete, overlay 0.15s allow-discrete;
}
.editor-palette:popover-open { display: grid; }
.editor-palette:not(:popover-open) { opacity: 0; translate: 0 6px; }
@starting-style { .editor-palette:popover-open { opacity: 0; translate: 0 6px; } }
.editor-swatch-pick {
  width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%; background: var(--swatch); cursor: pointer;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 15%, transparent);
}
.editor-swatch-pick.taken { background: radial-gradient(circle, var(--card) 0 5px, var(--swatch) 5.5px); }
.editor-swatch-pick[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--card), 0 0 0 4px var(--ink); }
.editor-lines .chip-name { cursor: pointer; }
