/* ---------------------------------------------------------------- faces
 *
 * Served from this site, not from a font CDN: no third-party requests at all.
 * A site arguing that your files stay yours should not hand its readers to
 * someone else on the way in.
 *
 * They were NAMED here before they were shipped, so every visitor got Georgia
 * while the design was checked on a machine that happened to have them. A font
 * stack is a claim about what the reader has, and this one was wrong.
 *
 * OFL 1.1. See fonts/README.md and fonts/licenses/.
 */
@font-face {
  font-family: 'Fraunces';
  src: url('fonts/fraunces-latin.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Fraunces';
  src: url('fonts/fraunces-italic-latin.woff2') format('woff2');
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Newsreader';
  src: url('fonts/newsreader-latin.woff2') format('woff2');
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Newsreader';
  src: url('fonts/newsreader-italic-latin.woff2') format('woff2');
  font-weight: 200 800;
  font-style: italic;
  font-display: swap;
}

/* Writer — documentation
 *
 * Aesthetic: archival print. A scholarly edition, not a product site. Warm
 * paper, ink-brown rather than black, a single seal-red accent used sparingly
 * enough that it still means something. Marginalia in the gutter, rules as
 * structure, hashes set as artifacts.
 *
 * Fraunces (display) carries the character; Newsreader (body) does the reading;
 * IBM Plex Mono holds anything that must be transcribed exactly.
 */

/* There was a Google Fonts @import here, left over from before the faces were
 * self-hosted. It never fired: an @import is only honoured at the top of a
 * stylesheet, and it sat below the @font-face blocks, so every browser threw it
 * away — which is the only reason the no-third-party test has been passing.
 * Inert by accident is not the same as absent, and anyone tidying this file
 * into conventional order would have quietly reconnected the site to Google. */

:root {
  --paper:      #f6f2e9;
  --paper-deep: #efe9dc;
  --ink:        #1e1913;
  --ink-soft:   #5c5245;
  --ink-faint:  #766d5f;   /* 4.56:1 — was #8d8271 at 3.38:1, failed AA */
  --rule:       #d8cfbc;   /* decorative hairlines only */
  --edge:       #938b7d;   /* functional borders, 3.02:1 */
  --seal:       #9c3b2e;
  /* THE MARK, not text. Kept apart from --seal on purpose.
   *
   * --seal is an accent that gets SET IN TYPE — labels, rules, the current nav
   * item — so it carries a contrast obligation and must clear 4.5:1 against the
   * paper of whichever theme is showing. --brand is the logotype: the band in
   * the icon, the dot after the wordmark. A logotype is exempt from contrast
   * minimums (WCAG 1.4.3), so it is free to be the colour the mark wants.
   *
   * Conflated, they fight: tune the brand and label text silently loses
   * contrast; tune the text to pass and the mark drifts off-brand. They start
   * at the same value, which is not the same as being the same token. */
  --brand:      #9c3b2e;
  /* Deep blue, not green. Under deuteranopia (~5% of men) the old #4a6b45
     rendered #686247 while --seal rendered #6d632b — the same olive, on the
     verification screen, where telling pass from fail is the entire point.
     Blue rides the blue-yellow axis, which red-green deficiency leaves intact:
     separation 0.241 vs 0.042, and 6.26:1 on paper. */
  --verified:   #2c5c8a;
  --pending:    #93661d;   /* 4.52:1 — was 4.18:1 */

  --display: 'Fraunces', Georgia, serif;
  --body:    'Newsreader', Georgia, serif;
  --mono:    'IBM Plex Mono', ui-monospace, Menlo, monospace;

  --measure: 34rem;
  --gutter:  13rem;
}

html[data-theme="dark"] {
  --paper: #16130f; --paper-deep: #1c1813;
  --ink-base: #ece5d8; --ink: var(--ink-base);
  --ink-soft: #a89c88; --ink-faint: #877c69;
  --rule: #332d24; --edge: #686258;
  --seal: #d4705d; --brand: #d4705d; --verified: #7fb0dd; --pending: #d9a441;
}
html[data-theme="light"] { color-scheme: light; }

@media (prefers-color-scheme: dark) {
  :root {
    --paper:      #16130f;
    --paper-deep: #1c1813;
    --ink:        #ece5d8;
    --ink-soft:   #a89c88;
    --ink-faint:  #877c69;   /* 4.51:1 */
    --rule:       #332d24;
    --edge:       #686258;   /* 3.07:1 */
    --seal:       #d4705d;
    --brand:      #d4705d;
    --verified:   #7fb0dd;   /* dark-mode counterpart, same reasoning */
    --pending:    #d9a441;
  }
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-optical-sizing: auto;
  font-size: 1.06rem;
  line-height: 1.68;
  /* Paper grain. Subtle enough to read as texture rather than noise. */
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='.035'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------- masthead */

/* Aligned to the column beneath it.
 *
 * Full-bleed, the nav's right edge landed wherever the window happened to end —
 * on a wide monitor, four hundred pixels past the content, with the rule under
 * it running out into empty space. The masthead is now the same width as the
 * page it heads, so the wordmark sits over the first word and the last nav item
 * sits over the right edge of the text. The landing page is wider than a
 * document page, so it says so. */
.masthead {
  border-bottom: 1px solid var(--rule);
  padding: 1.6rem clamp(1.2rem, 5vw, 2rem) 1.1rem;
  max-width: calc(var(--gutter) + var(--measure) + 4rem);
  margin: 0 auto;
  display: flex;
  align-items: baseline;
  gap: 2.2rem;
  flex-wrap: wrap;
}
body.front .masthead { max-width: 74rem; }

/* The wordmark: the two-band lockup from the mark sheet.
 *
 * Not rules under a word. The rust band runs in from the LEFT on the word's own
 * line and stops where the word starts — so the word occupies the top course of
 * the strata and the ink bands are the courses beneath it. That is the same
 * arrangement as the icon, which is why it reads as the same mark. The dot
 * stays.
 *
 * Drawn in CSS rather than set as an SVG so it scales with the type it belongs
 * to, inherits the theme's ink, and costs no request. `em` throughout for the
 * same reason: at any font-size the lockup keeps its proportions.
 *
 * The two ink bands are ONE element painted with a gradient. There are only two
 * pseudo-elements to spend and the rust band and the dot want one each; a
 * gradient with length stops draws band, gap, band without asking the markup
 * for a third box.
 */
.wordmark {
  font-family: var(--display);
  font-variation-settings: 'SOFT' 30, 'WONK' 1, 'opsz' 100;
  font-weight: 600;
  font-size: 1.5rem;
  letter-spacing: -0.015em;
  text-decoration: none;
  color: var(--ink);
  line-height: 1;
  display: inline-grid;
  /* The rust band's column. Sized so it comes to roughly 40% of the lockup,
     as on the sheet — a logotype's proportions are fixed, not fluid. */
  grid-template-columns: 2.05em auto;
  column-gap: .26em;
  row-gap: .17em;
}
/* The rust band. --brand, not --seal: a logotype is exempt from the contrast
   floor precisely because it is never read as text. Bottom-aligned to sit on
   the word's baseline rather than through its middle. */
.wordmark::before {
  content: '';
  grid-area: 1 / 1;
  align-self: end;
  height: .2em;
  margin-bottom: .12em;
  background: var(--brand);
}
.wordmark .wm { grid-area: 1 / 2; }
.wordmark .wm::after {
  content: '';
  display: inline-block;
  width: .26em; height: .26em;
  margin-left: .26em;
  border-radius: 50%;
  background: var(--brand);
  vertical-align: .34em;
}
/* Both ink bands, in one box. */
.wordmark::after {
  content: '';
  grid-area: 2 / 1 / 3 / 3;
  height: .58em;
  background: linear-gradient(
    to bottom,
    currentColor 0 .19em,
    transparent .19em .36em,
    currentColor .36em .58em);
}

/* Wraps rather than overflowing. At eight items this wrapped to three rows and
 * took half a phone's first screen, which is what sent three of them to the
 * footer; five wrap to two rows at worst. The one rule that cannot bend is
 * that the document itself never moves sideways. */
.masthead nav {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem 1.6rem;
  min-width: 0;
  align-items: baseline;
}
.masthead nav a { white-space: nowrap; }

@media (min-width: 48em) {
  .masthead nav { margin-left: auto; }
}
.masthead nav a, .reference a {
  font-family: var(--body);
  font-size: .84rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-decoration: none;
  padding-bottom: .2rem;
  border-bottom: 1px solid transparent;
  transition: color .18s, border-color .18s;
}
.masthead nav a:hover { color: var(--ink); border-bottom-color: var(--seal); }
.masthead nav a[aria-current] { color: var(--ink); border-bottom-color: var(--seal); }

/* ------------------------------------------------------------------ layout */

.sheet {
  max-width: calc(var(--gutter) + var(--measure) + 4rem);
  margin: 0 auto;
  padding: clamp(2.5rem, 7vw, 5.5rem) clamp(1.2rem, 5vw, 2rem) 6rem;
}

/* Marginalia: notes sit in the left gutter on wide screens, inline below the
   text on narrow ones. Scholarly-edition behaviour, not a tooltip. */
.row { display: grid; grid-template-columns: var(--gutter) minmax(0, var(--measure)); gap: 2rem; }
/* Marginalia is an aside, not fine print. At .78rem in the faintest ink on the
   palette it was the hardest thing on any page to read, which is a poor trade
   for text that carries the reasoning behind the thing beside it. */
.row > .margin {
  font-size: .92rem;
  line-height: 1.55;
  color: var(--ink-faint);
  text-align: right;
  padding-top: .35rem;
}
.margin .label {
  display: block;
  font-family: var(--body);
  text-transform: uppercase;
  letter-spacing: .09em;
  font-size: .76rem;
  color: var(--seal);
  margin-bottom: .3rem;
}

@media (max-width: 940px) {
  .row { grid-template-columns: minmax(0, 1fr); gap: .6rem; }
  .row > .margin { text-align: left; border-left: 2px solid var(--rule); padding-left: .8rem; }
  :root { --gutter: 0rem; }
}

/* ---------------------------------------------------------------- headings */

h1 {
  font-family: var(--display);
  font-variation-settings: 'SOFT' 20, 'WONK' 1, 'opsz' 144;
  font-weight: 500;
  font-size: clamp(2.5rem, 6.5vw, 4.2rem);
  line-height: 1.02;
  letter-spacing: -0.028em;
  margin: 0 0 .5rem;
  max-width: 22ch;
}

.standfirst {
  font-family: var(--body);
  font-size: 1.22rem;
  line-height: 1.55;
  color: var(--ink-soft);
  font-style: italic;
  max-width: 32ch;
  margin: 0 0 3.2rem;
}

h2 {
  font-family: var(--display);
  font-variation-settings: 'SOFT' 20, 'opsz' 60;
  font-weight: 500;
  font-size: 1.62rem;
  letter-spacing: -0.015em;
  line-height: 1.2;
  margin: 3.6rem 0 .9rem;
}

h3 {
  font-family: var(--body);
  font-weight: 600;
  font-size: 1.02rem;
  letter-spacing: .01em;
  margin: 2.2rem 0 .5rem;
}

p { margin: 0 0 1.15rem; }
.sheet p, .sheet ul, .sheet ol { max-width: var(--measure); }

/* Drop cap on the opening paragraph only — a printed-page gesture. */
.opening::first-letter {
  font-family: var(--display);
  font-variation-settings: 'SOFT' 0, 'WONK' 0, 'opsz' 144;
  font-weight: 500;
  float: left;
  font-size: 3.9em;
  line-height: .82;
  padding: .06em .09em 0 0;
  color: var(--seal);
}

a { color: var(--ink); text-decoration-color: var(--rule); text-underline-offset: .18em; }
a:hover { text-decoration-color: var(--seal); }

strong { font-weight: 600; }
em { font-style: italic; }

hr {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: 3.4rem 0;
  max-width: var(--measure);
}

/* ------------------------------------------------------------------- lists */

ul, ol { padding-left: 1.15rem; margin: 0 0 1.3rem; }
li { margin-bottom: .5rem; }
li::marker { color: var(--ink-faint); }

/* Numbered procedure — large numerals set into the gutter. */
.steps { list-style: none; padding: 0; counter-reset: step; }
.steps > li {
  counter-increment: step;
  position: relative;
  padding-left: 3.1rem;
  margin-bottom: 2.4rem;
}
.steps > li::before {
  content: counter(step);
  position: absolute;
  left: 0; top: -.32rem;
  font-family: var(--display);
  font-variation-settings: 'SOFT' 40, 'WONK' 1, 'opsz' 144;
  font-size: 2.1rem;
  font-weight: 400;
  color: var(--seal);
  opacity: .85;
  line-height: 1;
}
.steps h3 { margin-top: 0; }

/* ------------------------------------------------------------------- code */

code {
  font-family: var(--mono);
  font-size: .86em;
  background: var(--paper-deep);
  padding: .1em .34em;
  border-radius: 2px;
}

pre {
  font-family: var(--mono);
  font-size: .88rem;
  line-height: 1.62;
  background: var(--paper-deep);
  border: 1px solid var(--rule);
  border-left: 2px solid var(--seal);
  padding: 1rem 1.1rem;
  overflow-x: auto;
  max-width: var(--measure);
  margin: 0 0 1.4rem;
}
pre code { background: none; padding: 0; font-size: inherit; }
.out { color: var(--ink-soft); }

/* A hash is an artifact. Set it like one. */
.hash {
  display: block;
  font-family: var(--mono);
  font-size: .82rem;
  letter-spacing: .04em;
  word-break: break-all;
  color: var(--ink-soft);
  background: var(--paper-deep);
  border: 1px dashed var(--rule);
  padding: .55rem .7rem;
  margin: 0 0 1.2rem;
  max-width: var(--measure);
}

/* ------------------------------------------------------------------ blocks */

.callout {
  border: 1px solid var(--rule);
  border-top: 2px solid var(--seal);
  background: var(--paper-deep);
  padding: 1.25rem 1.4rem;
  margin: 2rem 0;
  max-width: var(--measure);
}
.callout > :last-child { margin-bottom: 0; }
.callout .kicker {
  display: block;
  font-size: .76rem;
  text-transform: uppercase;
  letter-spacing: .11em;
  color: var(--seal);
  margin-bottom: .5rem;
}

.ledger { max-width: var(--measure); margin: 1.6rem 0 2rem; }
.ledger div {
  display: flex;
  gap: .7rem;
  align-items: baseline;
  padding: .5rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: 1rem;
}
.ledger div:first-child { border-top: 1px solid var(--rule); }
.mark { font-family: var(--mono); font-size: .9em; flex: none; width: 1.1rem; }
.yes { color: var(--verified); }
.no  { color: var(--seal); }
.wait { color: var(--pending); }

table { border-collapse: collapse; width: 100%; max-width: var(--measure); margin: 1.4rem 0 2rem; font-size: .98rem; }
th, td { text-align: left; padding: .55rem .8rem .55rem 0; border-bottom: 1px solid var(--rule); vertical-align: top; }
th { font-weight: 600; font-size: .78rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-faint); }

/* ------------------------------------------------------------------ status */

.status-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-right: .45rem; vertical-align: .1em;
}
.built { background: var(--verified); }
.partial { background: var(--pending); }
.unbuilt { background: var(--rule); }

