:root {
  /* The page is one gradient from top to bottom, cool the whole way down: indigo,
     then blue, then violet, and out into near-black. The amber accent is the only
     warm thing in the palette. It marks the things that are yours and the things
     that ask something of you: every call to action, the small label over the
     countdown, and the city you play for. Nothing decorative wears it. */
  --bg: #080a12;
  --bg-top: #0a0f1f;
  --bg-mid: #101a3c;
  --bg-low: #1a1442;
  --panel: rgba(19, 25, 42, 0.86);
  --panel-2: rgba(31, 39, 62, 0.85);
  --line: #2b3654;
  --ink: #eef2f8;
  --muted: #9aa7c2;
  --accent: #fbbf24;
  --cool: #67e8f9;
  --good: #34d399;
  --bad: #f87171;
  --radius: 16px;
  /* The blooms that make the page abstract rather than merely dark. */
  --bloom-cyan: rgba(56, 189, 248, 0.20);
  --bloom-violet: rgba(139, 92, 246, 0.24);
  --bloom-magenta: rgba(217, 70, 239, 0.14);
  --bloom-amber: rgba(251, 191, 36, 0.10);
}

* { box-sizing: border-box; }

/* `hidden` has to beat any display rule, or a control hidden in the markup comes
   back the moment a class sets `display` on it. */
[hidden] { display: none !important; }

html, body { margin: 0; padding: 0; }

/* The vertical gradient sits on the root element so it covers the viewport whatever
   the page height is. */
html {
  background-color: var(--bg);
  background-image: linear-gradient(
    180deg,
    var(--bg-top) 0%,
    var(--bg-mid) 26%,
    var(--bg-low) 58%,
    var(--bg) 100%
  );
  background-attachment: fixed;
  background-repeat: no-repeat;
  min-height: 100%;
}

/* The abstract half: four soft blooms and a vignette, in one background, so nothing
   has to be stacked to get depth. A large radial gradient is already soft, which is
   why this costs nothing like a blurred layer would - and nothing here animates: a
   drifting background under a ten-second question clock is a distraction, not a
   flourish. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(120% 75% at 50% -10%, transparent 42%, rgba(4, 6, 12, 0.55) 100%),
    radial-gradient(58% 44% at 8% 4%, var(--bloom-cyan), transparent 70%),
    radial-gradient(54% 40% at 94% 30%, var(--bloom-violet), transparent 72%),
    radial-gradient(50% 38% at 14% 86%, var(--bloom-magenta), transparent 74%),
    radial-gradient(46% 34% at 88% 98%, var(--bloom-amber), transparent 72%);
}

body {
  background: none;
  color: var(--ink);
  font: 16px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-text-size-adjust: 100%;
}

.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-variant-numeric: tabular-nums; }

.skip {
  position: absolute; left: -9999px;
}
.skip:focus {
  position: static; display: block; padding: 8px 12px; background: var(--accent); color: #000;
}

.top {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 18px; gap: 12px;
  /* The name on the right can be twenty-four characters, and the mark and the name
     together are wider than a folded phone. Wrapping puts the name on its own line
     only when it has to, which is better than either squeezing it or letting the
     header push the page sideways. */
  flex-wrap: wrap;
  border-bottom: 1px solid var(--line);
  /* Above the blooms, and tinted at the top so the header does not float. */
  position: relative; z-index: 1;
  background: linear-gradient(180deg, rgba(9, 13, 28, 0.72), rgba(9, 13, 28, 0));
}
.brand { display: flex; align-items: center; font-weight: 700; letter-spacing: .01em; }
/* The mark is part of the link, not a thing next to it. It was outside the anchor
   on three pages and there was no anchor at all on the front one, so the icon was
   dead exactly where people aim. Padding with matching negative margins grows the
   target without moving the mark a pixel. */
