@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=IBM+Plex+Mono:wght@400;500;600;700&display=swap");
@import url("/assets/3e15e2e11213a3963ec689941323e40983ac02f9382ee4c9db9d0e1a9de64507/scrollbars.css");
@import url("/assets/f1bbf00418a39a288e3dbe61d6434b2e9299a58fe7bc89e62130f06fbffd9e04/motion.css");
@import url("/assets/3035cf6f6dbac04b69ca621cf3003b5c146c89ff06673011f1cd9ea8224a299a/theme.css");
@import url("/assets/a91bbb457f03c6f69c3c19e70783daefdb7256986924cd7ec87f8ae50ad3a0ff/solo-button.css");
@import url("/assets/c40a3363302f89252ccaecccf4e524dcff6f201f01901b0ace56defe2873a21b/switch.css");
@import url("/assets/d79d661f330d87925b88a7c2b6846e398a6e7ba66b574aab85f08e32b99b4ff7/map-navigation.css");
@import url("/assets/c3074ad81bc9f09dcda29b482aab960b889fd695b17461585058271124617195/game-controls.css");
@import url("/assets/291d29247f976667785a72e3c2ae5563b8b20d713592b1fdc83bbb940b0bf0a8/game-menu.css");

/* Shared chrome for the hub's games.
 *
 * City Limits inlines everything, because a cached page pairing stale markup
 * with fresh code is a bug you only ever see on someone else's machine. That
 * threat is real, but it is a *caching* problem: this server sends
 * `Cache-Control: no-store` on every HTML page and on this file, so the pair
 * can never desynchronise. Fifteen games duplicating this stylesheet would be
 * worse than the risk it avoids. City Limits stays inlined because it is
 * proven and there is nothing to gain by churning it.
 *
 * ---------------------------------------------------------------------------
 * The look is an almanac page, not a game screen.
 *
 * Printed reference works have a grammar the eye already trusts: paper ground,
 * one ink, a rule under the masthead, and figures set in monospace so columns
 * of them line up and read as *record* rather than as decoration. Everything
 * here follows that grammar, and the rule for adding to it is simple:
 *
 *   Inter carries language. IBM Plex Mono carries data.
 *
 * A label, a count, a score, a year, a unit, a coordinate, a countdown, a
 * breadcrumb: mono. A sentence a person reads: Inter. When something could go
 * either way it is data, because that is the half doing the teaching.
 *
 * Green is the only colour with any chroma, and it means "live" or "right".
 * Spending it anywhere else costs it that meaning, so the status colours are
 * held down at atlas saturation and the surfaces are all paper.
 *
 * Square corners throughout, and no shadows. Both are load-bearing rather than
 * taste: a radius and a drop shadow read as a floating control, and every
 * surface here is meant to read as printed onto the page.
 */

:root {
  /* Surfaces. Warm paper, never white: #fff next to these reads as a hole. */
  --bg: #f5f1ec;
  --panel: #f8f5f1;
  --panel-2: #ece7e0;
  --wash: #e5e0da;

  /* Ink. Three weights is all this needs; a fourth always ends up arbitrary. */
  --text: #13110f;
  --muted: #46413e;
  --faint: #6f6862;

  --border: #aaa39d;
  --rule: #13110f;

  /* The one saturated colour. It means live, correct, or yours. */
  --accent: #2e9e5b;        /* green, darkened to pass contrast on paper */
  --accent-bright: #48db7e; /* the signal green, for fills and rules only */
  --accent-ink: #07240f;
  --accent-soft: #e7f7ec;
  --accent-line: #7fc99a;

  /* Status, at atlas saturation so nothing out-shouts the accent. */
  --green: #3f8f5e;
  --lime: #639564;
  --yellow: #a8801f;
  --orange: #bb8e63;
  --red: #ad524d;

  /* The water, and it is the panel's rather than any one game's. Pastel and
     warm-leaning, because it sits under land the colour of paper: a saturated
     blue turns a printed page into a screen, and a grey one is not water. */
  --ocean: #d9e8f1;
  --ocean-swell: #a6c7dc;
  /* The land, its edges and the ring round a mark on it, for every map and
     globe (the globes read them too). Dark mode's night maps set their own
     (theme.css). */
  --map-land: #e3ded6;
  --map-land-deep: #ddd7cf;
  --map-edge: #bdb6ad;
  --map-edge-soft: #c9c2b8;
  --map-halo: #f5f1ec;

  /* Cartographic tints, for anything drawn on a map. */
  --map-blue: #4b81a5;
  --map-green: #639564;
  --map-red: #bb584a;
  --map-tan: #bb8e63;
  --map-yellow: #d3b460;

  --display: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, "Courier New", monospace;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--display);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* No page fade. A tab rendering hidden pauses CSS animations, so anything
 * whose *resting* state is `opacity: 0` renders as a blank page in that
 * harness and reads as a bug for as long as it takes to find. A paper page
 * has nothing to gain from arriving slowly anyway. */

