/*
 * strkBTC campaign identity. Read from the brand file TpOL8rCVFf85ObNlbLR4f1 on 2026-08-19 and
 * specified in docs/ROADMAP-v0.4.md §11.10: a dark, typographic identity — Shielded Black ground,
 * one accent, sharp 2px radii, thin 1px rules as section dividers, large mono numerals for stats,
 * and exactly one visual effect (an orange radial bloom behind the hero).
 *
 * This replaces the Frutiger Aero skin and its Snow Leopard alternate. There is no theme system
 * any more, no window chrome, nothing draggable and nothing randomised.
 *
 * RECONCILED WITH THE FAUCET on 2026-09-08, which is why several values below are not the brand
 * file's: the strkBTC faucet (staging /faucet) is a live application on these same two faces, so
 * where the brand file only described a thing and the faucet ships it, the faucet's value wins.
 * Its ground, borders, text colours, button radius, section rhythm, card treatment and FAQ marker
 * are taken; its 1240px multi-column shell is not, because this page is a single column around a
 * coin sized off the viewport. Two of its colours fail AA and are refused — see the note in
 * :root. THE BIGGEST CHANGE WAS NOT COLOUR: it sets every label, button and title in Neue
 * Montreal and reserves mono for one selector (.address), and this file had that the other way
 * round. Mono here now means numerals, addresses and the log.
 *
 * TYPE: TWO OF THE IDENTITY'S THREE FACES NOW SHIP. Unison Pro Bold (display) and PP Neue
 * Montreal Book (body) were supplied on 2026-09-07 and are self-hosted as woff2 under web/fonts/,
 * converted from the licensed OTFs. Both are licensed faces, so this repo going public needs a
 * WEB licence for each — that is a licence question, not a code one, and it is not settled here.
 *
 * The third face, GT America Mono (labels and numerals), was NOT supplied. So --mono below is
 * still a system mono stack standing in for it, and it keeps every tabular role: the countdown
 * clock, addresses, and the boards' numerals, where a proportional face would make the digits
 * jitter once a second. --display and --ui both fall back to that same stack, so a font that
 * fails to load leaves the page exactly as it looked before these two arrived.
 */

/* One weight each — Unison Pro in Bold, Neue Montreal in Book. Nothing declares a second, so the
   700s and 600s already in this file synthesise a faux bold off Book. Accepted: the alternative is
   two more binaries for a handful of 13px labels. font-display: swap — the fallback stack is the
   pre-2026-09-07 design, so swapping in is an upgrade rather than a flash of something wrong. */