/* ----------------------------------------------------------------- colophon */

.colophon {
  border-top: 1px solid var(--rule);
  margin: 5rem auto 0;
  max-width: calc(var(--gutter) + var(--measure) + 4rem);
  padding: 1.8rem clamp(1.2rem, 5vw, 2rem) 3.5rem;
  font-size: .92rem;
  color: var(--ink-faint);
  line-height: 1.6;
}
body.front .colophon { max-width: 74rem; }
.colophon p { max-width: 46ch; }

/* A real footer, because it is carrying real navigation.
 *
 * Three of the eight nav items moved down here when the masthead row got too
 * long, and a single line of links was not a home for them — the reference
 * pages became findable only by someone who already suspected they existed. A
 * footer that holds every page on the site, grouped and labelled, is a place a
 * reader can be sent to. It also gives the mark a second showing at the point
 * where someone has read to the end. */
.foot {
  display: grid;
  gap: 2.2rem;
  padding-bottom: 1.8rem;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--rule);
}
@media (min-width: 48em) {
  .foot { grid-template-columns: 1.7fr 1fr 1fr 1fr; gap: 2.6rem; }
}

.foot-brand .wordmark { font-size: 1.25rem; margin-bottom: .9rem; }
.foot-brand p { max-width: 32ch; margin: 0 0 .5rem; font-size: .98rem; line-height: 1.55; }
.foot-state { color: var(--ink-faint); }