/* ---------- the one-screen shell ----------
 * A round is a thing you read at a glance, so nothing it tells you may sit
 * below the fold. The page is therefore exactly one viewport tall and the
 * board absorbs the slack: prompt, input, tally and buttons keep their natural
 * heights, and the map or pad flexes into whatever is left.
 *
 * `min-height: 0` on every flex ancestor is the load-bearing half. A flex item
 * defaults to `min-height: auto`, which means "never shrink below my content",
 * so without it a tall SVG silently pushes the page open again and the
 * `flex: 1` above it does nothing at all.
 *
 * `main` keeps `overflow: auto` deliberately. It is a safety valve, not a
 * scroller: at any sane window it never triggers, but on a 400px-tall window
 * something has to give, and scrolling is honest where clipping would just
 * hide information and look like a bug. */
#app { display: flex; flex-direction: column; height: 100dvh; overflow: hidden; }

button { font-family: inherit; transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease, opacity 0.18s ease; }
button:focus-visible,
a:focus-visible,
input[type="range"]:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* The mono label that runs through everything: section heads, kickers, chips,
 * breadcrumbs, footers. Uppercase and letterspaced so it reads as a caption
 * and never competes with the sentence under it. */
.label,
.prompt .kicker,
#crumbs,
.chip,
.tally {
  font-family: var(--mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ---------- breadcrumb strip ---------- */
#crumbs {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 20px;
  font-size: 10px;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  background: var(--bg);
}
#crumbs a { color: var(--muted); text-decoration: none; }
#crumbs a, #crumbs .sep { flex-shrink: 0; }
#crumbs .here { min-width: 0; }
#crumbs a:hover { color: var(--text); text-decoration: underline; }
#crumbs .sep { opacity: 0.55; }
#crumbs .here { color: var(--text); }

/* ---------- a link out to the encyclopaedia ---------- */
/* Ink rather than the accent: green means live, correct or yours, and a link
   is none of the three. The rule underneath is drawn in the border colour so
   that a line of them reads as text with somewhere to go rather than as a line
   of emphasis, and it inks up under the pointer. */
a.wiki {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
a.wiki:hover,
a.wiki:focus-visible { text-decoration-color: currentColor; }

/* ---------- a scrollbar the page drew itself ---------- */
/* The same argument as Minefield's set picker: the browser's own bar is a
   rounded overlay in the operating system's colours, sitting on a page whose
   whole premise is that it is printed. A ruled column in the wash with a
   square ink thumb in it, holding its place in the layout rather than fading
   in and out, because on a strip two rows tall the bar is the only thing
   saying there is more.
 
   The order matters and is not a matter of taste. Chrome from 121 onwards
   drops every `::-webkit-scrollbar` rule on an element that also sets
   `scrollbar-width` or `scrollbar-color`, so declaring both plainly is
   declaring the standard one: the first version of this did, and drew the
   default rounded bar in slightly different colours. The standard properties
   are therefore behind a support query that only Firefox, which has no
   `::-webkit-scrollbar` at all, can answer. */
.paper-scroll::-webkit-scrollbar { width: 9px; height: 9px; }
.paper-scroll::-webkit-scrollbar-track,
.paper-scroll::-webkit-scrollbar-corner {
  background: var(--wash);
  border-left: 1px solid var(--border);
}
/* The thumb is drawn in the colour every rule and frame on the page is drawn
   in, and inks up under the pointer -- the same register as the links. */
.paper-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 0; }
.paper-scroll::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.paper-scroll::-webkit-scrollbar-button { display: none; }
@supports not selector(::-webkit-scrollbar) {
  .paper-scroll { scrollbar-width: thin; scrollbar-color: var(--border) var(--wash); }
}

/* ---------- masthead ---------- */
#topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  max-width: 1320px;
  margin: 0 auto;
  padding: 22px 20px 10px;
  border-bottom: 2px solid var(--rule);
  flex-wrap: wrap;
}
/* The back arrow lives in the breadcrumb now. This stays for pages that still
 * render one inline, and is styled to disappear into the strip. */
.back {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  text-decoration: none;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.back:hover { color: var(--text); }

#topbar h1 {
  font-family: var(--display);
  font-size: 21px;
  font-weight: 800;
  margin: 0;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }

