/* --------------------------------------------------------------------------
   landing.css — the public page, and nothing else.

   Self-contained on purpose. It shares no tokens and no rules with today.css or apps.css: the
   product UI has to survive a thousand states, this page has one. Importing the product's stylesheet
   here would tie a marketing change to a product regression.

   The palette is white, near-black, and a five-step grey ramp. There is no accent colour anywhere in
   this file. Two things on the rendered page carry colour of their own and are left alone: the
   product screenshots, and the brand marks in the wall — a Slack mark that is not Slack-coloured
   costs the reader a beat of recognition for nothing, so they are shown as their owners draw them.

   Structure, in order: tokens · reset · type · header · hero · graph · sections · use cases ·
   specimen blocks · figures · logo wall · notes · not-built-yet · close · footer · reveal ·
   reduced motion.

   Specificity note: every block below is a single class. Where two single-class rules touch the same
   property (`.sec` and `.sec-apps` both set padding-block), the override is declared *after* the base
   in this file and nowhere else, so nothing here depends on source order across files.
   -------------------------------------------------------------------------- */

:root {
  color-scheme: light;

  /* ink and paper — the whole palette */
  --paper: #ffffff;
  --ink: #0b0b0b;
  --ink-2: #3a3a3a;
  --ink-3: #6b6b6b;
  --ink-4: #979797;
  --rule: #e3e3e3;
  --rule-soft: #efefef;

  /* type */
  --serif: "Newsreader", ui-serif, Georgia, "Times New Roman", serif;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --f-h1: clamp(2.5rem, 8.2vw, 5.25rem);
  --f-h2: clamp(1.625rem, 3.9vw, 2.75rem);
  --f-statement: clamp(1.375rem, 3.1vw, 2.125rem);
  --f-lede: clamp(1.0625rem, 1.05vw + 0.74rem, 1.375rem);
  --f-lede-sm: clamp(1rem, 0.5vw + 0.88rem, 1.125rem);
  --f-body: clamp(0.9375rem, 0.3vw + 0.86rem, 1.0625rem);
  --f-small: 0.875rem;
  --f-mono: 0.6875rem;

  /* spacing */
  --s1: 0.25rem;
  --s2: 0.5rem;
  --s3: 0.75rem;
  --s4: 1rem;
  --s5: 1.5rem;
  --s6: 2rem;
  --s7: 3rem;
  --s8: 4.5rem;
  --s9: 6.5rem;

  --gutter: clamp(1.25rem, 5vw, 3.5rem);
  --measure: 60ch;
  --margin-col: 9.5rem;
  --ease: cubic-bezier(0.2, 0.65, 0.2, 1);
}

/* ---------------------------------------------------------------- reset */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 5rem;
}

body.lp {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--f-body);
  line-height: 1.62;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  /* No overflow-x:hidden here. It turns <body> into a scroll container, which quietly breaks the
     sticky header — nothing on this page is allowed to overflow sideways in the first place. */
}

img { max-width: 100%; }

figure { margin: 0; }

dl, dd, dt { margin: 0; }

h1, h2, h3 { margin: 0; font-weight: 400; }

p { margin: 0; }

ul, ol { margin: 0; padding: 0; list-style: none; }

a { color: inherit; }

code {
  font-family: var(--mono);
  font-size: 0.9em;
  letter-spacing: -0.01em;
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 20;
  padding: var(--s3) var(--s4);
  background: var(--ink);
  color: var(--paper);
  font-size: var(--f-small);
  text-decoration: none;
}

.skip:focus { left: var(--s3); top: var(--s3); }

#top { display: block; }

/* ----------------------------------------------------------------- type */