.foot-col { display: flex; flex-direction: column; align-items: start; gap: .55rem; }
.foot-h {
  font-family: var(--body);
  text-transform: uppercase;
  letter-spacing: .09em;
  font-size: .76rem;
  color: var(--seal);
  margin-bottom: .15rem;
}
.foot-col a {
  font-size: .95rem;
  color: var(--ink-soft);
  text-decoration: none;
  padding-bottom: .1rem;
  border-bottom: 1px solid transparent;
  transition: color .18s, border-color .18s;
}
.foot-col a:hover, .foot-col a:focus-visible, .foot-col a[aria-current] {
  color: var(--ink);
  border-bottom-color: var(--seal);
}

.colophon-note { max-width: 62ch; }

/* Load-in: one quiet staggered reveal, then stillness. Printed things
   don't fidget. */
@media (prefers-reduced-motion: no-preference) {
  .sheet > * { animation: rise .62s cubic-bezier(.2,.7,.3,1) backwards; }
  .sheet > :nth-child(1) { animation-delay: .04s; }
  .sheet > :nth-child(2) { animation-delay: .10s; }
  .sheet > :nth-child(3) { animation-delay: .16s; }
  .sheet > :nth-child(4) { animation-delay: .21s; }
  .sheet > :nth-child(n+5) { animation-delay: .25s; }
}
@keyframes rise { from { opacity: 0; transform: translateY(9px); } }