.brand a {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 7px; margin: -5px -7px;
  color: inherit; text-decoration: none;
}
.brand svg {
  width: 20px; height: 20px; fill: none;
  stroke: var(--accent); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

main { flex: 1; width: 100%; max-width: 660px; margin: 0 auto; padding: 18px; position: relative; z-index: 1; }

/* The name control, top right, where the clock and the leave button used to be. */
.nameSlot { font-size: 13px; color: var(--muted); min-width: 0; }

/* The name drawer. `--drawer-top` is set from the header's own height when it opens,
   so the drawer and its scrim begin below the control rather than covering it. */
.scrim {
  position: fixed; left: 0; right: 0; bottom: 0; top: var(--drawer-top, 60px);
  z-index: 25; background: rgba(4, 6, 14, 0.6);
}
.drawer {
  position: fixed; left: 0; right: 0; top: var(--drawer-top, 60px); z-index: 26;
  background: var(--panel-2); border-bottom: 1px solid var(--line);
  padding: 16px 18px; box-shadow: 0 24px 48px -30px rgba(0, 0, 0, 0.95);
  transform: translateY(-100%); transition: transform .18s ease;
}
.drawer.open { transform: translateY(0); }
.drawerForm { max-width: 660px; margin: 0 auto; }
.drawerTitle {
  margin: 0 0 10px; font-size: 12px; text-transform: uppercase;
  letter-spacing: .1em; color: var(--muted);
}
/* The invite link is read and selected rather than typed, so it does not take the
   code entry's field: that one is a large centred number because it is four taps
   and you have to see it. A web address wants to be small, left-aligned and
   selectable end to end. */
.shareRow { display: flex; gap: 10px; }
.shareLink {
  flex: 1; min-width: 0; background: var(--panel-2); border: 1px solid var(--line);
  color: var(--ink); border-radius: 12px; padding: 12px 14px; font-size: 13px;
}
.shareRow button { width: auto; margin: 0; padding: 12px 18px; }

.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 22px; margin-bottom: 18px;
  /* Lifted off the gradient: the cards are the game, the gradient is the room. */
  box-shadow: 0 20px 44px -28px rgba(0, 0, 0, 0.95);
}
.card.center { text-align: center; }

.eyebrow {
  margin: 0 0 6px; text-transform: uppercase; letter-spacing: .12em;
  font-size: 11px; color: var(--accent); font-weight: 700;
}

/* The city is the loudest thing on the screen: it is who the round is for. */
.cityName {
  margin: 0 0 4px;
  font-size: clamp(40px, 13.5vw, 78px);
  font-weight: 800;
  letter-spacing: -0.025em;
  line-height: 1;
  color: var(--accent);
}
/* It stays above the game on every screen, but steps back once a question is
   live so the heading does not crowd the play area on a phone. */
.cityName.live { font-size: clamp(26px, 7.5vw, 44px); }
.cityBar {
  width: 100%;
  max-width: 660px;
  margin: 0 auto;
  padding: 16px 18px 0;
  text-align: center;
}
h1, h2, h3 { margin: 0 0 10px; line-height: 1.15; }
.huge { font-size: clamp(38px, 12vw, 64px); margin: 4px 0 12px; }
.big { font-size: clamp(24px, 6.5vw, 34px); }
.sub { font-size: 14px; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); margin-top: 22px; }
/* The lead is the page heading. It is the only heading that is always present: on
   the front page it carries the game, and once a city is declared it names it. It
   borrows the body's type so it still reads as a line of copy rather than a title. */
.lede { color: var(--ink); margin: 0 0 12px; font-size: inherit; font-weight: inherit; line-height: inherit; }
.muted { color: var(--muted); }
.fine { color: var(--muted); font-size: 13px; line-height: 1.6; }
.fine a, .linkish { color: var(--muted); }
/* A pointer line rather than a sentence. An anchor inside `.fine` is muted grey, so
   on its own line it is findable only by its underline - which is how a link at the
   end of a paragraph reads as more of the paragraph. On its own line it gets the
   page's white, the same treatment the prose links above already get. */