@font-face {
  font-family: "Unison Pro";
  src: url(fonts/unison-pro-bold.woff2) format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Neue Montreal";
  src: url(fonts/neue-montreal-book.woff2) format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

:root {
  /* ── The palette, whole. Five entries (brand file slide 8, node 1:618). ───── */
  --ground:  #0b0b0b;            /* Shielded Black, at the faucet's value */
  --white:   #ffffff;
  --gray:    #2a2a2a;
  --accent:  #c53400;            /* Rusted Orange — the only CTA fill */
  /* Sunrise. The swatch's caption disagrees with its own fill; the rendered fill is
     authoritative (decided 2026-08-19), so this is #FEE252 → #C14211 with the stop at 60.69%. */
  --sunrise: linear-gradient(180deg, #fee252 0%, #c14211 60.69%);

  /*
   * Reconciled with the strkBTC faucet (staging /faucet, read 2026-09-08) so the two campaign
   * pages read as one site: its --bg, --border, --text and --text-dim, under the names this file
   * already uses. Ratios are WCAG 2.1 against the ground, computed rather than eyeballed, and
   * TWO of the faucet's values are deliberately NOT taken:
   *
   *   Its --text-faint #7c766d carries its section labels and its footer, and is 4.38:1 on the
   *   ground and 4.23:1 on a panel — under the 4.5:1 normal text needs. Muted text here stays on
   *   --ink-dim, which is the faucet's --text-dim at 9.45:1.
   *
   *   Its --accent #ed4a16 under white text is 3.76:1, so its own buttons fail AA. The fill here
   *   stays Rusted Orange (white on it is 5.43:1) and #ed4a16 takes the orange TEXT role, where
   *   it is 5.24:1 on the ground and 5.07:1 on a panel. Same hue as the faucet everywhere a
   *   reader compares them; the fill alone is a shade deeper.
   *
   * Gray #2a2a2a is 1.37:1 — a rule or a surface, never text.
   */
  --ink:        #f3f0e8;         /* the faucet's --text: warm off-white, 17.28:1 */
  --ink-dim:    #b9b3a9;         /* the faucet's --text-dim, 9.45:1 */
  --accent-text:#ed4a16;         /* the faucet's --accent, in its text role */

  --line:    var(--gray);        /* thin, sharp lines as dividers and texture — slide 14 */
  /* The faucet's --border-soft: the one step quieter it uses for the rules that bound the page
     top and bottom, as against the rules that divide its content. */
  --line-soft: #1d1d1d;
  --panel:   #101010;            /* the feed and the boards sit one step off the ground */
  /* 2px is the brand file's own figure and it stays, against the faucet's hard 0 on panels: the
     brand file is the source, the faucet is a reading of it. Its 4px button is taken, below. */
  --radius:  2px;

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  /* The display role: the page title and the section titles, nothing else. */
  --display: "Unison Pro", var(--mono);
  --ui: "Neue Montreal", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
        sans-serif;

  /* The coin's edge. Darkened Sunrise rather than a new hue: it is the same object in shadow. */
  --rim:      #8f2e08;
  --rim-dark: #5a1d05;

  /* The faucet's page width, and now this page's. */
  --shell: 1240px;
  /* The reading measure inside it. A block that is prose or a list of one-liners does not improve
     by being 1240px wide, and 860px is the faucet's own figure — what it caps its FAQ answers at
     inside this same shell. Three users here: the two boards, the feed, and those same answers.
     It replaced --col (720px), which the shell left with one user and a name that had stopped
     describing anything: there is no single centred column any more. */
  --measure: 860px;

  /*
   * The coin's diameter.
   *
   * Sized against BOTH axes, which the spec's `min(624px, 88vw)` was not: width alone leaves a
   * short viewport unprotected, and a 624px circle plus its shadow does not fit a 1280x720
   * laptop or any phone in landscape. 50vh keeps the clock and the coin on screen
   * together; 84vw keeps it off the gutters; the clamp stops it vanishing on a small phone or
   * ballooning on a 4K panel.
   *
   * Measured, not guessed: the coin is NOT what costs the fold — the dismissible connect
   * prompt above it is 111px on a MacBook Air and 173px on an iPhone SE, and with it away the
   * coin and its rim clear the fold on both. Shrink that strip before shrinking this again.
   *
   * This is deliberately smaller than §11.3's 624px — 520px is 2.5x the old 208px cap rather
   * than 3x. Asked for 2026-08-19: fitting every screen beat hitting the number exactly.
   */
  /* 620px, up from 520, and 62vh up from 50vh. The old pair was set when the coin sat UNDER the
     clock in a 720px column and the two had to clear the fold together; from 981px up they are
     side by side, so the coin's own height is the only vertical claim and the fold maths changed
     with the layout. header (88) + hero padding (32) + 62vh <= viewport holds above 316px tall,
     and the vh term still governs every laptop — 62vh is 496px on an 800px screen. 620 is what
     the faucet caps its own hero image at, and within 4px of §11.3's original 624. */
  --coin: clamp(200px, min(84vw, 62vh), 620px);
}

/* dvh accounts for the mobile URL bar, which vh does not. Guarded rather than declared twice:
   an unknown unit inside a custom property is only caught at use time, where it would take
   `width` with it — so the fallback has to be a whole separate declaration, not a preceding one. */
@supports (height: 1dvh) {
  :root { --coin: clamp(200px, min(84vw, 62dvh), 620px); }
}

html { color-scheme: dark; }

*, *::before, *::after { box-sizing: border-box; }

/* One rule rather than five per-element ones. `hidden` has to beat `display: flex` on the
   handful of elements the script toggles, and did so through a scattered set of overrides. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100vh;
  padding: 0 28px 48px;
  font: 15px/1.6 var(--ui);
  color: var(--ink);
  background: var(--ground);
}

/* ── Header ──────────────────────────────────────────────────────────────── */
/* Wordmark left, exactly one CTA right — the reference application's own arrangement. */
.head {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 22px 0;
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  border-bottom: 1px solid var(--line-soft);
}
.wordmark {
  display: flex; align-items: center; gap: 4.3px;
  color: var(--white);
}
/* The mark's minimum is 30px digital (brand file slide 6), and it is the size everything else
   here is derived from. The lettering's height, its width and the gap between the two are the
   lockup's own proportions against that 30px — measured off the supplied artwork, where the
   letters are 222x42 and the mark 97x97, so 42/97 and 222/97 of the mark. Keeping them as
   ratios is what stops the pair drifting if the 30px floor ever moves. */
.mark { width: 30px; height: 30px; display: block; }
.word { width: 68.7px; height: 13px; display: block; }

/* ── Buttons ─────────────────────────────────────────────────────────────── */
/* Sharp rectangles, uppercase mono. The coin is the one exception on the page. */
.btn {
  background: none;
  border: 1px solid var(--line);
  border-radius: 4px;            /* the faucet's button radius, and the one place it is not 2px */
  color: var(--ink);
  font: 700 13px/1 var(--ui);
  letter-spacing: .18em; text-transform: uppercase;
  padding: 14px 26px;
  cursor: pointer;
}
.btn:hover:not(:disabled) { border-color: var(--ink-dim); }
.btn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
.btn:disabled { color: var(--ink-dim); cursor: not-allowed; }

/* The only CTA colour, and the only place it is a fill. */
.btn-cta {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--white);
}
/* In the header it is a nav control, not a call to action in the page: sized to the status line
   beside it (12px, .22em) rather than to the strips below, where the full 14px/26px button sits
   next to a paragraph. At the full size it was the tallest thing in the header by half again. */
.head .btn-cta { padding: 10px 16px; font-size: 12px; letter-spacing: .16em; }
.btn-cta:hover:not(:disabled) { background: #e03c00; border-color: #e03c00; }

/* The wrong-network pill, in that same CTA slot: the accent as an outline, not a fill, so it
   reads as a blocker rather than as the action to take. */
.btn-alarm { background: none; border-color: var(--accent-text); color: var(--accent-text); }
.btn-alarm:hover:not(:disabled) { background: none; border-color: var(--accent-text); }

/* The same slot mid-connect ("Connecting…", "Reconnecting…"): still the CTA, dimmed and
   breathing, so it reads as working rather than switched off. It is disabled — the click is what
   starts a connect, and a second over the first is two pickers — but not painted with the grey
   .btn:disabled gives an outline button, which on the orange fill looked broken. */
.btn-cta.busy, .btn-cta.busy:disabled {
  color: var(--white); opacity: .75; cursor: progress;
  animation: pillpulse 1.4s ease-in-out infinite;
}
@keyframes pillpulse { 50% { opacity: .45; } }

/* Connected: the address, with a lit dot in front of it, and the one thing the click does said
   out loud on hover. The address stays in the box (transparent) so the pill keeps its width;
   "Disconnect" is laid over it. Without this the click was a footgun — the page's only CTA
   dropped the wallet on a click, and the tooltip was the only warning. */
.btn-cta.connected { position: relative; }
.btn-cta.connected::before {
  content: ''; display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: #fee252; margin-right: 10px; vertical-align: 1px;
  box-shadow: 0 0 6px #fee252;
}
.btn-cta.connected:hover, .btn-cta.connected:focus-visible { color: transparent; }
.btn-cta.connected:hover::before, .btn-cta.connected:focus-visible::before { visibility: hidden; }
.btn-cta.connected:hover::after, .btn-cta.connected:focus-visible::after {
  content: 'Disconnect'; position: absolute; inset: 0;
  display: grid; place-items: center; color: var(--white);
}

.tbtn {
  width: 26px; height: 26px; flex: none;
  background: none; border: none; border-radius: var(--radius);
  color: var(--ink-dim);
  font: 700 13px/1 var(--ui);
  display: grid; place-items: center;
  cursor: pointer;
}
.tbtn:hover { color: var(--white); }
.tbtn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 1px; }

/* ── Status pills ────────────────────────────────────────────────────────── */
.status {
  flex: 1;
  display: flex; gap: 8px; justify-content: flex-end;
  font: 700 12px/1 var(--ui);
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--ink-dim);
  white-space: nowrap;
}
.status span + span::before { content: "·"; margin-right: 8px; }
.status .grow { font-variant-numeric: tabular-nums; }

