/* ─────────────────────────────────────────────────────────────
   99cols.com — the site's own layer, on top of the design system.
   Loaded AFTER /ds/styles.css, and it defines no colour, radius or
   type size of its own: every value here is a token (CLAUDE.md —
   the DS token CSS is the source of truth). Where a token needs an
   alpha the site uses color-mix on the token rather than a new hex,
   so a brand change still reaches this page.
   ───────────────────────────────────────────────────────────── */

/* ── The display and data voices, on hold ───────────────────────
   Geograph and The Future Mono ship as 67- and 70-glyph subsets:
   `,-.0-9A-Za-z` and nothing else. No é è ê à ù û ç, no apostrophe,
   no % / · — « ». In French that does not degrade, it BREAKS: the
   browser falls back per missing glyph, so "L'écusson" renders in
   three fonts and "Où" splits down the middle. Measured on the
   files themselves (cmap) and confirmed in the browser.

   So the site runs on Satoshi — 431 glyphs, everything it needs —
   which is the fallback the DS itself declares next in line. This
   is the whole workaround: delete this block the day the complete
   families land, and `--font-display` / `--font-data` take over
   again with no other change. The @font-face rules stay loaded and
   simply go unused, so nothing is fetched in the meantime.
   ───────────────────────────────────────────────────────────── */
:root {
  --font-display: var(--sans);
  --font-data: var(--sans);
}

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  /* No `fixed` attachment: a fixed body background forces the compositor's
     full-repaint path, and this pane's renderer visibly drops the hero when
     scrolled. The wash reads the same scrolling with the page. */
  background: var(--grad-hero) var(--bg-page);
  color: var(--text-body);
  font-family: var(--font-ui);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  font-weight: var(--w-regular);
  -webkit-font-smoothing: antialiased;
}

/* The signature motif: contour lines, always below the noise threshold. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: repeating-radial-gradient(
    circle at 82% -6%,
    transparent 0 38px,
    color-mix(in srgb, var(--ink) 4%, transparent) 38px 39px
  );
}

h1,
h2,
h3 {
  margin: 0;
  color: var(--text-strong);
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  letter-spacing: var(--tr-title);
  text-wrap: balance;
}

h1 {
  font-size: clamp(var(--t-h1), 7vw, var(--t-display));
  line-height: var(--lh-display);
  letter-spacing: var(--tr-display);
}

h2 {
  font-size: var(--t-h2);
  line-height: var(--lh-tight);
}

h3 {
  font-size: var(--t-h3);
  line-height: var(--lh-snug);
}

p {
  margin: 0;
  max-width: var(--measure);
}

a {
  color: var(--fn-tint);
  text-underline-offset: 3px;
}

a:hover {
  color: var(--structure-700);
}

/* ── Layout ─────────────────────────────────────────────────── */

/* `padding-inline`, never the `padding` shorthand: a section IS a shell, and a
   shorthand here would reset the block padding that gives the page its rhythm
   — silently, because `.shell` outranks `main > section`. */
.shell {
  max-width: var(--content-max);
  margin: 0 auto;
  padding-inline: var(--gutter);
}

.masthead,
main > section,
.colophon {
  padding-block: var(--sp12);
}

.masthead {
  padding-block: var(--sp8) 0;
}

.masthead-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-row);
}

.quiet-link {
  color: var(--text-muted);
  font-size: var(--t-caption);
  text-decoration: none;
}

.quiet-link:hover {
  color: var(--text-body);
  text-decoration: underline;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-stack);
  align-items: flex-start;
}

.stack--loose {
  gap: var(--sp6);
}

.overline {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--t-overline);
  font-weight: var(--w-medium);
  letter-spacing: var(--tr-overline);
  text-transform: uppercase;
}

.wordmark {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp2);
  color: var(--text-strong);
  font-family: var(--font-display);
  font-size: var(--t-title);
  font-weight: var(--w-medium);
  letter-spacing: var(--tr-title);
  text-decoration: none;
}

.wordmark span {
  color: var(--text-muted);
  font-family: var(--font-ui);
  font-size: var(--t-overline);
  font-weight: var(--w-medium);
  letter-spacing: var(--tr-overline);
  text-transform: uppercase;
}

.lede {
  font-size: var(--t-h3);
  line-height: var(--lh-snug);
  color: var(--text-body);
}

.muted {
  color: var(--text-muted);
  font-size: var(--t-body-sm);
}

/* A section headline that reads as a claim, not a label. Sized between h1
   and the display cut so the hero keeps the top of the scale to itself. */
.statement {
  font-size: clamp(var(--t-h2), 4.5vw, var(--t-h1));
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-title);
}

/* ── Hero: the words and the object, side by side ───────────── */

.hero {
  display: grid;
  gap: var(--sp8);
  align-items: center;
}

@media (min-width: 880px) {
  .hero {
    /* A fixed track, not `auto`: the album's only intrinsic width is its
       cells', and letting the track ask them collapses the card. */
    grid-template-columns: minmax(0, 1fr) 440px;
    gap: var(--sp12);
  }
}

/* ── The night band: the one dark moment on the page ────────── */

.band {
  background: var(--grad-night);
  color: var(--night-ink-2);
}

.band .shell {
  padding-block: var(--sp16);
}

.band .overline {
  color: var(--night-ink-3);
}

.band .statement {
  color: var(--night-ink);
}

/* ── The album ──────────────────────────────────────────────── */