.round-chip {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* Rounds as a row of bars (setProgress): green found, red missed, ink for the
   round in play, an outline for the ones to come. */
.round-chip.has-steps { display: inline-flex; align-items: center; gap: 9px; color: var(--text); font-size: 11px; font-weight: 600; }
.round-chip .steps { display: inline-flex; align-items: center; gap: 3px; }
.round-chip .step { display: block; width: 16px; height: 6px; box-shadow: inset 0 0 0 1px var(--border); }
.round-chip .step.won { background: var(--accent-bright); box-shadow: none; }
.round-chip .step.lost { background: var(--red); box-shadow: none; }
.round-chip .step.now { height: 10px; background: var(--text); box-shadow: none; }
.round-chip .step.done { background: var(--faint); box-shadow: none; }
/* Room to spare on a wide screen: a row that reads at a glance. */
@media (min-width: 641px) {
  .round-chip.has-steps { gap: 10px; font-size: 12px; }
  .round-chip .steps { gap: 4px; }
  .round-chip .step { width: 22px; height: 7px; }
  .round-chip .step.now { height: 12px; }
}

.what.icon-btn, .metric-info.icon-btn, .warp-explain.icon-btn {
  width: 30px; height: 30px; display: inline-flex;
  padding: 0; border: 1px solid var(--border); border-radius: 0;
  background: transparent; color: var(--muted);
  font: 500 13px/1 var(--mono); text-decoration: none;
  align-items: center; justify-content: center;
}
.icon-btn {
  width: 30px; height: 30px;
  border-radius: 0;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 12px;
  padding: 0;
}
.icon-btn:hover { color: var(--text); border-color: var(--text); }

/* A statistic title that explains itself on hover (K.statInfo). The mark is
   deliberately smaller and quieter than the "?" that opens the game rules. */
.stat-info { cursor: help; }
.stat-info-mark-only { cursor: auto; }
.stat-info-mark-only > .stat-info-mark { position: relative; }
.stat-info:focus-visible { outline: none; }
.stat-info-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; background: var(--panel); margin-left: 0.4em; vertical-align: middle;
  border: 1px solid var(--border); color: var(--muted);
  font: 500 9px/1 var(--mono); font-style: italic; letter-spacing: 0;
  text-transform: none; cursor: help; user-select: none;
}
.stat-info:not(.stat-info-mark-only):hover > .stat-info-mark, .stat-info-mark-only > .stat-info-mark:hover,
.stat-info:focus-visible > .stat-info-mark, .stat-info[aria-describedby] > .stat-info-mark {
  color: var(--text); border-color: var(--text);
}
.stat-popover {
  position: fixed; z-index: 1200; pointer-events: auto; overscroll-behavior: contain;
  width: max-content; max-width: min(400px, calc(100vw - 24px));
  box-sizing: border-box; padding: 12px 14px; overflow: auto;
  background: var(--panel); color: var(--text); border: 1px solid var(--border);
  box-shadow: 0 5px 20px #0002;
  font: 13px/1.45 var(--display); overflow-wrap: anywhere; text-align: left;
}
.stat-popover[hidden] { display: none !important; }
.stat-popover h2 { margin: 0 0 6px; font: 700 15px/1.25 var(--display); letter-spacing: -0.01em; }
.stat-popover h3 {
  margin: 10px 0 2px; font: 500 9.5px/1.3 var(--mono);
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}
.stat-popover p { margin: 4px 0; color: var(--text); }
.stat-popover .plain { font-size: 13.5px; line-height: 1.55; }
.stat-popover a { color: var(--accent); }
.control-hint { margin:6px 0 0; text-align:center; font:10px/1.4 var(--mono); color:var(--muted); }
.icon-btn.muted { opacity: 0.55; }

/* The rules live behind the "?" now, so for the first ten seconds it says so.
   Ink, not green: green means live, correct or yours, and a hint is none of
   the three. The resting look is the whole message and the pulse is only
   decoration, so a throttled or hidden tab shows a button that reads as
   emphasised rather than one that reads as broken. */
.icon-btn.hint { color: var(--text); border-color: var(--text); }
@media (prefers-reduced-motion: no-preference) {
  .icon-btn.hint { animation: hint-ring 1.7s ease-out infinite; }
}
@keyframes hint-ring {
  0%   { box-shadow: 0 0 0 0 rgba(19, 17, 15, 0.28); }
  70%  { box-shadow: 0 0 0 6px rgba(19, 17, 15, 0); }
  100% { box-shadow: 0 0 0 0 rgba(19, 17, 15, 0); }
}

/* ---------- sound popover ---------- */
#settings-ctl { position: relative; }
#settings-pop {
  position: absolute;
  top: 38px; right: 0;
  z-index: 1200;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 0;
  padding: 16px;
  width: 226px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
#settings-pop label {
  display: flex;
  flex-direction: column;
  gap: 7px;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--muted);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
#settings-pop input[type="range"] { width: 100%; accent-color: var(--accent); height: 3px; }

/* Wikipedia links, off. The anchors stay where they are and stop being links:
   no colour, no underline, no pointer, and kit.js refuses the click as well,
   because a link is followable from the keyboard too. */
.no-wiki a.wiki {
  color: inherit;
  text-decoration: none;
  pointer-events: none;
  cursor: inherit;
}