/* ── The dethroning alert, and the connect prompt ────────────────────────── */
/*
 * The banner reports the round; this reports something that happened *to you*. Fixed rather
 * than modal on purpose: the answer to this warning is to press the button, and a dialog that
 * covers the button is a dialog arguing with itself.
 */
.alert {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 10;
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 12px 16px;
  background: var(--accent);
  color: var(--white);
  animation: alarmshake .42s ease-out 2;
}
.alert .btn { border-color: rgba(255, 255, 255, .55); color: var(--white); }
.alert .btn:hover:not(:disabled) { border-color: var(--white); }
.alert .btn-cta { background: var(--white); border-color: var(--white); color: var(--accent); }
.alert .btn-cta:hover:not(:disabled) { background: #ffe9df; border-color: #ffe9df; }
.alert .tbtn { color: rgba(255, 255, 255, .8); }
.alert .tbtn:hover { color: var(--white); }

/* Its calm twin, in the page flow rather than over it. */
.cta-strip {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.alert-text { flex: 1; min-width: 240px; display: flex; flex-direction: column; gap: 3px; }
.alert-text b { font: 700 12px/1.3 var(--ui); letter-spacing: .1em; text-transform: uppercase; }
.alert-text span { font-size: 13px; }
.cta-strip .alert-text span { color: var(--ink-dim); }
.alert-actions { display: flex; align-items: center; gap: 8px; }

/* Two beats sideways, and the second is smaller — a strip knocked, not a strip vibrating. */
@keyframes alarmshake {
  0%, 100% { transform: translateX(0); }
  20%  { transform: translateX(-9px); }
  45%  { transform: translateX(7px); }
  70%  { transform: translateX(-4px); }
}

/* ── The column ──────────────────────────────────────────────────────────── */
.col { max-width: var(--shell); margin: 0 auto; display: flex; flex-direction: column; }

/* Thin sharp rules between sections, which is the identity's own texture. */
.band { padding: 48px 0; border-top: 1px solid var(--line); }
.cta-strip { margin-top: 24px; }
.sec-title {
  margin: 0 0 16px;
  font: 700 12px/1 var(--ui);
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--ink-dim);
}
/* The faucet's .campaign-title: a large display heading, where .sec-title above is its small
   tracked .section-label. Still scoped to the two headings the campaign owner named — Leaderboard
   and More in the campaign — so "Recent presses", FAQ and Terms stay label-only, which is how the
   faucet sets its own FAQ heading too. Its titles are a flat 44px, dropping to 30px under 980; a
   clamp is the same two ends without the breakpoint — but the top end is 36px, not 44px: this
   column is 720px where the faucet's is 1240px, and measured in the browser "More in the
   campaign" takes two lines at 40px and one at 38px. 36px keeps a line's worth of slack for a
   fallback face that sets wider than Unison Pro.

   THE FLOOR IS 22px AND IT IS NOT TASTE: "Leaderboard" is ONE WORD with nothing to break on, so
   at the faucet's own 30px floor it is 343px wide inside a 288px column and a 320px viewport
   scrolls sideways. Measured, it fits at 24px there and at 28px on a 360. 7vw with a 22px floor
   clears every width from 320 up with slack; the faucet never meets this because both of its
   titles have spaces in them. Re-measure before raising either end.

   The faucet also pairs a label ABOVE each title ("How it works" over "One draw. Two claims.").
   Not taken: the second line of every pair is copy nobody has written, and this page does not
   invent campaign copy. Whoever writes it adds a <p class="sec-title"> above the h2. */
.sec-title.is-display {
  font: 700 clamp(22px, 7vw, 36px)/1.15 var(--display);
  letter-spacing: 0; text-transform: none;
  color: var(--ink);
}

/* ── Hero ────────────────────────────────────────────────────────────────── */
.hero { padding: 28px 0 24px; text-align: center; }

/* The headline block. .title is the display face, Unison Pro (see the top of this file); the
   tagline stays on the mono, because LAST PRESS WINS is the one line in accent — it is the rule
   of the game, and the page already paints the rule orange everywhere else. */
/* Sized DOWN from the first draft, deliberately. At 34px with .12em tracking this block read as
   a second, larger logo than the mark in the header — two lockups arguing about which one is the
   brand. It is a headline, so it is set below the mark's weight: the tracking comes in, the sizes
   come down, and the eye goes header → headline → clock instead of stopping here. */
.title {
  margin: 0;
  font: 700 clamp(16px, 3.2vw, 21px)/1.2 var(--display);
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink);
}
.tagline {
  margin: 5px 0 0;
  font: 700 clamp(11px, 2vw, 13px)/1.2 var(--ui);
  /* text-indent balances the trailing letter-space, the same trick .clock-sub uses: without it
     a centred tracked line sits half a space left of true centre. */
  letter-spacing: .28em; text-indent: .28em; text-transform: uppercase;
  color: var(--accent-text);
}
.lede {
  margin: 12px auto 0;
  max-width: 42em;
  font-size: 14px; line-height: 1.55;
  color: var(--ink-dim);
}

/* An orange uppercase mono eyebrow above the numerals — the identity's own headline pattern,
   and the natural home for the phase line the banner used to carry. */
.eyebrow {
  margin: 18px 0 0;
  display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap;
  justify-content: center;
  font-size: 13px; color: var(--ink-dim);
}
.eyebrow-label { text-transform: uppercase; letter-spacing: .1em; font-size: 11px; }

.eyebrow b {
  font: 700 12px/1 var(--ui);
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--accent-text);
}

/*
 * The one visual effect on the page: a single static orange bloom behind the hero numerals.
 * Static, so it costs nothing against the no-moving-components decision — it replaces four
 * animated bokeh orbs.
 */