/* The album is the only object on the page, so it is the only thing that
   sits on a raised surface — the card recipe from the DS, nothing else. */
.album {
  width: 100%;
  max-width: 440px;
  padding: var(--space-inset-card);
  background: var(--surface-card);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-peek);
}

.album-cells {
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  gap: var(--sp1);
}

.cell {
  aspect-ratio: 1;
  border-radius: var(--r-dot);
  /* An unearned well: the accent at 10%, the same recipe as HomeScreen —
     NOT --accent-soft, which is 22 units lighter and washes the grid out. */
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.cell--void {
  background: transparent;
}

.cell--d4 {
  background: var(--d4);
}
.cell--d3 {
  background: var(--d3);
}
.cell--d2 {
  background: var(--d2);
}
.cell--d1 {
  background: var(--d1);
}
/* Every hors catégorie glows, on top of its own colour — not instead of it. */
.cell--dhc {
  background: var(--dhc);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--warm) 45%, transparent);
}

.album-readout {
  display: flex;
  align-items: baseline;
  gap: var(--sp2);
  margin-top: var(--sp4);
  font-family: var(--font-data);
  font-size: var(--t-data);
  color: var(--text-muted);
}

.album-readout b {
  color: var(--text-strong);
  font-size: var(--t-data-hero);
  font-weight: var(--w-medium);
  line-height: 1;
}

/* The mono is for figures only. Prose beside them goes back to the UI voice. */
.album-readout em {
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  font-style: normal;
}

/* ── Cards ──────────────────────────────────────────────────── */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--sp4);
  margin: 0;
  padding: 0;
  list-style: none;
  /* .stack aligns flex-start, which would let this grid shrink to content
     and wrap early; the row of cards is the one child meant to fill it. */
  width: 100%;
}

.card {
  display: flex;
  flex-direction: column;
  gap: var(--sp2);
  padding: var(--space-inset-card);
  background: var(--surface-card);
  border-radius: var(--r-card);
  box-shadow: var(--elevation-card);
}

.card p {
  font-size: var(--t-body-sm);
}

.card .overline {
  font-family: var(--font-data);
  color: var(--fn-tint);
}

/* A note card: the wash tinted, with a 3px accent edge. */
.note {
  padding: var(--space-inset-card);
  border-radius: var(--r-card);
  border-left: 3px solid var(--human-tint);
  background: var(--human-tint-soft);
  color: var(--text-strong);
}

/* ── Actions ────────────────────────────────────────────────── */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: 0 var(--sp6);
  border: 0;
  border-radius: var(--r-control);
  background: var(--fn-tint);
  color: var(--text-on-accent);
  font-family: var(--font-ui);
  font-size: var(--t-body);
  font-weight: var(--w-medium);
  text-decoration: none;
  transition:
    transform var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease);
}

.button:hover {
  background: var(--structure-700);
  color: var(--text-on-accent);
}

.button:active {
  transform: scale(var(--press-scale));
}

.button--warm {
  background: var(--human-tint);
}

.button--warm:hover {
  background: var(--warm-ink);
}

/* ── Colophon ───────────────────────────────────────────────── */

.colophon {
  border-top: var(--border);
  color: var(--text-muted);
  font-size: var(--t-caption);
}

.colophon ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-row) var(--sp6);
  margin: 0 0 var(--sp3);
  padding: 0;
  list-style: none;
}

/* ── Prose (the privacy policy) ─────────────────────────────── */

.prose > .overline {
  margin-bottom: var(--sp2);
}

.prose h1 {
  margin-bottom: var(--sp4);
}

.prose .lede,
.prose .note {
  margin-bottom: var(--sp6);
}

.prose h2 {
  margin-top: var(--sp12);
  margin-bottom: var(--sp3);
}

.prose h3 {
  margin-top: var(--sp6);
  margin-bottom: var(--sp2);
}

.prose p,
.prose li {
  font-size: var(--t-body-sm);
  max-width: var(--measure);
}

.prose p + p {
  margin-top: var(--sp3);
}

.prose ul {
  padding-left: var(--sp4);
}

.prose li + li {
  margin-top: var(--sp2);
}

/* Deliberately loud: an unfilled blank must not survive to production. */
.todo {
  padding: 0 var(--sp1);
  border-radius: var(--r-dot);
  background: var(--human-tint-soft);
  color: var(--warm-ink);
  font-family: var(--font-data);
  font-size: var(--t-data-sm);
  text-transform: uppercase;
  letter-spacing: var(--tr-overline);
}

/* ── The Strava bounce ──────────────────────────────────────── */

.centred {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp6);
  min-height: 100svh;
  padding: var(--sp8) var(--gutter);
  text-align: center;
}

.centred p {
  max-width: 46ch;
}

.centred .stack {
  align-items: center;
}

/* Each state is revealed by the script; nothing shows before it has read the
   query string, so a code never flashes behind the wrong sentence. */
.state {
  display: none;
  flex-direction: column;
  align-items: center;
  gap: var(--sp4);
}

body[data-status="ok"] .state--ok,
body[data-status="refused"] .state--refused,
body[data-status="idle"] .state--idle {
  display: flex;
}

body[data-status] .state--nojs {
  display: none;
}

.state--nojs {
  display: flex;
}

/* Reduced motion needs nothing here: the DS collapses --dur-* to 1ms under the
   media query, and every transition above is expressed in those tokens. */