/* ---------- layout ---------- */
main {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  padding: 20px 20px 22px;
}
.stage {
  width: 100%;
  max-width: 620px;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* The map games. A world map is a wide thing and 980px was leaving the sides
   of a 1400px window empty, so these get the width and the masthead widens with
   them to stay aligned. */
.stage.wide { max-width: 1320px; }
/* Browser zoom reduces the CSS viewport. Preserve intrinsic controls and let
   the page scroll instead of squeezing the board into its action buttons. */
@media (max-height: 600px), (max-width: 600px) {
  main > .stage, main > .flight { flex: 0 0 auto; min-height: min-content; height: auto; }
  main > .stage > *, main > .flight > * { flex-shrink: 0; }
  /* With the svg out of the flow the panel has no content height of its own:
   * give it one, most of the screen, and let the page scroll for the rest. */
  main .map-panel { height: clamp(240px, 60dvh, 600px); }
  main .map-panel, main .globe-frame { min-height: 220px; }
  main .chart-layout { min-height: 340px; }
  main .chart-shell { min-height: 0; }
  main #pad-wrap, main #board-wrap { min-height: 240px; }
  main .row { flex-wrap: wrap; }
  #topbar { flex-wrap: wrap; }
  .topbar-right { flex-wrap: wrap; }
}
@media (max-width: 600px) {
  /* A board that opts in (mapPanel fitPhone) is as tall as its map, not most
     of the screen: less sea to scroll past, more page to scroll with. */
  main .map-panel.fit-extent { height: auto; aspect-ratio: var(--map-aspect, 1.6); min-height: 240px; max-height: 62dvh; }
}

/* Where the sprints hang their map. The board inside takes the slack. */
#prompt-extra {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1 1 auto;
  min-height: 0;
}

/* ---------- the prompt ---------- */
/* Left-aligned, like a page. Centred text reads as a title card, and the
 * whole point is that this looks like something you are reading. */
.prompt {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.prompt .kicker {
  font-size: 10px;
  color: var(--muted);
}
.prompt h2 {
  font-family: var(--display);
  font-size: 34px;
  font-weight: 700;
  margin: 0;
  letter-spacing: -0.025em;
  line-height: 1.15;
}
.prompt .note {
  margin: 2px 0 0;
  font-size: 14px;
  color: var(--muted);
  line-height: 1.45;
}

/* ---------- panels ---------- */
.card-panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 20px;
}

/* ---------- inputs ---------- */
.answer-box { position: relative; }
input[type="text"] {
  width: 100%;
  padding: 13px 14px;
  font-size: 16px;   /* >=16px stops iOS zoom-on-focus */
  font-family: var(--display);
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0;
  outline: none;
}
input[type="text"]:focus { border-color: var(--text); box-shadow: inset 0 -2px 0 var(--accent-bright); }
input[type="text"]::placeholder { color: var(--faint); }

/* ---------- the browser's own controls, in the page's ink ----------
   A select, a checkbox, a number field: each is drawn square, on the panel,
   in the mono caption face the chips and crumbs use, with the same ink line
   round it as every rule on the page. The chevron is drawn here rather than
   left to the operating system. Pages size them; this is only the look. */
select {
  appearance: none;
  -webkit-appearance: none;
  padding: 7px 30px 7px 10px;
  font: 500 11px var(--mono);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text);
  background: var(--panel) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' fill='none' stroke='%2313110f' stroke-width='1.4'/></svg>") no-repeat right 10px center;
  border: 1px solid var(--border);
  border-radius: 0;
  cursor: pointer;
}
select:hover { border-color: var(--text); }
select:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
select:disabled { opacity: .5; cursor: default; }
select option { font: 500 13px var(--display); text-transform: none; letter-spacing: 0; color: var(--text); background: var(--panel); }
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); width: 15px; height: 15px; margin: 0; cursor: pointer; }
input[type="number"] {
  padding: 8px 10px;
  font: 500 14px var(--mono);
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0;
  outline: none;
}
input[type="number"]:focus { border-color: var(--text); box-shadow: inset 0 -2px 0 var(--accent-bright); }

.suggestions {
  position: absolute;
  left: 0; right: 0; top: calc(100% - 1px);
  z-index: 900;
  background: var(--panel);
  border: 1px solid var(--text);
  border-radius: 0;
  max-height: 260px;
  overflow-y: auto;
}
.suggestion {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  cursor: pointer;
  font-size: 14px;
  border-bottom: 1px solid var(--wash);
}
.suggestion:last-child { border-bottom: 0; }
.suggestion .sub { font-family: var(--mono); color: var(--muted); font-size: 11px; }
.suggestion.active, .suggestion:hover { background: var(--wash); }

/* ---------- buttons ---------- */
/* Mono and uppercase, because every button here is an instruction rather than
 * a label. Padding is set for a 44px touch target at the default font size;
 * check it at 375px before trimming it. */
.btn {
  background: var(--accent-soft);
  color: var(--text);
  border: 2px solid var(--accent-bright);
  border-radius: 0;
  padding: 12px 18px;
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}
.btn:hover { background: #d8f2e1; }
.btn.wide { width: 100%; padding: 15px 18px; }
.btn.ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
}
.btn.ghost:hover { background: var(--wash); border-color: var(--text); }
.btn.quiet {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}
.btn.quiet:hover { color: var(--text); border-color: var(--text); }
.btn:disabled { opacity: 0.38; cursor: default; }
.row { display: flex; gap: 10px; flex-wrap: wrap; }
.row.end { justify-content: flex-end; }
.row.center { justify-content: center; }

/* ---------- what the answer box says back ----------
 * Two states, and they are different facts, so they are different colours.
 * `dupe` is green: you were right, you just already had it. `wrong` is red: a
 * definite miss. Both were being redefined per game, which is how three of them
 * ended up with the green one and none with the red. */