.stage {
  position: relative;
  padding: 28px 12px 40px;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: radial-gradient(ellipse 60% 46% at 50% 26%, rgba(197, 52, 0, .30) 0%, transparent 70%);
}

/* Large mono numerals are the identity's way of showing a stat, so the clock takes the display
   role the deferred Unison Pro would otherwise have had. */
.clock {
  font: 400 clamp(44px, 11vw, 76px)/1 var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em; text-indent: .04em;
  color: var(--ink);
}
.clock.idle {
  font-size: clamp(22px, 5.5vw, 34px);
  color: var(--ink-dim);
  letter-spacing: .16em; text-indent: .16em;
  padding: 16px 0;
}
/*
 * Warm-up. The number is the same size and place as a live win timer, and that was the problem:
 * the biggest thing on the page read as "time until someone wins" during the one phase where
 * nobody can win. So it is bracketed — a figure in brackets is a note about the game, not the
 * game's own clock, and the subline under it says what it counts down to.
 *
 * NO EXTRA DIMMING, deliberately. A first pass took it a step below .idle to #7c7c7c, which is
 * 4.52:1 against Shielded Black — it scrapes AA for large text and would fail the moment anyone
 * reused the colour on a label. The palette's own Gray is 1.85:1 and the brand file keeps it off
 * text entirely, so there is no darker value here that is allowed to hold a numeral. --ink-dim
 * (6.71:1) it stays, and the brackets carry the difference instead.
 *
 * Brackets in CSS, not in the string: #clock's textContent is the value the tab title and the
 * aria-live announcement both read, and neither wants punctuation the reader did not ask for.
 * A colon-separated time inside brackets is still announced as a time.
 */
.clock.idle.warmup::before { content: "["; margin-right: .18em; }
.clock.idle.warmup::after  { content: "]"; margin-left: .18em; }

/* The endgame's loudest cue. In --accent-text, not --accent: see the contrast note at the top. */
.clock.panic { color: var(--accent-text); }
.clock.panic.blink { animation: lcdblink 1s steps(2, start) infinite; }
@keyframes lcdblink { 50% { opacity: .3; } }
.clock-sub {
  font: 700 11px/1 var(--ui);
  letter-spacing: .26em; text-indent: .26em;
  color: var(--ink-dim); text-transform: uppercase;
}

/* ── The coin ────────────────────────────────────────────────────────────── */
/*
 * The hero control is a circular Bitcoin coin in the Sunrise gradient (decided 2026-08-19,
 * ROADMAP-v0.4 §11.3). The identity's buttons are sharp rectangles and every other control on
 * this page obeys that — but a coin is round for a reason a reader already understands, so the
 * roundness reads as the thing being won rather than as leftover chrome.
 *
 * The wrapper carries the heat: --heat runs 0 (at rest) to 1 (about to lapse), written from
 * tick() and computed by decode.js's heat(). It lives here rather than on the button because the
 * button owns a transform for the depress, and a filter composes with neither.
 *
 * It used to run the other way — the coin drained toward grey as the countdown fell, the brief's
 * "unfed becomes wilted" idea. Inverted 2026-08-27: that made the most exciting moment of a round
 * the dullest frame on the page. The coin now blooms instead, and the screenshot taken at T-30s is
 * the best one rather than the worst. Saturation, brightness and the gold glow do the work, all
 * climbing past their resting values; the cast shadow below them is untouched, because it grounds
 * the coin and has nothing to say about urgency.
 *
 * The pulse is scale-only and grows out of nothing: its amplitude is a function of --heat, so a
 * coin at rest does not breathe at all. It is also only attached while there is something to say
 * — .breathing is added by setHeat() the moment the heat leaves zero and removed when it returns.
 * A keyframe animation left running through eleven quiet hours is a live animation on the largest
 * element on the page, and it makes every screenshot of this element unstable.
 *
 * The DURATION IS FIXED, deliberately, and the earlier draft that ran it from 2s down to 0.6s was
 * wrong. Changing animation-duration on a running animation keeps the elapsed local time and so
 * re-derives the phase from it: after a few minutes even a 14ms change moves the animation whole
 * cycles, and setHeat() rewrites --heat about every nine seconds. The coin snapped mid-breath,
 * repeatedly. Amplitude carries the escalation instead; it is a value the keyframe reads fresh
 * rather than a property that re-times anything.
 */
.coin-wrap {
  --heat: 0;
  display: inline-block;
  margin-top: 34px;
  transition: filter .5s linear;
  filter: drop-shadow(0 0 calc(var(--coin) * .048 * (1 + var(--heat) * 3)) rgba(254, 226, 82, .34))
          drop-shadow(0 calc(var(--coin) * .022) calc(var(--coin) * .035) rgba(0, 0, 0, .7))
          saturate(calc(1 + var(--heat) * .9))
          brightness(calc(1 + var(--heat) * .25));
}
.coin-wrap.breathing { animation: breathe 1.8s ease-in-out infinite; }
@keyframes breathe { 50% { transform: scale(calc(1 + var(--heat) * .03)); } }

/* A circle has one dimension, so height follows from aspect-ratio and the old 208x86 stops being
   two numbers. Every offset below is a *fraction of the diameter* rather than a fixed pixel
   count: the rim is the coin's edge, so a coin that shrinks to fit a phone has to take its edge
   with it, or a 211px coin wears a 60px rim. The fractions are exactly the spec's numbers over
   its 624px, so the proportions are unchanged at every size. */
.press {
  display: grid; place-items: center;
  width: var(--coin);
  aspect-ratio: 1;
  padding: 0;
  border: none; border-radius: 50%;
  background: var(--sunrise);
  color: var(--ground);
  /* The rim is indirected through two variables the state rules below redefine, rather than
     restated in each state's own box-shadow. :active is 0-3-0 and .cooling is 0-2-0, so a
     cooling coin *being pressed* took the gold rim straight back — the exact "grey face lit
     from behind" this is all here to prevent. A custom property is resolved on the element,
     so :active reads whichever rim the element's state already settled on and there is no
     specificity race left to lose. Any future state sets these two and nothing else. */
  --rim-now: var(--rim);
  --rim-now-dark: var(--rim-dark);
  box-shadow:
    0 calc(var(--coin) * .0577) 0 var(--rim-now),
    0 calc(var(--coin) * .0962) 0 var(--rim-now-dark),
    0 calc(var(--coin) * .1346) calc(var(--coin) * .125) rgba(0, 0, 0, .6);
  transition: transform .06s ease-out, box-shadow .06s ease-out;
  cursor: pointer;
}
.coin-mark { width: 38%; height: 38%; display: block; }

