/* Aptastat, the public page.
 *
 * NO HEX VALUES HERE, the same rule static/app.css keeps and for the same
 * reason: theme.py's contrast ratios are measured and frozen, and a second
 * hand-typed palette drifts from them without saying so. Every colour below
 * is a custom property out of site-tokens.css, which aptastat/web/landing.py
 * generates from aptastat/ui/theme.py.
 *
 * WHAT THE TOKENS MEAN HERE, because none of them was named for a web page:
 *
 *   --accent_fg    THE BRAND, AS FAR AS THE BACKGROUND WILL CARRY IT. In the
 *                  light theme this is literally PRINT.brand, #002850, at
 *                  13.79:1 - theme.py says so in as many words. In the dark
 *                  theme the navy is 1.28:1, i.e. invisible, and the token
 *                  becomes #62D4DA. So headings addressed to --accent_fg are
 *                  the brand navy on paper and readable on a dark handset,
 *                  which is exactly what the token was built to do.
 *   --accent       the ONE primary action. Navy in light, the walked-down
 *                  teal in dark; both carry white at over 4.9:1.
 *   --print_brand_accent
 *                  the kit's teal, #0898A0, unthemed. GRAPHICS AND LARGE TYPE
 *                  ONLY. theme.py: "the teal is the mark's waveform and is a
 *                  series colour, never type" - it is 3.26:1 on white, which
 *                  clears the 3:1 bar for a non-text graphic and for large
 *                  text and fails it for an 11 px label. The eyebrow is
 *                  therefore navy type after a teal rule, not teal type.
 *   --text_muted   the quiet voice: citations, captions, provenance. Not
 *                  PRINT.quiet, which is 5.9:1 on PAPER and about 3:1 on a
 *                  #0E1116 screen. This is the themed equivalent.
 *
 * MOTION: NONE. No transition, no animation, no smooth scroll. A page that
 * argues its instrument does not move more than it has to should not.
 */

/* --- page-level aliases -------------------------------------------------- */
/* Named for what they do on THIS page, pointing at the tokens above, so a
   section below reads as layout rather than as a second palette lookup. */
:root {
  --ink:       var(--accent_fg);
  --body:      var(--text_primary);
  --quiet:     var(--text_muted);
  --ground:    var(--bg);
  --card:      var(--surface);
  --hairline:  var(--border);
  --hair-soft: var(--border_subtle);
  --signal:    var(--print_brand_accent);

  /* THE SAME TWO STACKS static/app.css asks for. The app does not ship
     either face - tests/test_measure_column records that Windows substitutes
     silently - and this page deliberately does not make that worse by
     fetching them from somebody else's CDN. See the notes: they want
     bundling, once, for both surfaces at the same time. */
  --sans: "IBM Plex Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --gutter: 16px;
  --measure: 66ch;
  --radius: 14px;
}

* { box-sizing: border-box; }

html {
  /* No smooth scroll: the nav's four anchors jump, deliberately. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--body);
  font-family: var(--sans);
  font-size: 16px;               /* never below 16: iOS zooms the page otherwise */
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* THE ONE LINE THAT KEEPS A PHONE HONEST. Any child that overflows - a
     long citation path, a wide figure - scrolls inside its own box rather
     than shifting the whole page sideways. */
  overflow-x: hidden;
}

img, svg { max-width: 100%; }