:focus-visible { outline: 2px solid var(--seal); outline-offset: 2px; border-radius: 2px; }

::selection { background: var(--seal); color: var(--paper); }

/* --------------------------------------------------------------- signup */
.signup { display: flex; gap: .5rem; flex-wrap: wrap; margin: .9rem 0 .5rem; }
.signup input {
  flex: 1 1 16rem; min-width: 0;
  font-family: var(--body); font-size: 1rem;
  padding: .55rem .7rem;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--edge); border-radius: 3px;
}
.signup input:focus-visible { outline: 2px solid var(--seal); outline-offset: 1px; }
.signup button {
  font-family: var(--body); font-size: 1rem;
  padding: .55rem 1.1rem;
  color: var(--paper); background: var(--ink);
  border: 1px solid var(--ink); border-radius: 3px; cursor: pointer;
}
.signup button:hover { background: var(--seal); border-color: var(--seal); }
.signup button:focus-visible { outline: 2px solid var(--seal); outline-offset: 2px; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}


/* ------------------------------------------------------------ screenshots */

/* A picture of an application is evidence, so it is shown large enough to read
   and framed like a plate in a book rather than floated like decoration. */
.shot { margin: 0 0 2.4rem; }
.shot img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--rule); border-radius: 3px;
  background: var(--paper-deep);
}
.shot figcaption {
  font-size: .92rem; line-height: 1.55; color: var(--ink-faint);
  margin-top: .55rem; max-width: 34rem;
}