.fine.pointer a { color: var(--ink); }
/* The nearby lines are actions, not prose: taking one of them changes the city you
   play for. An anchor inside `.fine` is muted grey, which is right for a footnote
   and wrong for a control, so these get the same white the prose links get. */
.fine a.cityPick { color: var(--ink); }
/* Body copy is muted grey, and a link inherits that grey - so a link inside a
   sentence read as more of the sentence. Links in the prose are the page's white
   instead, which is what makes them findable without shouting. */
.muted a { color: var(--ink); }

/* The consent banner.
   Three things here are deliberate and should stay that way.
   It sits in one row - the question, then the two answers - because a banner that
   stacks into paragraphs is a banner people resent. The two answers are the same
   size, the same weight and the same distance from the reader: no accept-first
   order, no smaller decline, no colour that quietly says which one is welcome. And
   the detail lives one link away rather than in the bar itself. */

/* A fixed banner sits on top of whatever is behind it and takes the click, so the
   page leaves exactly the banner's own height free while it is up - measured and
   written back by consent.js, and reset to zero the moment the question is
   answered. Before this the footer could not be clicked at all until a visitor
   answered a question about cookies, which is not a question anyone should have to
   answer to reach a link. */
body { padding-bottom: var(--consent-height, 0px); }

.consent {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 22px;
  padding: 13px 20px; background: var(--panel);
  border-top: 1px solid var(--line); backdrop-filter: blur(8px);
  /* It is the last thing on the screen, so it must never be taller than the screen.
     On a short viewport - a phone in landscape is about 320px tall - the stacked
     layout could otherwise grow past the top edge, and the part above it is fixed
     to the window: it cannot be scrolled to and cannot be clicked. Capped and made
     scrollable, every part of the question stays reachable. `dvh` is the height the
     browser actually gives the page once its own toolbars are accounted for; `vh`
     stays first for anything that does not know it. */
  max-height: calc(100vh - 12px);
  max-height: calc(100dvh - 12px);
  overflow-y: auto;
}
.consentTitle {
  flex: 0 0 auto; margin: 0; font-size: 14px; color: var(--ink); white-space: nowrap;
}
.consentText {
  flex: 1 1 300px; margin: 0; font-size: 13px; line-height: 1.5; color: var(--muted);
}
/* A grid, not a flex row, and that is deliberate. `flex: 1 1 0` split the free
   space arithmetically and the two answers came out 288px and 289px at 1280 -
   one pixel apart, because of sub-pixel rounding. Two answers meant to be
   indistinguishable should not depend on rounding: grid columns of `minmax(0, 1fr)`
   are exactly equal at every width, for any number of buttons.
   `min-width: 0` on the buttons is what lets a long label wrap inside its own
   column instead of pushing that column wider than the other one. */
.consentActions {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  flex: 1 1 auto; gap: 10px;
}
.consentActions button { min-width: 0; }
/* An anchor does not inherit body colour, so a link dropped into new markup gets
   the browser's default blue - which is out of place on this panel. Anything that
   wants to look like this site has to say so. */
.consentText a { color: var(--ink); }

/* The question and the longer answer share ONE fixed bar and swap places inside
   it, rather than a second dialog opening over the first. That is not tidiness:
   this bar is the element whose height is measured and reserved at the bottom of
   the page, and the observer already follows it - so the taller view is measured
   like the shorter one instead of guessed at. */
.consentAsk {
  flex: 1 1 320px; display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px 22px;
}
.consentPrefs { flex: 1 1 100%; }
/* The title's row, so the way out sits at the top right of the panel and not among
   the answers at the bottom. A panel with no dismiss is a consent wall: somebody
   who opened it to LOOK had to answer it again to get out. */