/* The depress — the coin drops onto its own edge. This is the tactility docs/virality.md
   credits as the half that already works, and the reason the decision went to a coin rather than
   a sharp block, so it scales with the coin like everything else. */
.press:active:not(:disabled) {
  transform: translateY(calc(var(--coin) * .0721));
  box-shadow:
    0 calc(var(--coin) * .0144) 0 var(--rim-now),
    0 calc(var(--coin) * .024) 0 var(--rim-now-dark),
    0 calc(var(--coin) * .0385) calc(var(--coin) * .048) rgba(0, 0, 0, .55);
}
.press:focus-visible { outline: 3px solid var(--accent-text); outline-offset: 8px; }

/* Live but not counting. A cooling coin keeps the cursor and the depress — it is clickable on
   purpose — and loses only its colour, so "you may click, it will not count" is legible before
   the note under it is read. Desaturated Gray now, not the old muddy red. */
.press.cooling {
  background: linear-gradient(180deg, #6a6a6a 0%, #3d3d3d 60.69%);
  color: #1c1c1c;
  --rim-now: #4a4a4a;
  --rim-now-dark: #333333;
}

/* The rim above is only half of it: the wrapper's gold glow and the heat's saturate/brightness
   sit *over* the grey face, so a cooling coin still read as the live one lit from behind. The
   whole control goes grey together, and the breathing stops with it — a coin that cannot count
   has nothing to be urgent about. The black cast shadow stays: it grounds the coin rather than
   saying anything about urgency, which is the same reason the heat filter leaves it alone. */
.coin-wrap.cooling {
  filter: drop-shadow(0 calc(var(--coin) * .022) calc(var(--coin) * .035) rgba(0, 0, 0, .7));
}
.coin-wrap.cooling.breathing { animation: none; }
.press:disabled {
  cursor: not-allowed;
  background: linear-gradient(180deg, #3a3a3a 0%, #232323 60.69%);
  color: #151515;
  --rim-now: #202020;
  --rim-now-dark: #171717;
  box-shadow:
    0 calc(var(--coin) * .0577) 0 var(--rim-now),
    0 calc(var(--coin) * .0962) 0 var(--rim-now-dark),
    0 calc(var(--coin) * .1346) calc(var(--coin) * .096) rgba(0, 0, 0, .5);
}

/* The clearance is the coin's rim, not a design gap: the shadow reaches ~.096 of the diameter
   below the coin's own box and the box does not grow to hold it, so a note placed by the old
   26px margin rendered *inside* the shadow. Sixth number to move with the coin, and the only one
   not in ROADMAP-v0.4 §11.3's list of five — so it is a fraction of the diameter like the rest,
   with a floor for the smallest coin. */
.press-note {
  margin-top: max(28px, calc(var(--coin) * .16));
  font-size: 13px; min-height: 18px; text-align: center;
  color: var(--ink-dim);
}
.press-note + .press-note { margin-top: 8px; }
.press-note.warn { color: var(--accent-text); }
.press-note.err  { color: #ff6b5e; }
#tx { word-break: break-word; }
#share { margin-top: 20px; }

/* ── Stats ───────────────────────────────────────────────────────────────── */
.rows {
  display: grid; grid-template-columns: auto 1fr; gap: 12px 24px;
  align-items: baseline; margin: 0; font-size: 14px;
}
.rows dt {
  color: var(--ink-dim);
  font: 700 11px/1 var(--ui);
  letter-spacing: .14em; text-transform: uppercase;
}
.rows dd { margin: 0; display: flex; align-items: baseline; gap: 10px; }

.addr { font-family: var(--mono); font-size: 14px; }
.you {
  background: var(--accent); color: var(--white); border-radius: var(--radius);
  font: 700 10px/1 var(--mono); padding: 4px 7px; letter-spacing: .1em;
}
.tally {
  font: 700 22px/1 var(--mono);
  font-variant-numeric: tabular-nums;
}
.counts { color: var(--ink-dim); font-size: 13px; display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }

.sig { margin: 0; color: var(--ink-dim); font-size: 12px; }

/* ── Feed ────────────────────────────────────────────────────────────────── */
.feed {
  background: var(--panel);
  height: 150px; overflow-y: auto; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--radius);
  font-family: var(--mono); font-size: 12px; color: var(--ink-dim);
}
.feed div { padding: 2px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.feed .mine { color: var(--white); }

/* The badge — `docs/virality.md` §1's flair, on feed rows, both boards and your own count.
   The fill arrives inline from badge(); only the shape lives here, because card.js keeps the
   tier palette semantic and in decode.js and a second consumer must not fork it. White on the
   five fills is 6.47:1 at worst, where the same colours as text are ~2-3.7:1 on the ground.
   The hairline is card.js:158's raised to .35 — STEADY and EARLY are dark enough that the pill
   loses its edge without one, and .28 composites to only 2.53:1 on --panel. `--gray` is the
   fallback so a chip that ever loses its inline fill is still a chip, not white on white.
   `flex: none` because the board rows are flex and must not shrink it. */
.tier {
  background: var(--gray); color: var(--white); border-radius: var(--radius);
  font: 700 9px/1 var(--mono); padding: 3px 5px; letter-spacing: .08em;
  border: 1px solid rgba(255,255,255,.35); white-space: nowrap; flex: none;
}

/* CLUTCH only — the tier that means seconds left, so it is the one worth a moving edge. A sheen
   over the inline fill rather than a second palette: the gradient is white at low alpha, so the
   colour underneath stays decode.js's. 200% wide so the travel is a pass of light, not a pulse. */
.tier.clutch {
  background-image: linear-gradient(100deg,
    rgba(255,255,255,0) 35%, rgba(255,255,255,.45) 50%, rgba(255,255,255,0) 65%);
  background-size: 220% 100%;
  animation: tier-sheen 2.4s linear infinite;
}
@keyframes tier-sheen { from { background-position: 120% 0; } to { background-position: -20% 0; } }

/* ── Leaderboard ─────────────────────────────────────────────────────────── */
.tabs { display: flex; gap: 8px; margin-bottom: 14px; }
.tabs button {
  background: none; border: 1px solid var(--line); border-radius: var(--radius);
  font: 700 12px/1 var(--ui);
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-dim); padding: 10px 14px;
  cursor: pointer;
}
.tabs button[aria-selected="true"] {
  background: var(--accent); border-color: var(--accent); color: var(--white);
}
.tabs button:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }

/* Same inset panel as the feed, in rows of rank / wallet / reign. */
.board {
  background: var(--panel);
  padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--radius);
  font-family: var(--mono); font-size: 12px; color: var(--ink-dim);
}
.board div { display: flex; gap: 10px; align-items: baseline; padding: 3px 0; }
.board b { color: var(--white); min-width: 18px; }
/* :last-child, not every span: the row's value is the thing pinned right, and a `.tier` chip
   sits between the address and it. Unscoped, `auto` matched the chip too (and won, being one
   specificity point above `.tier`), which floated it into the middle of the row. */