/* ═══════════════════════════════════════════════════ the landing page ═══
   The rest of this site is a document: a measure, a margin, and rules. The
   landing page has a different job — someone arrives knowing nothing and
   decides in fifteen seconds whether to keep reading — so it is built out of
   cards rather than rows, and it is the only page that moves.

   WRITTEN PHONE-FIRST. Everything outside a media query is the phone's layout,
   and the wider ones are additions in `min-width`. The previous version was the
   other way round, which meant the people most likely to arrive here from a
   link — holding a telephone — were the ones reading the fallback. */

/* Wider than the document measure. The rest of the site is prose and wants 64
   characters; this page is mostly demos, and a demo too small to read is
   decoration. */
.landing { max-width: 74rem; }

/* On a large monitor, a little more of it. Not all of it — a page that runs
   the full width of a 2000-pixel display has a headline nobody can track back
   to the start of and a paragraph measure of 200 characters. The masthead and
   colophon move with it or the alignment they were just given comes apart. */
@media (min-width: 96em) {
  .landing, body.front .masthead, body.front .colophon { max-width: 82rem; }
}

/* ── hero ─────────────────────────────────────────────────────────────── */
.hero { padding: .5rem 0 3rem; }
.hero h1 {
  font-size: clamp(2.7rem, 11vw, 5.2rem); line-height: .98;
  letter-spacing: -0.03em; margin: 0 0 1.1rem; max-width: 15ch;
  text-wrap: balance;
}
.hero .standfirst {
  font-size: clamp(1.12rem, 4.4vw, 1.5rem); color: var(--ink-soft);
  max-width: 32ch; margin: 0 0 2.4rem; line-height: 1.4;
}
.hero .standfirst em { color: var(--ink); font-style: italic; }

/* Capped. The film is 1280×800, and at the page's full 64rem it dominated
   everything under it — a demo is evidence for the claim above it, not the
   reason to be here. */