.wrap {
  width: 100%;
  max-width: 1160px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

h1 {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-size: var(--f-h1);
  font-weight: 450;
  line-height: 0.99;
  letter-spacing: -0.032em;
  max-width: 15.5ch;
  text-wrap: balance;
}

h1 em,
h2 em {
  font-style: italic;
  font-weight: 400;
}

h2 {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-size: var(--f-h2);
  font-weight: 450;
  line-height: 1.08;
  letter-spacing: -0.022em;
  max-width: 22ch;
  text-wrap: balance;
}

.lede {
  font-size: var(--f-lede);
  line-height: 1.48;
  color: var(--ink-2);
  max-width: 44ch;
  margin-top: var(--s5);
  letter-spacing: -0.008em;
}

.prose p {
  max-width: var(--measure);
  color: var(--ink-2);
}

/* The band ledes carry one sentence, not a paragraph, so they sit closer to the heading. */
.lede-tight {
  margin-top: var(--s4);
  font-size: var(--f-lede-sm);
  max-width: 52ch;
}

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

.eyebrow {
  font-family: var(--mono);
  font-size: var(--f-mono);
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-4);
}

.eyebrow-inline {
  padding-bottom: var(--s3);
  border-bottom: 1px solid var(--rule);
  margin-bottom: var(--s2);
}

/* The one eyebrow on the page set in ink rather than grey: the band it labels is the one a reader
   must not skim past. */
.eyebrow-flag { color: var(--ink); }

/* --------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-weight: 500;
  text-decoration: none;
  border: 1px solid var(--ink);
  border-radius: 2px;
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease);
  white-space: nowrap;
}

.btn:hover { background: #2b2b2b; border-color: #2b2b2b; }

.btn-sm {
  font-size: var(--f-small);
  padding: 0.45rem 0.9rem;
  letter-spacing: -0.005em;
}

.btn-lg {
  font-size: 1rem;
  padding: 0.8rem 1.6rem;
  letter-spacing: -0.005em;
}

.actions {
  margin-top: var(--s6);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s4) var(--s5);
}

.actions-note {
  font-size: var(--f-small);
  line-height: 1.45;
  color: var(--ink-3);
  max-width: 30ch;
}

/* ---------------------------------------------------------------- header */

.site {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}

.site-in {
  display: flex;
  align-items: center;
  gap: var(--s5);
  min-height: 3.75rem;
}

.wordmark {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-size: 1.3125rem;
  font-weight: 500;
  letter-spacing: -0.018em;
  text-decoration: none;
  color: var(--ink);
  margin-right: auto;
}

.site-nav { display: none; }

@media (min-width: 62rem) {
  .site-nav {
    display: flex;
    gap: var(--s5);
    margin-right: var(--s2);
  }

  .site-nav a {
    font-family: var(--mono);
    font-size: var(--f-mono);
    font-weight: 500;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--ink-3);
    text-decoration: none;
    padding-block: 0.35rem;
    transition: color 0.18s var(--ease);
  }

  .site-nav a:hover { color: var(--ink); }
}

/* ------------------------------------------------------------------ hero */

.hero { padding-top: clamp(2.5rem, 6vw, 5rem); }

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s8);
  align-items: end;
}

@media (min-width: 61.25rem) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr) 21rem;
    column-gap: var(--s7);
    row-gap: 0;
  }
}

.spec { border-top: 1px solid var(--ink); }

.spec-row {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr);
  gap: var(--s4);
  padding-block: 0.7rem;
  border-bottom: 1px solid var(--rule);
}

.spec dt {
  font-family: var(--mono);
  font-size: var(--f-mono);
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-4);
  padding-top: 0.2rem;
}

.spec dd {
  font-size: var(--f-small);
  line-height: 1.4;
  color: var(--ink);
}

.hero-fig { margin-top: clamp(2rem, 4.5vw, 3.5rem); }

.hero-fig img {
  display: block;
  width: 100%;
  max-width: 58rem;
  height: auto;
  margin-inline: auto;
  /* The capture is white to its own edge, so it needs no frame — it floats in the page. The inset
     shaves the one grey pixel the canvas left on its right edge, which read as a stray rule. */
}