/* --- rhythm -------------------------------------------------------------- */
.wrap {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.prose { max-width: var(--measure); }
.prose p { margin: 0 0 16px; }

section { padding: 44px 0; }
section + section { border-top: 1px solid var(--hair-soft); }
/* The nav is sticky and 64 px tall, so an anchor jump lands the heading
   UNDERNEATH it - the reader arrives at a section whose title is hidden by
   the thing that took them there. Four of the five anchors on this page are
   in that nav, so this is not an edge case. */
:target, [id] { scroll-margin-top: 84px; }

h1, h2, h3 {
  color: var(--ink);
  font-weight: 600;
  line-height: 1.15;
  margin: 0 0 14px;
  text-wrap: balance;
}
h1 { font-size: clamp(32px, 6.1vw, 52px); line-height: 1.05; letter-spacing: -0.015em; }
h2 { font-size: clamp(25px, 3.6vw, 34px); letter-spacing: -0.01em; }
h3 { font-size: clamp(19px, 2.2vw, 22px); }

.lede { font-size: clamp(17px, 2vw, 20px); color: var(--quiet); max-width: 60ch; }

.eyebrow {
  /* NAVY TYPE AFTER A TEAL RULE, not teal type. See the header comment. */
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 18px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
}
.eyebrow::before {
  content: ""; flex: none;
  width: 26px; height: 2px;
  background: var(--signal);
}

/* --- citations ----------------------------------------------------------- */
/* Under the claim, behind a hairline, and NEVER a link: a citation here
   points at a file in the repository, and a page that turned that into an
   anchor would be promising a reader something it cannot serve. */
.cite {
  margin: 12px 0 0;
  padding-top: 8px;
  border-top: 1px solid var(--hair-soft);
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.55;
  color: var(--quiet);
  /* A path is one long unbreakable token on a 360 px screen. */
  overflow-wrap: anywhere;
}

/* --- cards --------------------------------------------------------------- */
.card {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 20px;
}
.card h3 { margin-top: 0; }
.card > p:last-of-type { margin-bottom: 0; }
.card > .fig + .fig { margin-top: 12px; }

.grid { display: grid; gap: 16px; }

/* --- the mark and the wordmark ------------------------------------------- */
.lockup { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; }
.lockup .mark { width: 22px; height: 22px; flex: none; color: var(--ink); }
.lockup .name {
  font-size: 19px; font-weight: 600; letter-spacing: 0.2px; color: var(--ink);
}

/* --- nav ----------------------------------------------------------------- */
.nav {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--card) 92%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--hair-soft);
}
.nav .wrap {
  height: 64px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.nav-links { display: none; gap: 22px; }
.nav-links a {
  color: var(--quiet); text-decoration: none; font-size: 15px; font-weight: 500;
}
.nav-links a:hover, .nav-links a:focus-visible { color: var(--ink); }
@media (min-width: 860px) { .nav-links { display: flex; } }

/* --- actions ------------------------------------------------------------- */
/* ONE PRIMARY ACTION ON THE PAGE, and it is sign-in. Everything else that
   looks like a button is .btn, which spends no accent at all. */
.cta, .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 46px; padding: 0 18px;
  border-radius: 12px;
  font-family: inherit; font-size: 15px; font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.cta {
  background: var(--accent);
  color: var(--on_accent);
  border: 1px solid var(--accent_border);
}
.btn {
  background: transparent;
  color: var(--body);
  border: 1px solid var(--hairline);
}
.cta.small, .btn.small { min-height: 38px; padding: 0 13px; font-size: 14px; }
a:focus-visible, .cta:focus-visible, .btn:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
.actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }

/* Screen-reader-only, for the skip link and for the labels that make a
   figure legible to something that cannot see it. */
.sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); border: 0;
}
.skip {
  position: absolute; left: -9999px; top: 8px;
  background: var(--card); color: var(--ink);
  border: 1px solid var(--hairline); border-radius: 10px;
  padding: 10px 14px; z-index: 40;
}
.skip:focus { left: 16px; }

/* --- hero ---------------------------------------------------------------- */
.hero { padding-top: 40px; }
.hero-grid { display: grid; gap: 30px; align-items: start; }
@media (min-width: 900px) {
  .hero-grid { grid-template-columns: 1.1fr 0.9fr; gap: 44px; }
}

/* Three pills that say where the project actually is. The colours are the
   instrument's own status tokens, used for the same meanings they carry
   there - a validated thing, a thing not yet shown, and a thing that is
   neither because nobody has ruled on it. */
.status-strip {
  display: flex; flex-wrap: wrap; gap: 10px;
  margin: 26px 0 0; padding: 0; list-style: none;
}
.status-strip li {
  display: flex; align-items: baseline; gap: 8px;
  padding: 8px 13px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 13px; line-height: 1.4;
}
.status-strip b { font-weight: 600; }
.status-strip .glyph { font-size: 11px; }
.st-good { background: var(--success_bg); color: var(--success_fg); border-color: var(--success_border); }
.st-open { background: var(--warning_bg); color: var(--warning_fg); border-color: var(--warning_border); }
.st-none { background: var(--neutral_bg); color: var(--neutral_fg); border-color: var(--neutral_border); }

/* --- figures ------------------------------------------------------------- */
figure { margin: 0; }
figcaption { margin-top: 10px; font-size: 13px; color: var(--quiet); }

/* The drawing idiom is app.js's own Nyquist: an L-frame rather than a box,
   1.6 px data, 2 px point markers, and the real axis NOT starting at zero -
   R_s is the left intercept and it can be orders of magnitude larger than
   the arc. Every stroke takes a token, so the figure re-inks itself with the
   page. */
.fig { display: block; width: 100%; height: auto; }
.fig .frame  { fill: none; stroke: var(--hairline); stroke-width: 1; }
.fig .ink    { fill: none; stroke: var(--ink); stroke-width: 1.4;
               stroke-linecap: round; stroke-linejoin: round; }
.fig .thin   { fill: none; stroke: var(--quiet); stroke-width: 1;
               stroke-linecap: round; }