.film { margin: 0; max-width: 34rem; }
.film video {
  width: 100%; height: auto; display: block; border-radius: 4px;
  border: 1px solid var(--rule); background: var(--paper-deep);
}
/* No measure of its own: it is a caption to the film, so it runs the width of
   the film and stops where the film stops. */
.film figcaption {
  font-size: .92rem; color: var(--ink-faint); margin-top: .75rem;
  line-height: 1.55;
}

/* Two columns once there is room for two.
 *
 * As a single column the hero left half the screen empty on any monitor wider
 * than a laptop — a headline, a standfirst, and then a 34rem film with four
 * hundred pixels of nothing beside it. Side by side, the claim and the evidence
 * for it are one composition, and the film gets to be bigger while it is at it.
 *
 * 62em rather than 48em: at tablet widths two columns would give the headline
 * about fifteen characters a line, which is worse than the empty space. */
@media (min-width: 62em) {
  .hero {
    display: grid;
    grid-template-columns: 1fr 1.05fr;
    column-gap: 3.2rem;
    align-items: center;
    padding-bottom: 4rem;
  }
  .hero h1 { grid-column: 1; grid-row: 1; align-self: end; margin-bottom: 0; }
  .hero .standfirst {
    grid-column: 1; grid-row: 2; align-self: start;
    margin: 1.1rem 0 0; max-width: 30ch;
  }
  /* Spanning both rows, so it is centred against the pair rather than pinned
     to whichever of them happens to be taller. */
  .film { grid-column: 2; grid-row: 1 / 3; max-width: none; align-self: center; }
}

/* ── the signup, which is the second thing on the page ───────────────────
   Someone convinced by the first screen should not have to scroll past five
   cards and a cryptography lesson to act on it. */
.enrol {
  display: grid; gap: 1.4rem;
  padding: clamp(1.3rem, 5vw, 2.2rem);
  margin-bottom: 3.5rem;
  background: var(--paper-deep);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--brand);
  border-radius: 6px;
}
.enrol h2 {
  font-size: clamp(1.5rem, 5.5vw, 2.1rem); margin: 0 0 .5rem;
  letter-spacing: -0.022em; line-height: 1.1;
}
.enrol p { margin: 0; color: var(--ink-soft); font-size: 1.1rem; line-height: 1.55; }
/* The hint carries the privacy promise, which is the part of this band most
   worth reading and was the hardest thing on the page to read: the smallest
   size, in the faintest ink. */
.enrol .hint { font-size: .95rem; }
.enrol .hint { color: var(--ink-faint); line-height: 1.5; margin-top: .85rem; }

/* ── the stack ────────────────────────────────────────────────────────────
   On a phone these are simply cards in order, with nothing clever happening:
   stacking needs height to stack into, and a phone has none to spare. The
   sticky behaviour is added at width, below. */
.stack { display: flex; flex-direction: column; gap: 1.2rem; padding-bottom: 2rem; }
/* The footer is substantial now, so the run-up to it does not also need to be.
   Between the stack, the sheet and the colophon there were fifteen rems of
   nothing here. */
.landing { padding-bottom: 2rem; }
.card {
  display: grid; gap: 1.3rem;
  padding: clamp(1.1rem, 4.5vw, 1.6rem);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 6px;
  /* Enough shadow to read as a card lifting off the one beneath, and not a
     pixel more — this is printed matter, not a dashboard. */
  box-shadow: 0 -1px 0 var(--paper), 0 -14px 28px -18px rgba(0,0,0,.35);
}
.card .label { display: block; margin-bottom: .5rem; }
.card h3 {
  font-family: var(--display);
  font-size: clamp(1.4rem, 5.5vw, 1.9rem); line-height: 1.12;
  margin: 0 0 .65rem; letter-spacing: -0.018em; max-width: 20ch;
}
.card p { margin: 0 0 .7rem; color: var(--ink-soft); font-size: 1rem; line-height: 1.6; }
.card p:last-child { margin-bottom: 0; }

/* Edge to edge on a phone. The padding that gives prose its measure is just a
   frame around a picture, and the demo is the reason the card exists — so the
   clip takes back the card's own padding and runs the full width of the
   screen. */