figcaption {
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--rule);
  font-size: var(--f-small);
  line-height: 1.5;
  color: var(--ink-3);
  max-width: 54ch;
}

.cap-k {
  display: block;
  font-family: var(--mono);
  font-size: var(--f-mono);
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: var(--s1);
}

.hero-fig figcaption {
  max-width: 58rem;
  margin-inline: auto;
}

/* ----------------------------------------------------------------- graph */

/* The hero graph. `landing-graph.js` appends a <canvas> and a panel into .graph-stage and adds
   .is-live to it; everything here is written so that the pre-script state — the fallback prose the
   template rendered — is a finished thing to look at, not a hole. */

.graph {
  border: 1px solid var(--rule);
  border-radius: 3px;
  overflow: hidden;   /* keeps the canvas inside the rounded corner; clips nothing else */
}

.graph-stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 61.25rem) {
  .graph-stage {
    /* Panel to the right of the canvas, wide enough for a fact statement to breathe. */
    grid-template-columns: minmax(0, 1fr) 20.5rem;
  }
}

.g-canvas {
  display: block;
  width: 100%;
  height: clamp(18rem, 34vw, 26rem);
  /* pan-y is the whole scroll story: a vertical swipe belongs to the document, so the graph can
     never trap the page. There is no wheel handler in the module either. */
  touch-action: pan-y;
}

.g-canvas:focus-visible { outline-offset: -2px; }

.g-panel {
  border-top: 1px solid var(--rule);
  padding: var(--s5) clamp(1rem, 2.5vw, 1.5rem);
  min-height: 12rem;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}

@media (min-width: 61.25rem) {
  .g-panel {
    border-top: 0;
    border-left: 1px solid var(--rule);
    min-height: 0;
    overflow-y: auto;
  }
}

.g-hint {
  font-size: var(--f-small);
  line-height: 1.5;
  color: var(--ink-3);
  max-width: 40ch;
}

.g-stats { border-top: 1px solid var(--rule); }

.g-stat {
  display: flex;
  justify-content: space-between;
  gap: var(--s4);
  padding-block: 0.4rem;
  border-bottom: 1px solid var(--rule-soft);
}

.g-stat dt {
  font-family: var(--mono);
  font-size: var(--f-mono);
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-4);
}

.g-stat dd {
  font-family: var(--mono);
  font-size: var(--f-small);
  color: var(--ink);
}

.g-kind {
  font-family: var(--mono);
  font-size: var(--f-mono);
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-4);
}

.g-title {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-size: 1.25rem;
  line-height: 1.16;
  letter-spacing: -0.018em;
  color: var(--ink);
  margin-top: var(--s1);
  max-width: 26ch;
}

.g-topics {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}

.g-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  background: none;
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 0.25rem 0.5rem;
  font-family: var(--mono);
  font-size: var(--f-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  cursor: pointer;
  transition: border-color 0.18s var(--ease), color 0.18s var(--ease);
}

.g-chip:hover { border-color: var(--ink); color: var(--ink); }

.g-chip-n { color: var(--ink-4); letter-spacing: 0; }

.g-facts { border-top: 1px solid var(--ink); }

.g-facts li {
  display: grid;
  gap: 0.25rem;
  padding-block: 0.6rem;
  border-bottom: 1px solid var(--rule-soft);
}

.g-f-text {
  font-size: var(--f-small);
  line-height: 1.45;
  color: var(--ink);
}

.g-f-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.6rem;
  font-family: var(--mono);
  font-size: var(--f-mono);
  letter-spacing: 0.06em;
}

.g-f-topic { text-transform: uppercase; color: var(--ink-4); }

/* The point of the whole widget: the file, next to the fact, always. */
.g-f-src { color: var(--ink-2); letter-spacing: -0.01em; }