.fig .base   { fill: none; stroke: var(--quiet); stroke-width: 1.6; }
.fig .bound  { fill: none; stroke: var(--signal); stroke-width: 1.6; }
.fig .dot-base  { fill: var(--quiet); }
.fig .dot-bound { fill: var(--signal); }
.fig .solid  { fill: var(--ink); stroke: none; }
.fig .target { fill: var(--signal); stroke: none; }
.fig text {
  font-family: var(--mono); font-size: 10px; fill: var(--quiet);
  /* A tick label is a number; it must not be re-inked by a hover state or
     picked up as selectable furniture. */
  user-select: none;
}
.fig .lab { fill: var(--ink); font-family: var(--sans); font-size: 11px; font-weight: 600; }
.fig .lab-sig { fill: var(--signal); font-family: var(--sans); font-size: 11px; font-weight: 600; }

.diagram { display: grid; gap: 18px; margin: 26px 0 0; }
@media (min-width: 900px) { .diagram { grid-template-columns: repeat(3, 1fr); } }
.diagram .panel {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 14px;
}
.diagram .panel h4 {
  margin: 0 0 8px;
  font-family: var(--mono); font-size: 11px; font-weight: 400;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--quiet);
}

/* --- the four steps ------------------------------------------------------ */
.steps { display: grid; gap: 18px; margin: 28px 0 0; padding: 0; list-style: none; }
@media (min-width: 760px) { .steps { grid-template-columns: 1fr 1fr; gap: 24px 32px; } }
.steps li { max-width: var(--measure); }
.steps .n {
  display: block; margin-bottom: 6px;
  font-family: var(--mono); font-size: 24px; font-weight: 400;
  /* LARGE TEXT, which is where the teal is allowed: 3.26:1 clears the
     large-text bar and would fail the small-text one. */
  color: var(--signal);
}
.steps h3 { font-size: 18px; margin-bottom: 6px; }
.steps p { margin: 0; }

.note {
  margin-top: 28px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 18px 20px;
  color: var(--quiet);
  max-width: var(--measure);
}
.note h3 { font-size: 16px; color: var(--quiet); margin-bottom: 8px; }
.note p { margin: 0; }

/* --- the spec table ------------------------------------------------------ */
.spec { margin: 26px 0 0; display: grid; }
.spec > div {
  display: grid; gap: 4px 28px;
  padding: 18px 0;
  border-top: 1px solid var(--hair-soft);
}
.spec > div:last-child { border-bottom: 1px solid var(--hair-soft); }
@media (min-width: 760px) { .spec > div { grid-template-columns: 210px minmax(0, 1fr); } }
.spec dt { color: var(--ink); font-weight: 600; }
.spec dd { margin: 0; max-width: var(--measure); }
/* The citation is a <span> inside the <dd> so the markup stays a definition
   list; it still has to sit on its own line behind its own hairline. */