.clip {
  position: relative; overflow: hidden;
  background: var(--paper-deep);
  border-block: 1px solid var(--rule);
  margin-inline: calc(-1 * clamp(1.1rem, 4.5vw, 1.6rem));
}
.clip video, .clip img { width: 100%; height: auto; display: block; }
/* The still is the fallback, and it is the ONLY thing shown when someone has
   asked for less motion. A looping clip nobody can pause is a decision made on
   their behalf. */
.clip img { display: none; }
/* A card whose illustration is a still and nothing else. Without this the
   reduced-motion rule below hid its only content and left an empty box. */
.clip.still img { display: block; }
@media (prefers-reduced-motion: reduce) {
  .clip video { display: none; }
  .clip img { display: block; }
}

/* ── where to go for the detail ──────────────────────────────────────────
   The last card in the stack, and the only one that is a list of doors rather
   than a demo. It earns its place there twice: it keeps the long-form pages
   one tap from the front, and being the last thing in the stack it is what
   scrolls up over card five, which is what lets card five dock like the four
   above it. A sticky element only holds while something is still coming. */
.onward-list { list-style: none; padding: 0; margin: 0; }
.onward-list a {
  display: block; padding: .95rem 0;
  border-top: 1px solid var(--rule);
  text-decoration: none; color: inherit;
}
.onward-list li:last-child a { border-bottom: 1px solid var(--rule); }
/* --seal, not --brand: this is set in type and owes its 4.5:1. The rust is a
   logotype colour and is exempt precisely because it is never read as text. */
.onward-list b {
  display: block; color: var(--seal); font-weight: 600; margin-bottom: .22rem;
}
.onward-list b::after { content: ' \2192'; }
.onward-list span {
  display: block; color: var(--ink-soft); font-size: .95rem; line-height: 1.5;
}
.onward-list a:hover b, .onward-list a:focus-visible b { text-decoration: underline; }

.steps { list-style: none; counter-reset: s; padding: 0; margin: 0 0 2.4rem; }
.steps li {
  counter-increment: s; position: relative;
  padding: 0 0 1.5rem 3.1rem; border-left: 1px solid var(--rule); margin-left: .6rem;
}
.steps li::before {
  content: counter(s); position: absolute; left: -0.85rem; top: -0.1rem;
  width: 1.7rem; height: 1.7rem; border-radius: 50%;
  background: var(--paper); border: 1px solid var(--edge);
  color: var(--brand); font-family: var(--mono); font-size: .8rem;
  display: grid; place-items: center;
}
.steps li:last-child { border-left-color: transparent; padding-bottom: .4rem; }
.steps h3 { font-family: var(--body); font-size: 1.02rem; font-weight: 600;
            margin: 0 0 .3rem; color: var(--ink); }
.steps p { margin: 0; color: var(--ink-soft); font-size: 1rem; line-height: 1.6; }

/* The one block that has to be read by someone who is about to be alarmed. */
.callout.plain { border-left-color: var(--brand); }
.callout.plain .aside {
  font-size: .92rem; color: var(--ink-faint); margin-bottom: 0;
}
.more { margin-top: 1.8rem; }

/* ── wide enough for two columns ──────────────────────────────────────────
   Sticky, not scripted. Each card pins a little lower than the one before, so
   the previous card's top edge stays visible underneath as the next rides up
   over it — the layering the mark is about, doing a job. */
@media (min-width: 48em) {
  .enrol { grid-template-columns: 1fr 1fr; gap: 2.4rem; align-items: start; }

  .stack { gap: 1.6rem; }
  .card {
    position: sticky;
    top: calc(2.5rem + var(--i) * 1.15rem);
    /* The demo gets the larger share — it is the evidence, and the paragraph
       beside it is the caption. 1.35 rather than 1.5 on purpose: at 1.5 the
       column grew past every clip’s own width and started upscaling them. */
    grid-template-columns: 1fr 1.35fr;
    gap: 2.4rem; align-items: center;
    padding: clamp(1.6rem, 3vw, 2.5rem);
  }
  .clip {
    margin-inline: 0;
    border: 1px solid var(--rule); border-radius: 4px;
  }

  /* Sticky like the rest, one offset further down (--i:5 on the element).
     It still rides up over card five on the way — sticky only engages once an
     element reaches its offset — so card five docks, and then this comes to
     rest 1.15rem below it, leaving the same sliver every other card leaves.
     Left non-sticky, it stopped wherever the page ran out and card five sat
     half-covered with its heading cut through the middle of a word. */
  .onward { grid-template-columns: 1fr 1.35fr; gap: 2.4rem; align-items: start; }
}