.board span:last-child { margin-left: auto; font-variant-numeric: tabular-nums; }
.board .mine { color: var(--white); }

/* Fifty rows is a roster, not a glance, so both boards scroll — and both are fifty, so the rank
   column means the same thing on each. The reign board was five rows and needed neither. */
.board.scroll { max-height: 260px; overflow-y: auto; }

/* The line under fifth, drawn identically on both boards: the same rule the reader's own row
   below the scroller already uses, so the two read as one idea rather than two separators. It
   scrolls with the sixth row, which is where the boundary is — not pinned to the panel. */
.board div.cut { border-top: 1px solid var(--line); margin-top: 5px; padding-top: 6px; }

/* The reader's own rank, kept outside the scroller so it survives being fortieth. */
.board-you {
  display: flex; gap: 10px; align-items: baseline;
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: 12px; color: var(--white);
}
.board-you span { margin-left: auto; font-variant-numeric: tabular-nums; }

/* Both panels need a column of their own, or the sig line lands beside the board. */
#panel-presses, #panel-reign { display: flex; flex-direction: column; gap: 14px; }

/* A board row is a rank, an address and a number; a feed row is one line of log. Stretched to
   1240px the rank and the number end up a thousand pixels apart with nothing between them, so
   these keep the reading measure while the hero, the stats panel and the cards take the shell. */
#panel-presses, #panel-reign, .feed { max-width: var(--measure); }

/* ── The four link cards ─────────────────────────────────────────────────── */
/* Two-by-two below the shell: at 720px four columns would be 171px of card for two lines of copy.
   The wide block at the bottom of this file takes them four across, where there is room for it —
   that rule's premise was the 720px column this branch no longer has. Under 720px, one. */
.cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
/* The faucet's .campaign-card: a raised panel rather than a bare outline, its 24px padding
   pulled in to 20px because this column is 720px wide and its grid is 1240px. */
.card {
  display: flex; flex-direction: column; gap: 10px;
  padding: 20px;
  background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--ink); text-decoration: none;
}
/* Its .campaign-card-num — 01 / 02 / 03 in the accent above the title. */
.card-num { margin: 0; font: 700 13px/1 var(--ui); color: var(--accent-text); }
.card:hover { border-color: var(--accent); }
.card b { font: 700 16px/1.3 var(--ui); letter-spacing: .1em; text-transform: uppercase; }
.card span { font-size: 13px; color: var(--ink-dim); }
.card .card-go {
  margin-top: auto;
  font: 700 13px/1 var(--ui);
  color: var(--accent-text);
}
.card:focus-visible, .card-go a:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
/* `.is-todo` — the dashed border and dimmed link for a card with nowhere to go — is deleted, not
   kept for later. Wiring the last card to a real URL left none of them with nowhere to go, so the
   last user of it went with it (2026-09-07 — which wallet that card names has changed since and
   does not matter here; having a URL at all is what retired the selector). Three lines to write again if a dead card ever returns; a
   selector nothing matches is worse than that, because the next reader has to prove it is dead.
   `ROADMAP-v0.5.md` §6 still cites it as present — it is historical and says so. */

/* One card is promoted: a border, a soft bloom, and a slow breath on the bloom.

   The border is --accent-text, NOT --accent: --accent is exactly what .card:hover sets, so a
   featured card and any hovered card were the same border and only the bloom told them apart.
   The bloom is also the first thing forced-colors mode throws away, which would have left the
   promotion invisible there — hence the accent title as well, which is not a colour on the
   border and so survives both that and a hovered neighbour.

   The glow lives on ::after rather than on the card, for two reasons. The animation is then
   OPACITY, which the compositor handles without repainting a 20px shadow sixty times a second
   — animating box-shadow directly is the version that costs a whole card's paint per frame.
   And it leaves box-shadow free for :hover to change, which it could not do if the animation
   owned that property: a hover would have been fighting the keyframes for it.

   3.2s and a floor of .55, deliberately slow and shallow. This is the one animated component
   on a page that decided against animated components (ROADMAP-v0.4 §11.3), so it earns the
   exception by being noticeable in peripheral vision and invisible when read directly. */