.answer-box input.dupe { border-color: rgba(46, 158, 91, 0.55); }
.answer-box input.wrong { border-color: var(--red); }

/* ---------- answer chips ---------- */
/* The answers you have already given are a log, not the question, so this is
 * the one region allowed to run out of room. It takes what is left over and
 * scrolls inside itself; everything that tells you what to do next stays put
 * above and below it. */
/* Two rows, always, and a scrollbar only once there are more than two rows to
 * scroll. It used to be `flex: 0 1 auto` against a flexing board, which meant
 * it was squeezed to nothing and scrolling from the very first answer: a
 * scrollbar on two chips reads as broken.
 *
 * The height is fixed rather than grown so the board above it does not resize
 * every time a row of answers fills up. */
.chips {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 6px;
  flex: 0 0 auto;
  height: 52px;              /* two 23px rows and the gap between them */
  overflow-y: auto;
  overscroll-behavior: contain;
}
.chip {
  padding: 4px 9px;
  border-radius: 0;
  font-size: 10px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
}
.chip.got {
  color: var(--accent-ink);
  border-color: var(--accent-line);
  background: var(--accent-soft);
}
.chip.missed { color: var(--red); border-color: rgba(173, 82, 77, 0.45); background: transparent; }
/* Transform only, never opacity. A paused animation holds its first keyframe,
 * and a tab that renders hidden pauses every one of them, so `from { opacity: 0 }`
 * means the resting state of this element is invisible. The chip arrives by
 * sliding, and an unplayed slide is still a legible chip. */
@keyframes chip-in { from { transform: translateY(4px); } to { transform: none; } }
.chip.fresh { animation: chip-in 0.28s cubic-bezier(0.22, 0.61, 0.36, 1); }

/* ---------- meters ---------- */
/* `display: block` on the fill is load-bearing, not tidiness. A <span> in a
 * non-flex parent stays inline, `width` does not apply to inline boxes, and
 * the bar renders at 0px while its style attribute cheerfully reads `2%`.
 * That has shipped three times here. Measure it with getBoundingClientRect. */