.g-back {
  align-self: start;
  margin-top: auto;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rule);
  padding: 0 0 2px;
  font-family: var(--sans);
  font-size: var(--f-small);
  color: var(--ink-3);
  cursor: pointer;
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease);
}

.g-back:hover { color: var(--ink); border-color: var(--ink); }

/* The no-script / failed-fetch state. Hidden only once the module has actually mounted. */
.graph-fallback {
  padding: clamp(1.5rem, 4vw, 2.75rem);
  display: grid;
  gap: var(--s3);
}

.graph-stage.is-live > .graph-fallback { display: none; }

.graph-fb-lead {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-size: clamp(1.125rem, 2.2vw, 1.5rem);
  line-height: 1.28;
  letter-spacing: -0.018em;
  color: var(--ink);
  max-width: 44ch;
}

.graph-fb-note {
  font-size: var(--f-small);
  color: var(--ink-3);
  max-width: 46ch;
}

.graph-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s5);
  padding: var(--s3) clamp(1rem, 2.5vw, 1.5rem);
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: var(--f-mono);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.graph-legend li {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}

/* The legend swatches repeat the canvas exactly: type is carried by shape and weight, not hue, so
   the graph reads in greyscale and for a colour-blind reader without a colour key. */
.lg-mark {
  display: inline-block;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--ink-4);
}

.lg-root { background: var(--ink); }
.lg-topic { width: 9px; height: 9px; background: var(--ink-2); }
.lg-entity { width: 9px; height: 9px; background: var(--paper); border: 1.5px solid var(--ink-3); }
.lg-source { width: 9px; height: 9px; border-radius: 1px; background: var(--paper); border: 1.5px solid #8d8d8d; }
.lg-fact { width: 6px; height: 6px; background: #b4b4b4; }

/* -------------------------------------------------------------- sections */

.sec {
  /* Both of these open a gap at the same border, so the space between two sections is the sum.
     Kept deliberately modest: the rule does the separating, not the emptiness. */
  padding-block: clamp(2rem, 3.4vw, 3.25rem);
  border-top: 1px solid var(--rule);
  margin-top: clamp(1.75rem, 2.8vw, 2.5rem);
}

.sec-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s4);
}

@media (min-width: 56.25rem) {
  .sec-head {
    grid-template-columns: var(--margin-col) minmax(0, 1fr);
    column-gap: var(--s7);
    row-gap: 0;
  }

  .sec-head > .eyebrow { padding-top: 0.55rem; }
}

.sec-main > .prose { margin-top: var(--s5); }

/* The quiet section: the smallest claim on the page gets the smallest treatment. */
.sec-uses { padding-block: clamp(2.25rem, 4vw, 3.75rem); }

/* Read-only is the argument the page exists to make, so it still gets the most room —
   just less of it than before. */
.sec-apps { padding-block: clamp(2.5rem, 4.5vw, 4.25rem); }

/* An aside block sits under a section head, aligned to the same text column. */
.sec-aside { margin-top: clamp(1.75rem, 3.5vw, 2.75rem); }

/* Two beats of one argument, side by side rather than stacked — it saves a screen of scrolling and
   puts the formats next to the sentence that explains them. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s5);
}

@media (min-width: 56.25rem) {
  .split {
    grid-template-columns: minmax(0, 1fr) 21rem;
    column-gap: var(--s7);
    align-items: start;
  }

  .split > .sec-aside { margin-top: 0; margin-left: 0; }

  /* The margin column is a full-width device; inside the split there is no room for it, so the
     eyebrow sits above its heading instead of beside it. */
  .split > .sec-head-plain { grid-template-columns: minmax(0, 1fr); gap: var(--s3); }

  .split > .sec-head-plain > .eyebrow { padding-top: 0; }
}

@media (min-width: 56.25rem) {
  .sec-aside {
    margin-left: calc(var(--margin-col) + var(--s7));
  }
}