.card.is-featured {
  position: relative;
  border-color: var(--accent-text);
}
.card.is-featured::after {
  content: ''; position: absolute; inset: -1px;
  border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 20px rgba(197, 52, 0, .30);
  animation: vipglow 3.2s ease-in-out infinite;
}
@keyframes vipglow { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
.card.is-featured:hover::after { box-shadow: 0 0 26px rgba(197, 52, 0, .45); }
.card.is-featured b { color: var(--accent-text); }
/* Flex earns its keep again: card 03 carries two links (2026-09-10) and this is the row that
   wraps them. The other go-lines are bare text nodes, which become a single anonymous flex item
   and wrap exactly as a block would.

   The anchors take the go-line's own colour and size rather than the UA's blue — no global `a`
   rule exists in this file, so without this they were the only blue text on the page. */
.card-go { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.card-go a { color: inherit; font: inherit; text-decoration: none; }
.card-go a:hover { text-decoration: underline; }

/* ── FAQ ─────────────────────────────────────────────────────────────────── */
/* The faucet rules the LIST at the top and every row at the bottom, which is the same set of
   lines as a rule per row plus one — and it is the arrangement that survives a row being added
   or removed at either end. */
#faq details:first-of-type { border-top: 1px solid var(--line); }
#faq details { border-bottom: 1px solid var(--line); }
#faq summary {
  padding: 18px 0;
  font: 700 16px/1.4 var(--ui);
  cursor: pointer;
  display: flex; justify-content: space-between; align-items: center; gap: 20px;
  list-style: none;
}
/* The default marker goes, and the faucet's own +/− takes its place in the accent. The
   ::-webkit- rule is still needed: Safari draws the triangle from that pseudo-element and
   `list-style: none` alone does not remove it. */
#faq summary::-webkit-details-marker { display: none; }
#faq summary::after { content: "+"; flex: none; color: var(--accent-text); font-size: 20px; line-height: 1; }
#faq details[open] summary::after { content: "−"; }
#faq summary:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
#faq details > :not(summary) { margin: 0 0 12px; max-width: var(--measure); }
#faq details > :last-child { margin-bottom: 18px; }
#faq h3 {
  font: 700 11px/1 var(--ui);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-dim);
}
#faq ol, #faq ul { padding-left: 20px; display: flex; flex-direction: column; gap: 6px; font-size: 14px; }
#faq p { font-size: 14px; }
#faq p.sig { font-size: 12px; }

/* ── Footer ──────────────────────────────────────────────────────────────── */
.foot {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 26px 0 0;
  border-top: 1px solid var(--line-soft);
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
}
.foot-links { display: flex; gap: 16px; margin-left: auto; }
.foot-links a {
  color: var(--ink-dim); text-decoration: none;
  font: 700 13px/1 var(--ui); letter-spacing: .08em; text-transform: uppercase;
}
.foot-links a:hover { color: var(--white); }
.foot .sig { flex-basis: 100%; text-align: right; }

/* ── Dialogs ─────────────────────────────────────────────────────────────── */
/* Native <dialog>, which is what retires the old .overlay scrim and its ten-level z-index
   ladder — and brings focus trapping and Escape with it. */
.dlg {
  width: min(440px, calc(100vw - 32px));
  padding: 20px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--ground); color: var(--ink);
  font: 14px/1.6 var(--ui);
}
.dlg::backdrop { background: rgba(0, 0, 0, .72); }
.dlg-head {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 16px;
}
.dlg-head b {
  flex: 1;
  font: 700 12px/1 var(--ui);
  letter-spacing: .14em; text-transform: uppercase;
}
.dlg p { margin: 0 0 10px; }
.dlg-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
/* 1200x630, so it is the width of the dialog and however tall that makes it. */
#share-img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: var(--radius);
  margin-bottom: 12px;
}
#share-text { word-break: break-word; }
#share-said:empty { display: none; }

/* ── Motion & responsive ─────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .press, .coin-wrap { transition: none; }
  /* The pulse goes; the filter stays. Brightness, saturation and glow are information about how
     much time is left, not decoration — and the whole point of them is that a still screenshot
     carries the state. The CLUTCH sheen is the same trade: the fill underneath is the tier. */
  .clock.panic.blink, .alert, .coin-wrap.breathing, .tier.clutch, .btn-cta.busy { animation: none; }
  /* The glow stays, at full strength — it is the promotion, and only the breath was motion. */
  .card.is-featured::after { animation: none; opacity: 1; }
}

@media (max-width: 720px) {
  .cards { grid-template-columns: 1fr; }
}

@media (max-width: 520px) {
  body { padding: 0 16px 48px; }
  .band { padding: 32px 0; }
  .hero { padding: 18px 0; }
  .stage { padding: 20px 4px 32px; }
  /* The faucet's own remedy for this element at this width: its .site-nav-ticker drops the
     tracking, allows a wrap and opens the line-height rather than scrolling sideways. Needed
     here too — at 12px/.22em the ticker is 313px wide and a 320px viewport scrolled. */
  .status {
    flex-basis: 100%; justify-content: flex-start; order: 3;
    white-space: normal; letter-spacing: .16em; line-height: 1.8;
  }
  .rows { grid-template-columns: 1fr; gap: 2px 0; }
  .rows dd { margin-bottom: 10px; }
  .feed { height: 120px; }
  /* The faucet's 26px side padding is sized for its 1240px page; in a 286px dialog three of
     these wrap their labels to two lines. The face, size and tracking stay — only the box
     tightens, so the buttons still read as the same control. */
  .btn { padding: 12px 16px; }
}

/* M16a's consolation line, one step down from the subline above it: the subline is what to do
   about the loss, this is only what the loss was worth. */
.alert-text .lost-boast { color: rgba(255, 255, 255, .75); font-size: 12px; }

/*
 * ── The wide layout ───────────────────────────────────────────────────────────
 *
 * The faucet's hero is a two-column grid: .hero-media beside .hero-copy at 6fr/5fr with a 56px
 * gutter, collapsing to one column at 980px with the copy first. The coin is this page's media
 * and everything that reads is its copy, so the coin takes the wide column and the clock moves
 * up beside the headline, which is where the faucet keeps its own countdown.
 *
 * Written as min-width so the DEFAULT is the single centred column this page has always been:
 * every narrow viewport keeps a layout that was already measured, and the split is additive.
 * DOM order is copy first, which is what the single column wants, so the coin is moved into the
 * first grid column with `order` — the mirror of the faucet's own .hero-copy { order: -1 }.
 *
 * THESE RULES LIVE AT THE BOTTOM OF THE FILE ON PURPOSE. Several of them override a base
 * declaration (.title and .tally set theirs through the `font` shorthand, .rows its
 * grid-template-columns, .stage its background, .coin-wrap its margin) and a media query carries
 * no extra specificity — put this block up beside .hero and six of its declarations lose
 * silently to rules further down. Measured that way once; the columns came out 149/812/230.
 */