.meter {
  height: 4px;
  border-radius: 0;
  background: var(--wash);
  overflow: hidden;
}
.meter > span {
  display: block;
  height: 100%;
  border-radius: 0;
  background: var(--text);
  transition: width 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.tally {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 10px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ---------- modal ---------- */
.backdrop {
  position: fixed;
  inset: 0;
  background: rgba(19, 17, 15, 0.28);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  padding: 20px;
  overflow-y: auto;
}
@keyframes modal-in { from { transform: translateY(6px); } to { transform: none; } }
.modal {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 0;
  padding: 26px 28px;
  max-width: 480px;
  width: 100%;
  animation: modal-in 0.28s ease;
  max-height: 88vh;
  overflow-y: auto;
}
.modal h2 {
  margin: 0 0 10px;
  font-family: var(--display);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.025em;
}
.modal p { margin: 6px 0; color: var(--muted); font-size: 14px; line-height: 1.55; }
.modal .headline {
  color: var(--text);
  font-family: var(--mono);
  font-size: 30px;
  font-weight: 600;
  margin: 8px 0 4px;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.modal ul { margin: 10px 0 0; padding-left: 18px; color: var(--muted); font-size: 13.5px; line-height: 1.7; }
.modal li { margin-bottom: 4px; }
.modal-actions { margin-top: 22px; display: flex; justify-content: flex-end; gap: 10px; flex-wrap: nowrap; }
.answer-correct { color: #21834b; font-weight:700; }
.answer-wrong { color: #bd3939; font-weight:700; }

.scorebits {
  display: flex;
  gap: 22px;
  margin: 16px 0 2px;
  flex-wrap: wrap;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.scorebit b {
  display: block;
  font-family: var(--mono);
  font-size: 21px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.scorebit span {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- the map panel ---------- */
/* Every map in the app is this element, so that they are the same size, the
 * same shape and the same weight on the page. Before it, each game styled its
 * own and no two agreed. */
.map-panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 8px;
  overflow: hidden;
  position: relative;
  /* Takes whatever height is left after the fixed rows, which is what keeps a
   * round on one screen. `min-height: 0` is what lets it actually shrink. */
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
}
/* The slot the panel replaces, shaped like the panel from the first frame.
 * Three games fetch megabytes before they can draw anything -- Population Hunt
 * reads four and a half of them -- and until the panel arrived the page laid
 * itself out around an empty div: the answer box and the score sat up under
 * the heading, and the whole column jumped down when the map appeared. Half a
 * second of a page that is not the page you clicked. */
#map-slot {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  place-items: center;
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.map-panel.can-pan { cursor: grab; touch-action: none; }
.map-panel.can-pan.dragging { cursor: grabbing; }
/* The drawing is laid over the panel, never inside its flow: an <svg> with a
 * viewBox has an intrinsic height, and on a phone -- where the panel is as
 * tall as its content -- that height fed the panel's, which set the next
 * viewBox, which set the next height. Minefield's map grew two pixels a tick,
 * forever. Absolute, the svg takes the panel's size and gives nothing back. */
.map-panel-svg { display: block; position: absolute; inset: 8px; width: calc(100% - 16px); height: calc(100% - 16px); min-height: 0; }

/* The swell. Two bands of long shallow curves drifting past each other, built
   once in their own units and placed by one transform per frame (see seaLayer
   in kit.js). A period is 40 of those units however far the camera has come
   in, so the drift is a fixed translate and the rows meet themselves exactly.

   Decoration, and it declares itself as such: the resting state is a correct
   sea and the animation only slides it sideways, so a hidden tab, a throttled
   one and prefers-reduced-motion all leave water on the map. It takes no
   pointer, because two of these maps are clicked and open water is an answer
   there. */
.sea { pointer-events: none; }
/* The water is a shape, not a background: on the world projection it stops at
   the edge of the globe, because paper outside that oval is off the map rather
   than sea. See seaLayer's `outline` in kit.js. */
.sea .ocean { fill: var(--ocean); stroke: none; }
.sea .swell { stroke: var(--ocean-swell); stroke-linecap: round; fill: none; }
.sea .far { opacity: 0.34; animation: sea-drift-far 24s linear infinite; }
.sea .near { opacity: 0.5; animation: sea-drift-near 15s linear infinite; }
@keyframes sea-drift-far { to { transform: translateX(40px); } }
@keyframes sea-drift-near { to { transform: translateX(-40px); } }
@media (prefers-reduced-motion: reduce) { .sea .swell { animation: none; } }

.map-btns {
  position: absolute;
  left: 8px; top: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.map-btns button {
  width: 26px; height: 26px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.map-btns button:hover { border-color: var(--text); }

/* The tooltip a map hands back when you point at something on it. Built by
   K.mapPanel's `hover`, positioned in panel coordinates and clamped to it. */
.map-tip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  background: var(--panel);
  border: 1px solid var(--text);
  padding: 6px 9px;
  /* Position must not affect size. An auto-width absolute box shrinks to the
     space after `left`, even though translate(-50%) centers it afterward.
     That caused edge clamping and text wrapping to feed back into each other. */
  width: max-content;
  max-width: min(240px, calc(100% - 16px));
  white-space: normal;
  overflow-wrap: anywhere;
  transform: translate(-50%, -100%);
  font-size: 13px;
  color: var(--text);
  line-height: 1.35;
}
.map-tip-corner { transform: none; }
.map-tip[hidden] { display: none !important; }
/* The hovered country's border, 2px like the globe's (population-globe.js). */
.map-hover-outline { fill: none; stroke: #198d4d; stroke-width: 2px; stroke-linejoin: round; vector-effect: non-scaling-stroke; pointer-events: none; }
.map-tip b { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.map-tip .sub {
  display: block;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.map-tip .num {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
@media (pointer: coarse) {
  .map-btns button { width: 40px; height: 40px; }
  /* Under 16px, iOS zooms the page in when a field is tapped and leaves it
     there. A floor for every field; a game that sizes one by id sets its own. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), select, textarea { font-size: max(16px, 1em); }
}

/* A live per-continent readout, right-aligned under the board. What is still
 * dark on the map, as numbers, for the games where "how much of the world" is
 * the actual question. */
.continents {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 4px 12px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  flex: none;
}
.continents span { font-variant-numeric: tabular-nums; }
.continents b { color: var(--text); font-weight: 500; }
.continents .full b { color: var(--accent); }

/* ---------- the briefing ---------- */
/* The rules, opened from the "?". A game no longer opens on this: clicking a
 * game gets you the game, and the "?" glows for ten seconds so the rules are
 * findable without being in the way.
 *
 * It is still a page rather than a dialog. It sits in the flow under the real
 * masthead, so the page you read the rules on is recognisably the same page
 * you play on, and it replaces the round rather than floating over it -- a
 * dialog over a live board is exactly what this site does not do. The game's
 * own `main` is hidden by a class on <body> instead of by anything here,
 * because the chrome is built before the game has created `main` and a rule
 * that waits for it cannot miss.
 *
 * The clock stops while it is open, in both games that own one. That was the
 * original reason for gating the round behind it, and it is handled properly
 * now rather than avoided. */
body.briefing main { display: none !important; }

#briefing {
  width: 100%;
  max-width: 1320px;
  margin: 0 auto;
  padding: 30px 20px 56px;
}
#briefing .sheet {
  width: 100%;
  max-width: 460px;
  margin: 0 auto;   /* same centred measure the stage uses */
  display: flex;
  flex-direction: column;
  gap: 16px;
}
#briefing .kicker {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
#briefing h2 {
  font-family: var(--display);
  font-size: 36px;
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.1;
  margin: 0;
}
#briefing .body { color: var(--muted); font-size: 14px; line-height: 1.55; }
#briefing .body p { margin: 0 0 12px; }
#briefing .body p:last-child { margin-bottom: 0; }
/* The opening line is the whole game in a sentence, so it is set as a lede. */
#briefing .body > p:first-child {
  font-size: 16.5px;
  line-height: 1.45;
  color: var(--text);
}
#briefing .body strong { color: var(--text); font-weight: 600; }
/* Where the figures came from. A colophon rather than a rule: it is set in
   mono under a rule of its own, so it reads as the imprint at the foot of a
   page and never as an eighth thing the player has to learn. */
#briefing .body .source {
  margin-top: 18px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-family: var(--mono);
  font-size: 10.5px;
  line-height: 1.7;
  letter-spacing: 0.02em;
  color: var(--faint);
}
#briefing .body .source b { color: var(--muted); font-weight: 500; }
#briefing .body kbd {
  font-family: var(--mono);
  font-size: 11px;
  border: 1px solid var(--border);
  padding: 1px 5px;
  color: var(--text);
}

/* The rules, numbered. This is the teaching surface, and numbering it is what
 * turns a list of tips into something that reads as a specification: countable,
 * referable, finite. A bulleted list says "some notes"; 01..07 says "this is
 * all of it", which is the difference the whole design is chasing. */
#briefing .body ul {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: rule;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
#briefing .body li {
  counter-increment: rule;
  position: relative;
  padding-left: 32px;
  font-size: 13.5px;
  line-height: 1.5;
}
#briefing .body li::before {
  content: counter(rule, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 2px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--accent);
}

/* Provenance and shape of the round, as a strip of figures. Optional: a
 * half-filled strip reads worse than none. */
#briefing .meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 11px 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
#briefing .meta b { color: var(--text); font-weight: 500; }
#briefing .btn { margin-top: 2px; }

@media (max-width: 640px) {
  #crumbs { padding: 7px 14px; }
  #topbar { padding: 16px 14px 9px; gap: 10px; row-gap: 6px; }
  #topbar h1 { font-size: 17px; }
  main { padding: 16px 14px 16px; }
  .prompt h2 { font-size: 26px; }
  #briefing h2 { font-size: 29px; }
  .btn { padding: 13px 16px; }
  .scorebits { gap: 16px; }
}

/* Short windows. A laptop with a browser's chrome on it is routinely under
 * 700px of viewport, and the games whose content is fixed-height rather than a
 * flexing board (Sort It's five rows, Flag Sprint's card) have nothing to give
 * unless the furniture around them gets smaller. This is the tier that keeps
 * them on one screen. */
@media (max-height: 720px) {
  main { padding: 12px 20px 14px; }
  .stage { gap: 10px; }
  .prompt { gap: 3px; }
  .prompt h2 { font-size: 26px; }
  .prompt .note { font-size: 13px; }
  #topbar { padding: 12px 20px 8px; }
  #topbar h1 { font-size: 18px; }
  .btn { padding: 10px 16px; }
}
@media (max-height: 600px) {
  .prompt h2 { font-size: 21px; }
  .stage { gap: 8px; }
  .chips { height: 29px; }   /* one row, on a window with nothing to spare */
}

/* A finger is not a cursor. Everything a round depends on already clears 44px,
   but the chrome did not: the breadcrumb was a 15px-tall hit and the icon
   buttons were 30px square. Measured at 375px, which is the width that finds
   these. */
@media (pointer: coarse) {
  /* A slider drawn 4px tall is a 4px target. The track keeps its look (it
     is drawn centred in the box); the box itself is what a finger hits. */
  main input[type="range"] { height: 36px; margin: -10px 0; }
  /* The small secondary controls on a board: globe and chart zoom, north-up,
     lives. Bigger only on a touch screen, where 25px is a miss. */
  .hunt-globe-tools button, .zoom-btn, .lives-picker button, [data-overview] { min-height: 36px; min-width: 36px; }
  .world-mode-switch a, .world-tools button, #compare { min-height: 36px; }
  #crumbs { padding: 4px 14px; }
  #crumbs a { padding: 11px 2px; }
  #crumbs .sep, #crumbs .here { padding: 11px 0; }
  .icon-btn { width: 40px; height: 40px; }
  .back { padding: 10px 2px; }
}

@media (prefers-reduced-motion: reduce) {
  .modal, .chip.fresh { animation: none; }
  .meter > span { transition: none; }
}

/* ---------- loading screen ---------- */
/* The cover, its bar and their styles are in loading.js, loaded in each game's <head>. */

/* Information occupies the available screen. Long rules use measured pages. */
/* This comes after both the short-window and touch rules so compact phones
   keep their board space without losing keyboard or touch controls. */
/* On a phone the answer box follows the player down the page: the globe or
   map above it can be studied without scrolling back to type, and the box
   returns to its place in the flow when the page is scrolled to the end. */
/* Keyboard shortcuts mean nothing on a touch-only device; the buttons they
   stand in for are always on screen. */
@media (hover: none) and (pointer: coarse) {
  .keys-only { display: none !important; }
}

@media (max-width: 640px) and (pointer: coarse) {
  main .answer-box { position: sticky; bottom: 0; z-index: 6; background: var(--bg); padding: 6px 0; box-shadow: 0 -8px 8px -6px rgba(19,17,15,.12); }
}
@media (max-width: 640px) and (max-height: 740px) {
  #topbar { padding:6px 12px; gap:8px; }
  #crumbs { padding:0 12px; }
  main { padding:8px 12px; }
  .stage { gap:7px; }
  .prompt h2 { font-size:22px; }
  .prompt .note { font-size:12px; }
  .row { gap:6px; }
  .row > .btn { padding:10px; font-size:12px; }
  .tally { font-size:9px; gap:8px; }
}

/* The sheet fills the briefing as a flex item, not by height: 100%, which
   resolves to nothing where the page grows with its content (the Oracle
   games on a phone) and left the rules no room at all. */
#briefing { flex: 1; min-height: 0; overflow: hidden; padding: 16px 20px; display: flex; flex-direction: column; }
#briefing .sheet { flex: 1 1 auto; min-height: 0; max-width: 720px; gap: 10px; }
#briefing h2 { font-size: 28px; }
#briefing .body.info-content, .info-content {
  flex: 1 1 0;
  min-height: 0;
  overflow: hidden;
  overflow-wrap: anywhere;
}
#briefing .body > p:first-child { font-size: 14px; }
#briefing .body h3 { font-size: 15px; margin: 10px 0 6px; }
#briefing .body ul { margin: 0 0 8px; }
#briefing .body .source { margin-top: 10px; padding-top: 8px; }
#briefing .meta { padding: 5px 0; }
.info-pages { display: flex; align-items: center; justify-content: center; gap: 16px; flex: none; }
.info-pages span { font: 11px var(--mono); color: var(--muted); }
.info-pages button { width: 38px; height: 30px; border: 1px solid var(--border); background: var(--panel); color: var(--text); cursor: pointer; }
.info-pages button:disabled { opacity: .35; cursor: default; }
.info-pages button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.info-modal { display: flex; flex-direction: column; gap: 10px; height: min(600px, calc(100dvh - 32px)); max-height: calc(100dvh - 32px); max-width: 640px; overflow: hidden; padding: 18px 22px; }
.info-modal h2 { font-size: 24px; margin: 0; flex: none; }
.info-modal .modal-actions { margin-top: 0; flex: none; }
@media (max-height: 500px) {
  #briefing { padding: 8px 16px; }
  #briefing .kicker, #briefing .meta { display: none; }
  #briefing h2 { font-size: 21px; }
  .info-modal { padding: 12px 18px; gap: 6px; }
}

/* Definitions remain usable with a finger and a keyboard, including long sources. */
.stat-info-mark:focus-visible, .stat-popover:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (pointer: coarse) { .stat-info-mark { min-width: 44px; min-height: 44px; font-size: 14px; } .stat-popover { max-height: calc(100dvh - 24px); } }

/* A paragraph longer than one help page still needs to be readable. */
#briefing .info-content, .info-modal .info-content { overflow-y: auto; overscroll-behavior: contain; }

/* Keep answer feedback readable without shifting the board after each guess. */
.sprint-answer-status { min-height: 1.4em; margin: 2px 0 6px; font-size: 12px; }

/* ---------- a phone's header ----------
   One row where it can: the title, then Solo Play, the rules' "?" and the
   Menu at a finger's 44px; the settings, share and page links wait in the
   Menu (game-menu.css). The breadcrumb strip gives way (Solo Play is in the
   row and the home page one tap from it), and sharing and Report are also
   under the rules (kit.js). A round count, clock or time setting takes a slim
   row below. A party round has its own slim bar (party-game.js). */
.briefing-more { display: none; }
@media (max-width: 640px) {
  :root:not(.party-game) #crumbs { display: none; }
  :root:not(.party-game) #topbar { flex-wrap: wrap; align-items: center; column-gap: 6px; row-gap: 0; padding: 6px 12px; }
  :root:not(.party-game) #topbar > h1 { flex: 1 1 0; min-width: 0; font-size: clamp(13px, 4vw, 17px); line-height: 1.12; overflow-wrap: break-word; }
  :root:not(.party-game) #topbar .topbar-right { display: contents; }
  :root:not(.party-game) #topbar :is(.menu-solo, #settings-ctl, #help-btn) { order: 1; }
  :root:not(.party-game) #topbar #solo-share { display: none; }
  :root:not(.party-game) #topbar::after { content: ""; order: 2; flex-basis: 100%; }
  :root:not(.party-game) #topbar :is(.round-chip, .solo-time-setting, #solo-clock, .hunt-clock) { order: 3; margin: 4px 10px 0 0; }
  :root:not(.party-game) #topbar :is(.icon-btn, #settings-btn) { width: 44px; height: 44px; }
  :root:not(.party-game) #topbar .menu-solo { min-height: 44px; }
  :root:not(.party-game) .briefing-more:not(:empty) { display: flex; justify-content: center; gap: 8px; margin-top: 8px; }
  :root:not(.party-game) .briefing-more .btn { flex: 1 1 0; min-height: 40px; padding: 8px 10px; }
  :root:not(.party-game) .report-row { display: none; }
}