.aside-note {
  margin-top: var(--s4);
  font-size: var(--f-small);
  color: var(--ink-3);
  max-width: 52ch;
}

/* ------------------------------------------------------------- use cases */

/* Named jobs, one line each, dense enough to take in at a scroll. The grouping is the navigation:
   a reader looking for one of these five things finds it without reading the other four. */

.uses {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s5) var(--s7);
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
}

@media (min-width: 46rem) {
  .uses { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 66rem) {
  .uses { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.use-list li {
  display: grid;
  gap: 0.1rem;
  padding-block: 0.55rem;
  border-bottom: 1px solid var(--rule-soft);
}

.u-k {
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.01em;
  line-height: 1.35;
}

.u-v {
  font-size: var(--f-small);
  line-height: 1.45;
  color: var(--ink-3);
}

/* ------------------------------------------------- specimen: file types */

.types { border-top: 1px solid var(--rule); }

.types li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.15rem;
  padding-block: 0.5rem;
  border-bottom: 1px solid var(--rule-soft);
}

@media (min-width: 34rem) {
  .types li {
    grid-template-columns: 9.5rem minmax(0, 1fr);
    gap: var(--s4);
    align-items: baseline;
  }
}

.types-k {
  font-family: var(--mono);
  font-size: var(--f-mono);
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-4);
}

.types-v {
  font-family: var(--mono);
  font-size: var(--f-small);
  color: var(--ink-2);
  letter-spacing: -0.01em;
}

/* ------------------------------------------------- specimen: fact + source */

.facts { border-top: 1px solid var(--ink); }

.facts li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.3rem var(--s4);
  padding-block: 0.6rem;
  border-bottom: 1px solid var(--rule);
}

.f-text {
  color: var(--ink);
  line-height: 1.45;
}

.f-topic {
  font-family: var(--mono);
  font-size: var(--f-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-4);
}

.f-src {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-3);
  letter-spacing: -0.01em;
}

@media (min-width: 34rem) {
  .facts li {
    grid-template-columns: minmax(0, 1fr) 5.5rem;
    align-items: baseline;
  }

  .f-src { grid-column: 1 / -1; }
}

@media (min-width: 48rem) {
  .facts li {
    grid-template-columns: minmax(0, 1fr) 5.5rem 14rem;
  }

  .f-src { grid-column: auto; }
}

/* ---------------------------------------------------------- read-only block */

.statement {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-size: var(--f-statement);
  font-weight: 400;
  line-height: 1.24;
  letter-spacing: -0.018em;
  color: var(--ink);
  max-width: 34ch;
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
  padding-top: var(--s5);
  border-top: 1px solid var(--ink);
}

@media (min-width: 56.25rem) {
  .statement { margin-left: calc(var(--margin-col) + var(--s7)); }
}

.ref {
  text-decoration: none;
  color: var(--ink-4);
  font-family: var(--mono);
  padding-inline: 0.1em;
  transition: color 0.18s var(--ease);
}

.ref sup {
  font-size: 0.62em;
  line-height: 0;
  vertical-align: super;
}

.ref:hover { color: var(--ink); }

.ledger {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s5);
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
}

@media (min-width: 48rem) {
  .ledger {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--s7);
  }
}

@media (min-width: 56.25rem) {
  .ledger { margin-left: calc(var(--margin-col) + var(--s7)); }
}

.ledger-col > .plain { margin-top: var(--s3); }

.plain { border-top: 1px solid var(--rule); }

.plain li {
  padding-block: 0.5rem;
  border-bottom: 1px solid var(--rule-soft);
  color: var(--ink-2);
  font-size: var(--f-small);
  line-height: 1.5;
}

/* --------------------------------------------------------------- logo wall */

.wall { margin-top: clamp(2rem, 4.5vw, 3.25rem); }

/* auto-fit, so it is four across on a phone-and-a-half and all eight across on a laptop without a
   breakpoint per step. */