@media (min-width: 981px) {
  .hero {
    display: grid;
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
    align-items: center;
    gap: 56px;
    padding: 32px 0 56px;
    text-align: left;
  }
  .stage { order: -1; }
  /*
   * The coin now has a COLUMN as well as a viewport to fit, and the viewport terms alone do not
   * see it: at 981px the 6fr column is 474px while min(84vw, 62vh) is 558, so the coin hung 14px
   * off the left edge of the page and was clipped there. Measured, not guessed.
   *
   * The third term is that column: the shell (or the viewport less its 28px gutters, whichever
   * is smaller), less the 56px gutter, times .5 — a shade under the 6/11 the column actually
   * gets, and the difference is the slack the drop-shadow glow needs to land on the ground
   * rather than on the cut. Keeping --coin itself the constraint is what preserves the file's
   * own rule that every rim and shadow offset is a fraction of the diameter: a `max-width` on
   * .press would shrink the circle and leave the rim sized for a coin that is no longer there.
   *
   * vh rather than dvh here, unlike the :root pair: this branch is desktop-only, where there is
   * no URL bar for the two to disagree about.
   */
  :root { --coin: clamp(200px, min(62vh, (min(var(--shell), 100vw - 56px) - 56px) * .5), 620px); }
  .hero-copy { display: flex; flex-direction: column; align-items: flex-start; }
  /* All three centring tricks have to come off together: text-align above, the auto margins on
     the lede, and the text-indent that balances a trailing letter-space. An indent that
     compensates a centred line is just a wrong first indent once the text is flush left. */
  .lede { margin-left: 0; margin-right: 0; }
  .eyebrow { justify-content: flex-start; }
  /* .clock.idle is (0,2,0) and carries its own text-indent, so a bare .clock never reaches it
     — the idle and warm-up clock is exactly the state a visitor arrives to. */
  .tagline, .clock, .clock.idle, .clock-sub { text-indent: 0; }
  /* The headline anchors its own column now instead of sitting under the header mark, so it can
     take something near the faucet's hero scale. The cap is measured against the 5fr column. */
  .title { font-size: clamp(30px, 3.2vw, 44px); }
  .clock { margin-top: 18px; }
  /* Stacked, the coin needed clearance from the clock above it. Side by side it does not. */
  .coin-wrap { margin-top: 0; }
  /* The bloom followed the numerals, and the numerals moved: it now sits behind the coin, which
     is the object it was always lighting. */
  .stage {
    background: radial-gradient(ellipse 62% 62% at 50% 50%, rgba(197, 52, 0, .30) 0%, transparent 70%);
  }

  /*
   * The three stats as the faucet's .how-grid: one bordered raised panel, three equal columns,
   * a 1px divider between them. Its chevrons are NOT taken — they say 01 -> 02 -> 03, a sequence,
   * and these three are parallel facts about one round.
   *
   * grid-auto-flow: column over two explicit rows puts each dt above its own dd, so a <dl> of
   * three pairs becomes three columns with no wrapper per pair. The divider is drawn on both the
   * dt and the dd of columns two and three; with no gap the two segments meet as one line.
   */
  .rows {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: auto auto;
    grid-auto-flow: column;
    gap: 0;
    background: var(--panel);
    border: 1px solid var(--line);
  }
  .rows dt { padding: 26px 28px 12px; }
  .rows dd { padding: 0 28px 26px; }
  .rows dt:not(:first-of-type), .rows dd:not(:first-of-type) { border-left: 1px solid var(--line); }
  /* Large numerals for a stat is the identity's own rule (slide 14), and there is room for it. */
  .tally { font-size: 34px; }
}

/*
 * Four cards across, one row. #123 made the band 2x2 and named its own premise — "the column is
 * 720px, so four columns would be 171px of card for two lines of copy" — and that premise is the
 * thing this branch changed. In the 1240px shell four columns are 295px each; the faucet runs its
 * own campaign grid as one row of three at 400px, and this is one row of four at 295.
 *
 * 1100px, NOT the 981 the hero splits at, and the difference is measured rather than tidy:
 * "Bridge to Starknet" is the longest of the four titles and at 16px/.1em it wraps to two lines
 * in a card narrower than 246px, which is what four columns give at 1100. At 981 they give 216
 * and it wraps. So the band keeps #123's 2x2 through the range where four would not fit, and
 * below 720 its one column. Re-measure if a longer title joins the band.
 */
@media (min-width: 1100px) {
  .cards { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ── Terms (web/terms.html) ──────────────────────────────────────────────── */
/*
 * The one page on this site that is a document rather than an interface. Everything it needs
 * that the app does not: prose headings below .sec-title, and a reading measure — 1240px of
 * indemnity clause is unreadable, so the band caps at --measure the way the FAQ answers do.
 *
 * Scoped under .legal rather than added to a bare h2/h3/ul, because index.html has none of
 * those and an unscoped rule would be three selectors nothing matches on the page that matters.
 */
.legal { border-top: none; padding-top: 8px; }
.legal > * { max-width: var(--measure); }
.legal h2 {
  margin: 36px 0 12px;
  font: 700 15px/1.3 var(--ui);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--white);
}
.legal h3 {
  margin: 24px 0 8px;
  font: 700 14px/1.3 var(--ui);
  color: var(--ink);
}
.legal p { margin: 0 0 14px; font-size: 14px; }
.legal p.sig { margin-bottom: 28px; }
.legal ul {
  margin: 0 0 14px; padding-left: 20px;
  display: flex; flex-direction: column; gap: 8px;
  font-size: 14px;
}
.legal a { color: var(--accent-text); }
/* The links out to it — under the header CTA, the two FAQ answers sourced from it, and the
   band at the bottom. Same role as
   .legal a, and the only bare <a>s in the app's own body copy. Left as ONE rule naming both:
   an unscoped `.sig a` would also catch the footer's own fine print. */
#terms a, .head-sig a, #faq a { color: var(--accent-text); }
/* The consent line under the header CTA. Exactly `.foot .sig`'s trick — a flex child with
   flex-basis: 100% takes a row of its own — so the button keeps the position it has always had
   at every width, and the line lands under it against the same right edge. */
.head-sig { flex-basis: 100%; text-align: right; margin-top: -8px; }