/* Roomy enough that a card can hold most of the screen, which is what makes
   the demo inside it worth looking at. Capped in `rem` as well as `vh` so a
   tall desktop window does not stretch five cards into five empty fields. */
@media (min-width: 62em) {
  .card { min-height: min(70vh, 31rem); align-content: center; }
  .onward { min-height: 0; }
}


/* ── the explainer page ───────────────────────────────────────────────────
   Diagrams instead of formulae. Everything is inline SVG in `currentColor`
   and the palette's own tokens, so it follows the theme and asks nothing of
   the network. */
.diagram {
  margin: 1.6rem 0 1.9rem; padding: .6rem .2rem;
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
}
.diagram svg { display: block; width: 100%; height: auto; }

/* The live fingerprint. It is the one place on the site where a reader can
   test the claim themselves rather than read an assurance of it. */
.tryit {
  margin: 1.4rem 0 1.8rem; padding: 1rem 1.1rem 1.1rem;
  background: var(--paper-deep); border: 1px solid var(--rule); border-radius: 4px;
}
.tryit label {
  display: block; font-size: .76rem; text-transform: uppercase;
  letter-spacing: .11em; color: var(--seal); margin-bottom: .4rem;
}
.tryit textarea {
  width: 100%; font-family: var(--body); font-size: 1rem; line-height: 1.5;
  padding: .55rem .7rem; border: 1px solid var(--edge); border-radius: 3px;
  background: var(--paper); color: var(--ink); resize: vertical;
}
.tryit .tryout { margin: .9rem 0 .4rem; display: flex; align-items: baseline;
                 gap: .7rem; flex-wrap: wrap; }
.tryit .tryout .label { margin: 0; flex: none; }
.tryit code {
  font-family: var(--mono); font-size: .82rem; letter-spacing: .02em;
  color: var(--brand); word-break: break-all; flex: 1; min-width: 12rem;
}
.tryit .hint { margin: 0; font-size: .92rem; }

/* ── the pre-launch checklist ─────────────────────────────────────────────
   A status table said what each component was; a checklist says where the
   whole thing is. Grouped the way the plan is grouped, so the public page and
   the internal one cannot drift apart without somebody noticing.

   The state is a WORD as well as a colour. Colour alone would fail the same
   readers the accessibility work is for, and this project does not get to
   argue for contrast on one page and encode meaning in hue on another. */
.checklist { list-style: none; padding: 0; margin: .4rem 0 1rem; }
.ck {
  display: grid;
  grid-template-columns: 1fr;
  gap: .3rem;
  padding: 1rem 0;
  border-top: 1px solid var(--rule);
}
.ck:last-child { border-bottom: 1px solid var(--rule); }
.ck-state {
  font-family: var(--body);
  text-transform: uppercase;
  letter-spacing: .09em;
  font-size: .72rem;
  white-space: nowrap;
}
.ck-state::before {
  content: '';
  display: inline-block;
  width: .58em; height: .58em;
  border-radius: 2px;
  margin-inline-end: .5em;
  background: currentColor;
}
/* --verified and --pending were chosen for a deuteranopia separation of 0.241
   on the verification screen; they are the right pair to reuse here. */
.ck[data-state="done"] .ck-state     { color: var(--verified); }
.ck[data-state="half"] .ck-state     { color: var(--pending); }
.ck[data-state="next"] .ck-state     { color: var(--seal); }
.ck[data-state="later"] .ck-state,
.ck[data-state="deferred"] .ck-state { color: var(--ink-faint); }

.ck-body h3 {
  font-family: var(--display);
  font-size: 1.02rem;
  margin: 0 0 .2rem;
  line-height: 1.25;
}
.ck-body p { margin: 0; color: var(--ink-soft); font-size: .95rem; line-height: 1.55; }

@media (min-width: 40em) {
  /* The state gets its own column, so the eye can run down it and read the
     shape of the release without reading a word of the detail. */
  .ck { grid-template-columns: 7rem 1fr; gap: 1.2rem; align-items: baseline; }
}

/* The honeypot. Off the page rather than `display: none`, because some bots
   skip what is display:none and fill what is merely positioned away — and
   either way no sighted user, keyboard user or screen-reader user can reach
   it. */
.signup .nonce {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}