.wall-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr));
  column-gap: var(--s4);
}

.app {
  display: block;
  padding-block: var(--s4);
  border-bottom: 1px solid var(--rule);
}

.app-mark {
  display: block;
  width: 24px;
  height: 24px;
  color: var(--ink);
  /* Full colour, deliberately. This is the one place on the page where colour appears, and it is
     doing work: eight marks are eight recognitions, and the reader gets them without reading a
     word. No filter, and nothing to hover for — what you see is what it is. */
}

.app-mark svg {
  display: block;
  width: 100%;
  height: 100%;
}

.app-name {
  display: block;
  margin-top: var(--s3);
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.01em;
}

.app-cat {
  display: block;
  margin-top: 0.1rem;
  font-family: var(--mono);
  font-size: var(--f-mono);
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-4);
}

/* ------------------------------------------------------------------ notes */

.notes-block {
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
  padding-top: var(--s5);
  border-top: 1px solid var(--ink);
}

@media (min-width: 56.25rem) {
  .notes-block { margin-left: calc(var(--margin-col) + var(--s7)); }
}

.notes {
  counter-reset: note;
  margin-top: var(--s4);
  max-width: 64ch;
}

.notes li {
  counter-increment: note;
  position: relative;
  padding-left: 2.25rem;
  padding-block: 0.4rem;
  font-size: var(--f-small);
  line-height: 1.58;
  color: var(--ink-3);
}

.notes li::before {
  content: counter(note);
  position: absolute;
  left: 0;
  top: 0.65rem;
  font-family: var(--mono);
  font-size: var(--f-mono);
  font-weight: 500;
  color: var(--ink);
}

.notes li:target { color: var(--ink); }

.notes li:target::before {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.notes code { color: var(--ink); }

/* ---------------------------------------------------------------- figures */

.fig { margin-top: clamp(2rem, 4vw, 3rem); }

.shot {
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: clamp(0.4rem, 1vw, 0.75rem);
  background: var(--paper);
  /* Capped, not full-bleed. At the full column width a 2160x1320 capture is 670px tall and costs a
     whole screen for one glance; at this width it reads as a plate and the page keeps moving. */
  max-width: 52rem;
  margin-inline: auto;
}

.shot img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 1px;
}

/* A clipping box inside the frame, so the frame keeps even padding on all four sides while the
   image itself is cropped. The apps capture used to be cropped to hide an error banner that was an
   artifact of a fake API key during the screenshot run; it has been recaptured clean, so the crop
   is now just a framing choice — show the read-only panel, which is the part worth reading here. */
.clip {
  display: block;
  overflow: hidden;
  border-radius: 1px;
}

.clip img {
  display: block;
  width: 100%;
  height: auto;
}

.clip-apps { aspect-ratio: 2160 / 900; }

@media (max-width: 47.99rem) {
  /* On a phone that wide crop is a thin letterbox, so zoom into the panel instead: the image is
     scaled past the frame and offset so the window lands on the promise itself. */
  .clip-apps { aspect-ratio: 1.85; }

  .clip-apps img {
    width: 260%;
    max-width: none;
    margin: -37% 0 0 -56.25%;
  }
}

/* --------------------------------------------------------------- whatsapp */

.wa-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2.5rem, 6vw, 4rem);
  align-items: start;
}

@media (min-width: 56.25rem) {
  .wa-grid {
    grid-template-columns: minmax(0, 1fr) 20rem;
    column-gap: var(--s7);
  }

  .wa-grid > .sec-head { grid-template-columns: var(--margin-col) minmax(0, 1fr); }
}

.fig-phone { margin-top: 0; }

.phone {
  width: fit-content;
  padding: 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 1.125rem;
}

.phone img {
  display: block;
  width: 100%;
  max-width: 18rem;
  height: auto;
  border-radius: 0.75rem;
  border: 1px solid var(--rule-soft);
}