.spec .cite { display: block; }
.spec .num {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

/* --- the one dark band --------------------------------------------------- */
/* It carries data-theme="dark", so every token inside it is the dark
   palette - which is why there is not a single literal in this rule. The
   app's own screens are dark; a band that shows them should be too. */
.band {
  background: var(--bg);
  color: var(--text_primary);
  /* --border resolves inside the band's OWN dark scope, so this is a dark
     hairline either way: on a light page it is the lip of the band and all
     but invisible against it, and on a dark page - where the band and the
     section above it are the same #0E1116 - it is the only thing that still
     says a band starts here. */
  border-top: 1px solid var(--border);
}
.band h2, .band h3 { color: var(--accent_fg); }
.band .cite { border-top-color: var(--border); color: var(--text_muted); }
.band-grid { display: grid; gap: 26px; margin-top: 26px; }
@media (min-width: 900px) { .band-grid { grid-template-columns: 1fr 1fr; gap: 40px; } }
.band-points { display: grid; gap: 26px; margin: 26px 0 0; padding: 0; list-style: none; }
@media (min-width: 900px) { .band-points { grid-template-columns: repeat(3, 1fr); gap: 32px; } }
.band-points li { max-width: var(--measure); }
.band-points p { margin: 0; }
.band-points b { color: var(--accent_fg); }

/* --- the pull-quotes ----------------------------------------------------- */
/* Two of them, and they are the only decorated blocks on the page: a rule at
   the left for section 2's, a short teal rule above section 4's. Anything
   more and the accent stops meaning "this one". */
.pull {
  margin: 30px 0 0;
  padding-left: 20px;
  border-left: 3px solid var(--ink);
  font-size: clamp(19px, 2.4vw, 22px);
  line-height: 1.35;
  color: var(--ink);
  max-width: 44ch;
}
.oath { max-width: var(--measure); }
.oath::before {
  content: ""; display: block;
  width: 40px; height: 3px; margin-bottom: 20px;
  background: var(--signal);
}
.oath .aside {
  margin: 20px 0 0;
  padding-left: 20px;
  border-left: 3px solid var(--signal);
  font-style: italic;
  color: var(--quiet);
}
.oath .cite { max-width: var(--measure); }

/* --- evidence ------------------------------------------------------------ */
.tiles { display: grid; gap: 16px; margin: 26px 0 0; }
@media (min-width: 760px) { .tiles { grid-template-columns: repeat(3, 1fr); } }
.tile {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 20px;
}
.tile .figure {
  display: block;
  font-family: var(--mono); font-size: 44px; font-weight: 400;
  line-height: 1.05; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  /* 44 px of monospace does not fit 360 px minus two gutters for the longest
     of these, which is a WORD rather than a number. */
  overflow-wrap: anywhere;
}
.tile .unit {
  display: block; margin-top: 8px;
  font-variant-caps: all-small-caps; letter-spacing: 0.06em;
  font-weight: 600; color: var(--body);
}
.tile .prov { margin: 6px 0 0; font-size: 13px; color: var(--quiet); }
/* The second half of section 5 is a story, not a tile, and it needs air
   between it and the three figures it explains. */
.spaced { margin-top: 40px; }

/* --- limits -------------------------------------------------------------- */
.limits { display: grid; gap: 16px; margin: 26px 0 0; }
@media (min-width: 860px) { .limits { grid-template-columns: 1fr 1fr; } }
.limits .card.wide { grid-column: 1 / -1; }
.limits h3 { font-size: 18px; }

.disclaimer {
  margin: 30px 0 0;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  padding: 20px;
}
.disclaimer h3 { font-size: 15px; margin-bottom: 8px; }
.disclaimer p { margin: 0; max-width: var(--measure); }

/* --- people -------------------------------------------------------------- */
.people { display: grid; gap: 16px; margin: 24px 0 30px; }
@media (min-width: 760px) { .people { grid-template-columns: 1fr 1fr; } }
.people .role {
  display: block; margin-bottom: 6px;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--quiet);
}
.people .who { font-weight: 600; color: var(--ink); }

.audiences { display: grid; gap: 20px; margin: 24px 0 0; padding: 0; list-style: none; }
.audiences li { max-width: var(--measure); }
.audiences b { color: var(--ink); }

/* --- footer -------------------------------------------------------------- */
/* Navy ground from the kit itself (PRINT.brand), and data-theme="dark" on
   the element so the mark's helix reaches for the dark theme's --accent_fg -
   #62D4DA - rather than the navy it would take on a light page, which would
   be invisible on navy. The white is --on_accent, which is #FFFFFF in BOTH
   themes; --text_inverse is not, and would have gone black here. */
.foot {
  background: var(--print_brand);
  color: var(--on_accent);
  padding: 40px 0 28px;
}
.foot a { color: var(--on_accent); }
.foot .lockup .name, .foot .lockup .mark { color: var(--on_accent); }
.foot-top { display: grid; gap: 22px; }
@media (min-width: 760px) {
  .foot-top { grid-template-columns: 1fr auto; align-items: start; }
  .foot-right { text-align: right; }
}
.foot .quiet { color: color-mix(in srgb, var(--on_accent) 70%, transparent); }
.foot .quiet p { margin: 4px 0 0; }
.foot-rule {
  margin-top: 30px; padding-top: 18px;
  border-top: 1px solid color-mix(in srgb, var(--on_accent) 22%, transparent);
  font-size: 12px; line-height: 1.6;
  color: color-mix(in srgb, var(--on_accent) 55%, transparent);
}
.foot-rule p { margin: 0 0 8px; max-width: var(--measure); }
.foot-rule p:last-child { margin-bottom: 0; }

/* THE TWO-TONE LOCKUP, and it is two SVGs rather than one recoloured.
   icons.mark_svg emits every path with currentColor so the status ring can
   re-ink the whole mark by condition; that is the right call there and it
   means a two-tone version cannot be a one-line override. So the letter and
   the helix are stacked, each taking its own colour, from the same generated
   geometry - landing.mark_svg_document derives the split rather than
   re-drawing it, and a test pins both halves to icons.mark_svg. */
.lockup .duo { position: relative; flex: none; width: 26px; height: 26px; }
.lockup .duo svg { position: absolute; inset: 0; width: 26px; height: 26px; }
.lockup .duo .mark-letter { color: var(--on_accent); }
.lockup .duo .mark-helix { color: var(--accent_fg); }

/* --- 404 ----------------------------------------------------------------- */
.gone { min-height: 60svh; display: grid; align-content: center; gap: 8px; }