.consentHead { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.consentClose {
  flex: 0 0 auto; background: none; border: 1px solid var(--line); border-radius: 10px;
  color: var(--muted); font: inherit; font-size: 17px; line-height: 1; cursor: pointer;
  padding: 3px 10px;
}
.consentClose:hover, .consentClose:focus-visible { color: var(--ink); border-color: var(--ink); }
/* The way to the longer answer, and quieter than the two answers on purpose: it
   is not a third answer to the question, and it must not read as one. */
.consentSettings { flex: 0 0 auto; color: var(--muted); }

/* EVERY PURPOSE IS NAMED. There is one real choice on this site and one cookie
   that is not a choice; a list of imaginary categories would be a longer panel
   that says less. */
.consentList { list-style: none; margin: 8px 0 0; padding: 0; }
.consentRow {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px;
  padding: 10px 0; border-top: 1px solid var(--line);
}
/* `min-width: 0` so a long sentence wraps inside its own column instead of
   pushing the control beside it off the edge - the same trap the city field fell
   into, in the same place. */
.consentRow > div { flex: 1 1 260px; min-width: 0; }
.consentRowName { margin: 0; font-size: 13px; font-weight: 600; color: var(--ink); }
.consentRowWhy { margin: 2px 0 0; font-size: 12px; line-height: 1.5; color: var(--muted); }
/* The state of a thing that has no choice in it, said in words rather than shown
   as a switch that refuses to move. */
.consentFixed { margin: 0; font-size: 12px; color: var(--muted); white-space: nowrap; }

/* The switch. Its ring is drawn from `aria-checked`, so what the eye sees and
   what a screen reader announces are the same attribute and cannot drift apart -
   nothing in the script toggles a class. The word beside it carries the state as
   well, because a state shown only in colour is a state some readers cannot see. */
.consentSwitch {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 8px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 12px; font-size: 12px; color: var(--muted); cursor: pointer;
}
.consentSwitch .consentDot {
  width: 10px; height: 10px; border-radius: 50%; background: var(--muted);
}
.consentSwitch[aria-checked="true"] { border-color: var(--accent); color: var(--ink); }
.consentSwitch[aria-checked="true"] .consentDot { background: var(--accent); }

/* A real list on the text pages. Every list in the game has its own rules already,
   and scoping to `article` keeps this away from them. */
article ul { margin: 0 0 14px; padding-left: 20px; }
article ul li { margin: 0 0 10px; }

/* On a phone the row cannot hold three things, so the text takes a line and the
   two answers take their own - stacked, full width, still the same size, the same
   weight and the same distance from the reader.
   Two label-sized buttons side by side was the first attempt and it does not work:
   each label needs more width than half a phone, so `nowrap` ran the text outside
   its own button. `flex-basis: 100%` on the buttons does not fix it either - a
   percentage basis inside a shrink-to-fit container is circular, and the browser
   falls back to the labels' full width.
   KEEP THIS BLOCK LAST IN THE FILE. A duplicate `.consentActions` rule sitting
   below it silently overrode it once already, and nothing about the page said so. */
@media (max-width: 640px) {
  .consent { gap: 10px 14px; padding: 12px 16px; }
  .consentTitle { white-space: normal; }
  .consentText { flex: 1 1 100%; }
  .consentActions { display: flex; flex-direction: column; flex: 1 1 100%; }
  .consentActions button { width: 100%; }
}
.linkish { background: none; border: 0; padding: 0; font: inherit; text-decoration: underline; cursor: pointer; }
/* The way back off a board you cannot answer for. It sits under the line it belongs
   to and stays quiet: it is a correction, not an invitation. */
/* Under the city name now, so the gap goes below rather than above, and it only
   takes up room while it is actually offered. */
.cityChange { display: block; margin: 0 auto 14px; font-size: 13px; color: var(--muted); }
/* The title's place with no title in it. Amber, because it is the one thing to do here
   - without a city you are not on anybody's board - but at the size of a control rather
   than of a name, because the city name is exactly what this is not. */
button.cityAsk {
  display: block; width: 100%; max-width: 420px; margin: 0 auto 14px;
  padding: 14px 18px; font-size: clamp(18px, 4.5vw, 22px); font-weight: 700;
  color: var(--accent); background: rgba(251, 191, 36, 0.12);
  border: 1px solid rgba(251, 191, 36, 0.45);
}
button.cityAsk:hover { background: rgba(251, 191, 36, 0.2); }
/* The nearest boards to this one, under the control that changes it. 14px is the
   change control's own margin, so the gap down to the card is the same whether or not
   there is a list under it - and the list is hidden whenever changing is not offered. */
.cityLinks { margin: 0 0 14px; }
/* Inside the picker, under the fine print: only there is there anything to clear. */
.cityClear { display: block; margin: 14px auto 0; font-size: 13px; color: var(--muted); }
/* Below the card, centred, and the same quiet weight as the header's leave control.
   `fit-content` because a block button still shrinks to its label, and auto margins
   only centre something with a definite width. */
.lobbyLeave { display: block; width: fit-content; margin: 0 auto 18px; font-size: 13px; color: var(--muted); }
/* The leave question, in the page. Both answers are the same control for the same
   reason the cookie banner's are: leaving costs the night, and neither answer should
   be dressed as the easy one. */
.leaveAsk { text-align: center; margin-top: 32px; }
.leaveConfirm { margin: 0 auto 18px; max-width: 420px; }
.leaveQuestion { margin: 0 0 12px; font-size: 13px; color: var(--muted); }
.confirmText { margin: 0 0 12px; font-size: 14px; color: var(--muted); }
/* Two answers of equal size and weight, everywhere a question is asked. Never a
   primary and a secondary: on the destructive ones that would be the site picking for
   the reader, and the cookie banner already settled that it does not do that. */
.askChoices { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.askChoices button { flex: 1 1 150px; }

button { font: inherit; cursor: pointer; border-radius: 12px; }
button.primary {
  background: var(--accent); color: #1a1200; border: 0;
  padding: 14px 18px; font-weight: 700; width: 100%; margin-bottom: 10px;
}
button.primary:hover { filter: brightness(1.06); }
button.ghost {
  background: transparent; color: var(--ink); border: 1px solid var(--line);
  padding: 12px 18px; width: 100%; margin-bottom: 10px;
}
button:disabled { opacity: .5; cursor: default; }

/* The one control on the page that is navigation rather than a game action, sent to
   players who have no city yet and have never seen this before: a countdown is not
   an explanation. It is styled on the action colour so a newcomer notices it, and it
   is not a solid amber button because it must not outshout entering the round. */
a.how {
  display: block; text-align: center; text-decoration: none; font: inherit; font-weight: 600;
  border: 1px solid rgba(251, 191, 36, 0.45);
  background: rgba(251, 191, 36, 0.08);
  color: var(--accent);
  padding: 13px 18px; margin: 4px 0 14px; border-radius: 12px;
}
a.how:hover { background: rgba(251, 191, 36, 0.16); }

.row { display: flex; gap: 10px; }
.row button { margin-bottom: 0; }

/* HUD */
.hud { display: flex; align-items: center; justify-content: space-between; gap: 10px 14px; margin-bottom: 18px; flex-wrap: wrap; }
.hud-left { display: flex; align-items: center; gap: 12px; }
.step { font-size: 14px; color: var(--muted); }
.lives { display: inline-flex; gap: 5px; }
.lives i { width: 11px; height: 11px; border-radius: 50%; background: var(--good); display: block; }
.lives i.spent { background: #33405c; }
/* `min-width` on the timer is what stops the countdown being squeezed to nothing
   when the step label and the lives are on the same line, so it stays. Wrapping is
   what keeps it honest on the narrowest screens: at 280px - a folded phone - the
   row could not hold both and pushed the timer past the right edge. `margin-left:
   auto` is so a wrapped timer still sits on the right, which is where a countdown
   belongs. */
.timer { text-align: right; min-width: 96px; margin-left: auto; }
.timerNum { font-size: 22px; font-weight: 700; }
.timerBar { height: 4px; background: var(--panel-2); border-radius: 999px; overflow: hidden; margin-top: 4px; }
.timerBar i { display: block; height: 100%; width: 100%; background: var(--accent); transition: width .25s linear; }

.prompt { font-size: clamp(20px, 5.4vw, 27px); margin-bottom: 18px; }

/* The declaration. It is the only way onto a city board, so it sits on the
   waiting screen where it can still change something. */
.citySetup {
  margin: 18px 0 6px;
  padding: 16px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  text-align: left;
}
.cityForm { margin: 10px 0 0; }

/* The city search results. A list rather than a datalist, because the label has
   to carry the country: there are many Hamiltons and the player must be able to
   see which one they are picking. */
.cityResults {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  background: var(--panel);
}

.cityResult {
  padding: 11px 14px;
  cursor: pointer;
  font-size: 15px;
  border-top: 1px solid var(--line);
}

.cityResult:first-child { border-top: 0; }
.cityResult:hover, .cityResult:focus { background: var(--panel-2); outline: none; }

.cityFallback { margin-top: 14px; }
.cityFallback summary { cursor: pointer; color: var(--muted); font-size: 13px; }
.cityFallback .cityForm { margin-top: 8px; }
.cityState {
  margin: 12px 0 4px;
  font-size: 14px;
  color: var(--accent);
}

/* The reminder opt-in. Deliberately quiet: it is an offer, not a demand, and the
   game is playable without it. */
.pushSetup {
  margin: 14px 0 6px;
  padding: 14px;
  border: 1px dashed var(--line);
  border-radius: 12px;
}
.pushSetup .fine { margin: 8px 0 0; }

/* The bonus question. It is the one that decides the city standing, so it gets
   its own note rather than blending in with the nine. */
.qualifierNote {
  margin: -8px 0 16px;
  font-size: 13px;
  color: var(--accent);
  letter-spacing: .01em;
}

.choices { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; }
.choice {
  background: var(--panel-2); color: var(--ink); border: 1px solid var(--line);
  padding: 16px; text-align: left; font-size: 17px; width: 100%;
}
.choice:hover:not(:disabled) { border-color: var(--accent); }
.choice.picked { border-color: var(--accent); background: #20293c; }
.choice.correct { border-color: var(--good); }
.choice.wrong { border-color: var(--bad); }

.yearForm { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; }
.yearRow { display: flex; gap: 10px; }
.yearInput {
  flex: 1 1 0; min-width: 0; background: var(--panel-2); border: 1px solid var(--line); color: var(--ink);
  border-radius: 12px; padding: 14px; font-size: 22px; text-align: center;
}
.yearRow button { width: auto; margin: 0; padding: 14px 20px; }
/* A text field and its button in one row, on a screen too narrow to hold both.
   This was a real overhang and a real dead end: `flex: 1` leaves `min-width: auto`,
   and a text input's automatic minimum is its own intrinsic width - about twenty
   characters - so the field refused to narrow, pushed the button past the right edge
   and made the whole document wider than the screen. Measured before the fix, with
   the picker on screen: the page was 454px wide at every viewport below 480px, which
   is a sideways scroll on a phone and an unreachable "Set my city".
   `min-width: 0` is the part that removes the overhang at any width, and it is what
   must not be dropped. The stack below is only so the field is not left ~170px wide
   on the smallest phones; it matches what the consent banner already does at this
   same breakpoint. */
@media (max-width: 640px) {
  .yearRow { flex-wrap: wrap; }
  .yearRow > * { min-width: 0; }
  .yearRow > .yearInput { flex: 1 1 100%; }
  .yearRow > button { flex: 1 1 100%; }
}
/* The other half of the same trap, and the one that actually bit the question view.
   A grid item's automatic minimum is its `min-content` size, so a single `auto`
   column is at least as wide as its widest item's minimum - and a text field's
   minimum is about twenty characters. `minmax(0, 1fr)` lets a column be narrower
   than its contents, which is the only thing that stops a long field, a long
   unbreakable word or a big number from widening the whole document. `1fr` alone is
   `minmax(auto, 1fr)` and does NOT do this: measured with the year question on
   screen at 320px, `#yearForm`'s column forced the page to 351px and the field, its
   button, its label and the hint all sat past the right edge.
   Every grid on the site is listed here on purpose, so a new one cannot quietly
   reintroduce it. The consent banner's grid already used `minmax(0, 1fr)`; the
   histogram's own rules carry it where they are declared. */
.locked { text-align: center; margin-top: 14px; font-size: 14px; }

/* Near-miss card */
.nearmiss {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: linear-gradient(180deg, #182136, #121722);
  padding: 24px 18px; margin: 16px 0;
}
.nm-big { font-size: clamp(40px, 13vw, 66px); font-weight: 700; margin: 0; color: var(--accent); }
.nm-label { margin: 2px 0 14px; color: var(--muted); font-size: 14px; }
.nm-mid { margin: 0 0 6px; font-size: 19px; font-weight: 600; }
.nm-small { margin: 0; color: var(--muted); font-size: 14px; }

.histogram { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 7px; }
.histogram li { display: grid; grid-template-columns: 62px minmax(0, 1fr) 44px; align-items: center; gap: 10px; font-size: 13px; color: var(--muted); }
.histogram .bar { height: 9px; background: var(--panel-2); border-radius: 999px; overflow: hidden; }
.histogram .bar i { display: block; height: 100%; background: #3f5b93; }
.histogram .bar.mine i { background: var(--accent); }

.leaders { margin: 0; padding-left: 20px; color: var(--muted); font-size: 14px; }
.leaders li { margin-bottom: 4px; }
.answers { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
.answers li { border-top: 1px solid var(--line); padding-top: 10px; font-size: 14px; color: var(--muted); }
.answers b { color: var(--ink); display: block; margin-bottom: 2px; font-weight: 600; }

.stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; text-align: center; }
.stat { display: block; font-size: clamp(22px, 6vw, 30px); font-weight: 700; color: var(--accent); }
.statLabel { display: block; font-size: 12px; color: var(--muted); margin-top: 4px; }

.bottom { padding: 16px 18px 26px; text-align: center; border-top: 1px solid var(--line); }

/* The notification. Every other panel is drawn with the same hairline, which is why
   this one had to wear more than that to be noticed at all: the accent, a halo of it,
   and a surface solid enough to sit over anything. The accent is the site's own "this
   wants something from you" colour, and a message wants to be read. */
.toast {
  position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 8px;
  background: #1b2338; border: 1px solid var(--accent); color: var(--ink);
  padding: 10px 10px 10px 16px; border-radius: 12px; font-size: 14px;
  max-width: min(90vw, 520px); z-index: 30;
  box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.16), 0 18px 40px -22px rgba(0, 0, 0, 0.95);
}
.toastText { flex: 1 1 auto; }
/* Quiet until it is aimed at, then plainly a control. 28px is the smallest square
   that is comfortably hittable, and this one sits over whatever is behind it. */
.toastClose {
  flex: 0 0 auto; width: 28px; height: 28px; padding: 0; margin: 0;
  background: transparent; border: 1px solid transparent; border-radius: 8px;
  color: var(--muted); font-size: 18px; line-height: 1;
}
.toastClose:hover { color: var(--ink); border-color: var(--line); }

[hidden] { display: none !important; }

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