.fig-phone figcaption { max-width: 30ch; }

/* ------------------------------------------------------- not built yet */

/* This band has one job: be unmistakable at speed. The flag is repeated on every row rather than
   stated once at the top, because a reader skimming past the heading still has to meet it. */

.next-list {
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
  border-top: 1px solid var(--ink);
}

@media (min-width: 56.25rem) {
  .next-list { margin-left: calc(var(--margin-col) + var(--s7)); }
}

.next-list li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.35rem;
  padding-block: var(--s3);
  border-bottom: 1px solid var(--rule);
}

@media (min-width: 44rem) {
  .next-list li {
    grid-template-columns: 7.75rem 12rem minmax(0, 1fr);
    column-gap: var(--s5);
    align-items: baseline;
  }
}

.next-flag {
  justify-self: start;
  font-family: var(--mono);
  font-size: var(--f-mono);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 2px;
  padding: 0.1rem 0.35rem;
  white-space: nowrap;
}

.next-name {
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.01em;
}

.next-line {
  font-size: var(--f-small);
  line-height: 1.5;
  color: var(--ink-3);
}

/* The one place the page distinguishes a partly-built surface from an absent one. */
.next-caveat {
  display: block;
  margin-top: 0.25rem;
  color: var(--ink-4);
}

/* ------------------------------------------------------------------ close */

.close {
  margin-top: clamp(2.5rem, 5vw, 4.25rem);
  border-top: 1px solid var(--ink);
  padding-block: clamp(2.75rem, 6vw, 5rem);
}

.close-line {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-size: clamp(1.75rem, 5.2vw, 3.5rem);
  font-weight: 450;
  line-height: 1.06;
  letter-spacing: -0.026em;
  max-width: 24ch;
}

.close-in > .actions { margin-top: var(--s6); }

/* ----------------------------------------------------------------- footer */

.foot {
  border-top: 1px solid var(--rule);
  padding-block: var(--s6);
}

.foot-in {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s3) var(--s5);
}

.wordmark-foot { margin-right: 0; }

.foot-note {
  font-size: var(--f-small);
  color: var(--ink-3);
  margin-right: auto;
}

.foot-link {
  font-size: var(--f-small);
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 1px;
  transition: border-color 0.18s var(--ease);
}

.foot-link:hover { border-color: var(--ink); }

/* ------------------------------------------------------------- hero entrance */

/* CSS-only, so it cannot strand anything: `both` fill-mode leaves every element in its natural
   state whether the animation runs, is cut short, or never starts. The hero is the one orchestrated
   moment on the page — everything below it just fades up as you reach it. */
@keyframes lp-rise {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

.rise {
  animation: lp-rise 0.85s var(--ease) both;
  animation-delay: var(--d, 0ms);
}

/* ----------------------------------------------------------------- reveal */

/* The hidden state is applied per element, by landing.js, and only to elements that are below the
   fold when the module runs. Three consequences, all deliberate:
     - a page with no JavaScript, or whose module fails to load, is fully visible from the start;
     - nothing already on screen is ever hidden, so there is no flash of vanishing content;
     - nothing can be stranded at opacity 0, because only JS ever puts it there.
   `.is-in` is declared after `.pre` so it wins when both are present; the element keeps `.pre` and
   transitions out of it. */
.reveal.pre {
  opacity: 0;
  transform: translateY(14px);
  will-change: opacity, transform;
}

.reveal.is-in {
  opacity: 1;
  transform: none;
  will-change: auto;
  transition:
    opacity 0.72s var(--ease) var(--d, 0ms),
    transform 0.72s var(--ease) var(--d, 0ms);
}

/* -------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
  }

  .reveal.pre {
    opacity: 1;
    transform: none;
  }
}

/* ------------------------------------------------------------------- print */

@media print {
  .site, .skip { position: static; }
  .btn { border-color: var(--ink); }
}
