/* The public site: landing, docs, and the two legal pages (spec §1.4, §6.8.15).
 *
 * ── ONE STYLESHEET FOR EVERY PUBLIC PAGE, AND WHY ─────────────────────────
 *
 * Four documents share it — the landing page, `/docs`, `/terms`, `/privacy` —
 * and a fifth is generated per public workflow at `/demo/<slug>`. A second
 * stylesheet is how two of them come to disagree about what a link looks like,
 * which is the kind of drift nobody files a bug about and everybody notices.
 *
 * It is also the styling contract's first customer. Everything this file does to
 * `<construe-search>` and `<construe-extract>` it does through `--construe-*`,
 * `::part()` and slots — if it needed anything else, the contract would be a
 * fiction we sell to integrators.
 */

/* ═══ Tokens ═══════════════════════════════════════════════════════════════
 *
 * ONE SATURATED COLOUR ON THE PAGE, and it is the accent. The neutrals are warm
 * — a trace of yellow rather than the blue-grey a default reaches for — so the
 * petrol reads as chosen against them rather than as more of the same hue at a
 * different lightness. `deny` is semantic and never decorative: it marks
 * exclusions, which is the direction that matters.
 */
:root {
  --paper: #faf9f7;
  --paper-deep: #f2efe9;
  --raised: #ffffff;
  --ink: #14181d;
  --ink-muted: #5b6470;
  --ink-faint: #8b939d;
  --hairline: #e7e4de;
  --hairline-strong: #d8d3ca;
  --accent: #145c6e;
  --accent-strong: #0e4353;
  --accent-bg: #e4eef1;
  --accent-ink: #ffffff;
  --deny: #8c3a2e;
  --glow: color-mix(in srgb, var(--accent) 13%, transparent);

  --serif: ui-serif, Charter, "Iowan Old Style", Georgia, serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* ONE MEASURE FOR THE WHOLE SITE, set once. Three different max-widths is how
     a masthead, a demo and a footer come to sit on three different left edges. */
  /* THE FRAME, not the reading measure. Reading width is capped on the text
     itself — `.lead` at 46 characters, `.prose` at 42 rems — because a hero that
     puts the promise beside a live demo and a two-column extraction section are
     pictures rather than paragraphs, and a frame narrow enough for a sentence
     starves both. */
  --measure-wide: 72rem;
  --gutter: clamp(1.15rem, 4vw, 2.5rem);
  --radius: 10px;
  --radius-lg: 16px;
  --shadow: 0 1px 2px rgba(20, 24, 29, 0.04), 0 12px 32px rgba(20, 24, 29, 0.06);
  --shadow-lift: 0 2px 4px rgba(20, 24, 29, 0.05), 0 20px 48px rgba(20, 24, 29, 0.1);
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #0f1216;
    --paper-deep: #0a0d10;
    --raised: #161b21;
    --ink: #e2e7ec;
    --ink-muted: #99a2ac;
    --ink-faint: #6e7781;
    --hairline: #242a31;
    --hairline-strong: #333b44;
    --accent: #7cc0d2;
    --accent-strong: #a5d8e6;
    --accent-bg: #14313a;
    --accent-ink: #08191f;
    --deny: #e0968b;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 12px 32px rgba(0, 0, 0, 0.3);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.45), 0 20px 48px rgba(0, 0, 0, 0.45);
  }
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
/* SMOOTH SCROLLING IS A PREFERENCE SOMEBODY HAS ALREADY EXPRESSED. An anchor
   that animates for a person who asked their system not to animate is the one
   motion they cannot escape by not hovering anything. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 16px/1.65 system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ═══ Type ═════════════════════════════════════════════════════════════════ */

h1, h2, h3 { text-wrap: balance; }

h1 {
  font-family: var(--serif);
  font-size: clamp(2.3rem, 6.2vw, 4rem);
  line-height: 1.03;
  letter-spacing: -0.026em;
  margin: 0 0 1.2rem;
  max-width: 20ch;
}

h2 {
  font-family: var(--serif);
  font-size: clamp(1.55rem, 3.2vw, 2.15rem);
  line-height: 1.14;
  letter-spacing: -0.02em;
  margin: 0 0 1rem;
  max-width: 24ch;
}

h3 { font-size: 1.0625rem; line-height: 1.35; margin: 0 0 0.4rem; letter-spacing: -0.008em; }

.eyebrow {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.lead, .standfirst {
  font-size: clamp(1.05rem, 1.6vw, 1.2rem);
  line-height: 1.6;
  color: var(--ink-muted);
  max-width: 46ch;
  margin: 0 0 1.75rem;
}
.lead em, .standfirst em { color: var(--ink); font-style: italic; }
.lead strong, .standfirst strong { color: var(--ink); font-weight: 600; }

.muted { color: var(--ink-muted); }

/* Links in prose. The site had none until the demo page, so the browser's
   default blue was never on screen to look wrong — and on a page with one
   accent, it does. */
a { color: var(--accent); text-underline-offset: 0.16em; }
a:hover { text-decoration-thickness: 2px; }

/* ═══ Layout ═══════════════════════════════════════════════════════════════ */

/* ONE LEFT EDGE FOR THE WHOLE PAGE. The hero and the extraction demo want a
   wider frame than a paragraph does, and giving those two their own max-width
   put three different left edges down one scroll — the masthead, the demo and
   the footer each starting somewhere else, which reads as three pages stapled
   together. So the FRAME is one width everywhere and the READING measure is set
   on the text: `.lead`, `.prose` and the headings each cap their own line
   length, which is where a measure belongs. */
.wrap { max-width: var(--measure-wide); margin: 0 auto; padding-inline: var(--gutter); }

section { scroll-margin-top: 5rem; }

/* A BAND IS A BACKGROUND, NOT A BOX. Alternating the page's own paper against a
   slightly deeper one gives the long scroll a rhythm without adding a single
   border — and it is the one device that reads the same in both colour schemes,
   because both tokens move together. */
.band { background: var(--paper-deep); border-block: 1px solid var(--hairline); }
.band-tight { padding-block: clamp(3rem, 7vw, 5rem); }
.stack { padding-block: clamp(3.5rem, 9vw, 7rem); }

/* ── A SECTION IS COPY BESIDE A THING ─────────────────────────────────────
 *
 * Every section on the landing page is a claim and the evidence for it — four
 * keys, two edges, ten lines of integration — and stacked in one column the
 * evidence is always below the fold from the sentence that introduces it, with
 * half the frame empty beside both. Side by side, the reader's eye goes from the
 * claim to the thing without a scroll in between.
 *
 * THE COPY COLUMN IS THE NARROWER ONE. It is prose and caps at its own measure;
 * the evidence is a panel and takes what is left.
 */
.split { display: grid; gap: 2rem; align-items: start; }
@media (min-width: 62rem) {
  .split { grid-template-columns: 0.85fr 1.15fr; gap: 4rem; align-items: center; }
  .split > :first-child > :last-child { margin-bottom: 0; }
}

/* ═══ The top bar ══════════════════════════════════════════════════════════
 *
 * STICKY AND ALMOST NOT THERE. The page is long and the only two things
 * somebody wants from anywhere in it are the docs and a way onto the list; a
 * bar that announced itself would be competing with the hero it sits above.
 */
.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  /* TRANSPARENT UNTIL THE PAGE MOVES, which is not a flourish. The wash behind
     the hero runs up to the top of the document, and a bar tinted 82% paper over
     it is a lighter rectangle with a hard horizontal edge along its bottom — two
     backgrounds meeting at a line, right across the first thing anybody sees. */
  background: transparent;
  backdrop-filter: saturate(1.6) blur(12px);
  -webkit-backdrop-filter: saturate(1.6) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
/* THE RULE ARRIVES ONLY ONCE THE PAGE HAS MOVED. A permanent line under a
   transparent bar draws a border across the top of the hero for no reason.
   `animation-timeline` is progressive: where it is unsupported the bar simply
   keeps its hairline, which is the state it used to have all the time. */
@supports (animation-timeline: scroll()) {
  .topbar {
    animation: topbar-settle linear both;
    animation-timeline: scroll();
    animation-range: 0 6rem;
  }
  @keyframes topbar-settle {
    to { border-bottom-color: var(--hairline); background: color-mix(in srgb, var(--paper) 94%, transparent); }
  }
}
.topbar-inner {
  max-width: var(--measure-wide);
  margin: 0 auto;
  padding: 0.8rem var(--gutter);
  display: flex;
  align-items: center;
  gap: 1rem;
}
.wordmark {
  font-family: var(--mono);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
/* THE MARK IS THE FAVICON, INLINE. It was a `c` in a coloured square drawn with
   type — which meant the wordmark, the browser tab and the touch icon were three
   different renderings of the same idea, and only one of them was a shape. It is
   one path now, and `currentColor` on the tile is what lets it take the accent
   from the page's own tokens in either colour scheme. */
.wordmark .mark { width: 1.35rem; height: 1.35rem; display: block; color: var(--accent); }
.topnav { margin-left: auto; display: flex; align-items: center; gap: 0.25rem; }
.topnav a {
  color: var(--ink-muted); text-decoration: none;
  font-size: 0.875rem; padding: 0.45rem 0.7rem; border-radius: 7px;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}
.topnav a:hover { color: var(--ink); background: var(--hairline); }
.topnav a[aria-current] { color: var(--ink); }
/* THE ONE LINK ON THIS PAGE THAT SHOULD LOOK LIKE A DECISION. Everything else
   in the bar is navigation; this is the thing the page is for. */
.topnav .cta {
  background: var(--accent); color: var(--accent-ink);
  padding: 0.45rem 0.95rem; font-weight: 550;
}
.topnav .cta:hover { background: var(--accent-strong); color: var(--accent-ink); }
@media (max-width: 34rem) {
  .topnav .hide-sm { display: none; }
}

/* ═══ Buttons ══════════════════════════════════════════════════════════════ */

.btn {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font: inherit; font-weight: 550; font-size: 0.9375rem;
  padding: 0.72em 1.35em; border-radius: 999px; border: 1px solid transparent;
  text-decoration: none; cursor: pointer;
  transition: transform 0.12s var(--ease), box-shadow 0.12s var(--ease),
              background 0.15s var(--ease), border-color 0.15s var(--ease);
}
.btn-primary {
  background: var(--accent); color: var(--accent-ink);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--accent) 26%, transparent);
}
.btn-primary:hover {
  background: var(--accent-strong);
  transform: translateY(-1px);
  box-shadow: 0 10px 26px color-mix(in srgb, var(--accent) 34%, transparent);
}
.btn-ghost { border-color: var(--hairline-strong); color: var(--ink); }
.btn-ghost:hover { background: var(--raised); border-color: var(--ink-faint); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ═══ The hero ═════════════════════════════════════════════════════════════
 *
 * THE DEMO IS THE HERO. It used to be a section a third of the way down, under
 * a masthead that described it — so the first thing a visitor met was a claim,
 * and the thing that substantiates the claim was below the fold. The live
 * element is now the first interactive thing on the page, which is also the
 * shortest possible answer to "what does it actually do".
 */
/* A SINGLE WASH AT THE TOP OF THE PAGE, and nothing else on the site has one.
   It is the accent at thirteen percent over the paper — enough to lift the
   field off the background, not enough to be a colour.

   ON THE BODY AND NOT ON THE HERO, which is not a tidying. The top bar is
   sticky and translucent, so a wash that began where the hero begins drew a
   hard horizontal seam along the bottom of the bar: two different backgrounds
   meeting at a line. Starting it above everything puts the bar inside it.

   Behind the content and pointer-transparent, so it can never eat a click on
   the search box. */
body::before {
  content: '';
  position: absolute; inset: 0 0 auto; height: 62rem;
  background:
    radial-gradient(52% 40% at 18% 14%, var(--glow), transparent 72%),
    radial-gradient(42% 34% at 84% 4%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 72%);
  pointer-events: none; z-index: 0;
}
body > * { position: relative; z-index: 1; }

.hero { padding-block: clamp(2.5rem, 7vw, 4.5rem) clamp(2.5rem, 6vw, 4rem); }
.hero h1 { max-width: 15ch; }
.hero .lead { max-width: 42ch; font-size: clamp(1.08rem, 1.7vw, 1.2rem); margin-bottom: 1.5rem; }

/* ── THE PROMISE, AND THE THING THAT SUBSTANTIATES IT, SIDE BY SIDE ────────
   Stacked, the live parser was below the fold on a laptop — so the first screen
   was a claim, and the demo that makes the claim checkable was a scroll away.
   The columns are uneven on purpose: the object is the wider of the two,
   because it is the one that has to be read rather than skimmed. */
.hero-grid { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 64rem) {
  .hero-grid { grid-template-columns: 0.92fr 1.08fr; gap: 3.5rem; align-items: center; }
  .hero { padding-block: clamp(3rem, 6vw, 5.5rem) clamp(3rem, 6vw, 5rem); }
}
.hero-demo > construe-search { margin-bottom: 1rem; }

/* The two buttons under the lead. A hero with no button is a page that assumes
   everybody scrolls. */
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-bottom: 1.6rem; }

/* The three facts under the field. Not a feature list: they are the three
   things somebody is quietly checking while they decide whether to type. */
.trust {
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem;
  margin: 1.25rem 0 0; padding: 0; list-style: none;
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.02em;
  color: var(--ink-faint);
}
.trust li { display: flex; align-items: center; gap: 0.45rem; }
.trust li::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent); flex: 0 0 auto;
}

/* ═══ Panels: the response, code, anything in a box ════════════════════════ */

figure { margin: 0; }
figcaption {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 0.5rem;
  display: flex; align-items: center; gap: 0.5rem;
}
.out, .code {
  font-family: var(--mono); font-size: 0.8125rem; line-height: 1.55;
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: 1rem 1.1rem; margin: 0;
  overflow-x: auto;
  box-shadow: var(--shadow);
}
.code { white-space: pre; }
/* Tall enough that the object reads as an object rather than as a snippet,
   short enough that the page below it is not a rumour. */
.response .out { min-height: 15rem; max-height: 26rem; overflow: auto; }

/* The response, syntax-coloured. Keys, strings, numbers and literals get a
   class each and nothing is rearranged — a demo that reformatted the object
   would be a demo of the reformatting. */
.out .k { color: var(--accent); }
.out .s { color: var(--ink); }
.out .n, .out .w { color: var(--deny); }

.badge {
  font-family: var(--mono); font-size: 0.6875rem; padding: 0.12rem 0.45rem;
  border-radius: 999px; background: var(--accent-bg); color: var(--accent);
  text-transform: none; letter-spacing: 0; white-space: nowrap;
}
.badge.quiet { background: var(--hairline); color: var(--ink-muted); }
/* AN EMPTY BADGE IS NOT A SMALL BADGE. Before the first parse there is no
   coverage to report, and the span still drew its padding and its background —
   a stray chip beside the caption, on the state every visitor sees first. */
.badge:empty { display: none; }
.badge.warn { background: color-mix(in srgb, var(--deny) 16%, transparent); color: var(--deny); }
.note { font-size: 0.8125rem; color: var(--ink-muted); margin: 0.6rem 0 0; min-height: 1.2rem; }

/* ═══ The elements, dressed by the host page and nothing else ══════════════
 *
 * BOTH, FROM ONE RULE. They open the same custom properties on purpose — a page
 * that has themed one of our elements has themed both — so listing them together
 * is what keeps the search box and the drop zone looking like one product.
 */
construe-search, construe-extract {
  --construe-radius: 12px;
  --construe-border: var(--hairline-strong);
  --construe-bg: var(--raised);
  --construe-fg: var(--ink);
  --construe-muted: var(--ink-muted);
  --construe-accent: var(--accent);
  --construe-accent-bg: var(--accent-bg);
  --construe-danger: var(--deny);
  --construe-size: 1.0625rem;
  --construe-shadow: var(--shadow-lift);
  display: block;
}
/* The field is the largest thing on the page above the fold, so it is set at
   reading size rather than at control size — somebody types a sentence into it,
   not a keyword. */
construe-search::part(field) { padding: 1rem 1.15rem; box-shadow: var(--shadow); }
construe-search::part(row) { padding: 0.5rem 0.65rem; }
/* The default magnifier, in the page's own faint ink rather than the element's.
   `::part(icon)` is how a page restyles what it did not have to supply. */
construe-search::part(icon) { color: var(--ink-faint); }
/* THE ZONE FILLS ITS HOST, so a host given a column's height passes it on. The
   element sizes to its content by default, which is right for a form and wrong
   beside a panel. */
construe-extract::part(zone) { padding: 1.4rem 1.3rem; height: 100%; }

/* ── AND THE PAGE HAS TO RESTORE `hidden`, HAVING JUST BROKEN IT ───────────
   The elements each carry `:host([hidden]) { display: none }`, because the UA
   rule that implements the attribute is outranked by their own `:host` display.
   A DOCUMENT rule outranks both — so the `display: block` above silently undid
   it again, on the one page that hides an element: the catalogue demo shows the
   search box or the drop zone depending on the workflow's kind, and without this
   it showed a text field above every extraction demo. */
construe-search[hidden], construe-extract[hidden] { display: none; }

/* ═══ The containment story ════════════════════════════════════════════════
 *
 * THE ONE IDEA THAT IS NOT OBVIOUS AND IS THE WHOLE PRODUCT, so it gets a
 * picture rather than a paragraph: two chips and two arrows saying that a jar of
 * cashew butter IS a spread and HAS cashews, and that the two edges point
 * different ways.
 */
/* A PANEL IS THE PAGE'S ONE BOX. Cards everywhere would be a page of boxes; one
   raised surface per section, holding the evidence, is what makes the evidence
   read as a separate thing from the sentence beside it. */
.panel {
  padding: 1.6rem 1.75rem;
  background: var(--raised); border: 1px solid var(--hairline);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
.panel .keys { margin: 0; gap: 0.75rem 1.25rem; }
.panel .keys dd { font-size: 1rem; }

.graph {
  display: grid; gap: 0.9rem;
  padding: 1.4rem 1.5rem;
  background: var(--raised); border: 1px solid var(--hairline);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
.graph-row { display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap; font-size: 0.9375rem; }
.graph-edge {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-faint);
  border: 1px dashed var(--hairline-strong); border-radius: 999px;
  padding: 0.1rem 0.55rem;
}
.chip {
  font-family: var(--mono); font-size: 0.8125rem;
  padding: 0.25rem 0.6rem; border-radius: 999px;
  background: var(--accent-bg); color: var(--accent); white-space: nowrap;
}
.chip.deny { background: color-mix(in srgb, var(--deny) 14%, transparent); color: var(--deny); }
.chip.plain { background: var(--hairline); color: var(--ink); }
.graph-note { margin: 0.4rem 0 0; font-size: 0.875rem; color: var(--ink-muted); }

/* ═══ Numbers ══════════════════════════════════════════════════════════════ */

.figures {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1.5rem 2rem;
}
.figures div { border-top: 1px solid var(--hairline); padding-top: 0.9rem; }
.figures b {
  display: block; font-family: var(--serif); font-weight: 500;
  font-size: clamp(1.9rem, 4vw, 2.6rem); line-height: 1; letter-spacing: -0.03em;
  color: var(--ink); margin-bottom: 0.35rem;
}
.figures span { font-size: 0.875rem; color: var(--ink-muted); }

/* ═══ Feature columns ══════════════════════════════════════════════════════
   THREE COLUMNS, NO CARDS. Boxing each paragraph added three borders, three
   backgrounds and three radii to say something the column gap already says. */
.points {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 2rem 2.25rem;
}
.points article { border-top: 1px solid var(--hairline); padding-top: 1.1rem; }
.points p { margin: 0; font-size: 0.9375rem; color: var(--ink-muted); }
.points em { color: var(--ink); font-style: italic; }
.points .ico { color: var(--accent); display: block; margin-bottom: 0.7rem; }

/* The four keys, as a legend rather than an essay. Two columns of key-and-gloss
   so the eye reads down the mono column and stops at the one it wants. */
.keys {
  display: grid; grid-template-columns: auto 1fr; gap: 0.45rem 1.1rem;
  margin: 1.5rem 0 0; align-items: baseline;
}
.keys dt { margin: 0; }
.keys dd { margin: 0; font-size: 0.9375rem; color: var(--ink-muted); }
.keys code { font-family: var(--mono); font-size: 0.8125rem; color: var(--accent); white-space: nowrap; }

/* The one sentence the four lines above are in service of. */
.punch { margin: 1.4rem 0 0; font-size: 1.0625rem; color: var(--ink); font-weight: 500; }

/* Annotation chips, used by the demo pages. */
.tok {
  font-family: var(--mono); font-size: 0.8125rem; padding: 0.15rem 0.4rem;
  border-radius: 3px; margin-right: 0.3rem; white-space: nowrap;
  border: 1px solid transparent;
}
.tok.atom { background: var(--accent-bg); color: var(--accent); }
.tok.operator { background: color-mix(in srgb, var(--deny) 14%, transparent); color: var(--deny); font-weight: 600; }
.tok.comparator, .tok.unit, .tok.number { background: var(--hairline); color: var(--ink); }
.tok.unknown { border: 1px dashed var(--deny); color: var(--deny); }
.tok.noise, .tok.boundary { color: var(--ink-faint); }
.tok .role { font-size: 0.7em; opacity: 0.75; margin-left: 0.35rem; text-transform: uppercase; letter-spacing: 0.06em; }

/* ═══ Two demos, two sections ══════════════════════════════════════════════ */
.demo > .eyebrow { margin-bottom: 0.9rem; }
.demo > h2 { margin-top: 0; }
/* A DEMO IS TWO COLUMNS WHERE THERE IS ROOM. The input and what comes out of it
   are one thought, and stacking them puts a scroll between the two halves of it
   — on a laptop, the object was always below the fold from the field. */
.demo-split { display: grid; gap: 1.75rem; }
@media (min-width: 60rem) {
  /* ── THE TWO HALVES ARE THE SAME HEIGHT, WHICH THEY WERE NOT ─────────────
     `align-items: start` left the drop zone — a single row of text and an icon,
     about seven rems tall — floating at the top of a column beside a response
     panel of twenty, with three hundred pixels of nothing under it. It read as
     a mistake because it looked like one.

     STRETCHING IT IS ALSO THE BETTER TARGET. The interaction this element is
     for is dragging a file onto it, and the size of the thing you are aiming at
     is most of how easy that is. */
  .demo-split { grid-template-columns: 1fr 1fr; gap: 2rem; align-items: stretch; }
  .demo-split > .response .out { min-height: 20rem; }
  .demo-split > construe-extract { height: 100%; }
}


/* ═══ The waitlist ═════════════════════════════════════════════════════════
   Signups are shut on the deployment itself, so this is the only way in and it
   should look like a considered one rather than a fallback.

   ITS COLOURS COME FROM THE TOKENS NOW. Every value here was a hex literal —
   `#5a6b73`, `#c9d6db`, `#fff`, `#1c5a76` — so in dark mode the only form on
   the site rendered as a white box with pale grey labels on a near-black page. */
.waitlist { display: grid; gap: 0.45rem; max-width: 34rem; }
/* IN THE RIGHT-HAND COLUMN IT IS THE PANEL, so it gets the surface the other
   sections' evidence has rather than sitting bare on the band. */
@media (min-width: 62rem) {
  .split > .waitlist {
    max-width: none; padding: 1.75rem;
    background: var(--raised); border: 1px solid var(--hairline);
    border-radius: var(--radius-lg); box-shadow: var(--shadow);
  }
}
.waitlist label { font-size: 0.85rem; color: var(--ink-muted); }
.waitlist input, .waitlist textarea {
  font: inherit; padding: 0.65rem 0.8rem;
  border: 1px solid var(--hairline-strong); border-radius: var(--radius);
  background: var(--raised); color: var(--ink);
}
.waitlist input::placeholder, .waitlist textarea::placeholder { color: var(--ink-faint); }
/* THE PROPOSAL IS THE FIELD WE READ, so it gets room and is resizable — but
   only vertically: a horizontal drag pulls it out of the column it lives in. */
.waitlist textarea { resize: vertical; min-height: 6.5rem; line-height: 1.55; }
.waitlist input:focus-visible, .waitlist textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}
.waitlist button {
  font: inherit; font-weight: 550; margin-top: 0.5rem; justify-self: start;
  padding: 0.72em 1.35em; border: 0; border-radius: 999px;
  background: var(--accent); color: var(--accent-ink); cursor: pointer;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--accent) 26%, transparent);
  transition: background 0.15s var(--ease), transform 0.12s var(--ease);
}
.waitlist button:hover { background: var(--accent-strong); transform: translateY(-1px); }
.waitlist button[disabled] { opacity: 0.6; cursor: default; transform: none; }
.waitlist .note { margin: 0.3rem 0 0; min-height: 1.2em; }

/* ═══ Prose, for the docs and the legal pages ══════════════════════════════
 *
 * A NARROWER MEASURE THAN THE PAGE. Sixty rems is right for a landing page
 * whose widest thing is a two-column demo, and much too wide for a paragraph:
 * the eye loses the start of the next line somewhere past about 75 characters.
 */
.prose { max-width: 42rem; }
/* SMALLER THAN A SECTION HEADING ON THE LANDING PAGE, and for the opposite
   reason. There, a heading is one of six and has a whole screen to itself; here
   there are eleven of them down one scroll, and set at display size they shout
   over the prose they are organising. */
.prose h2 { margin-top: 3rem; max-width: none; font-size: clamp(1.35rem, 2.2vw, 1.6rem); }
.prose h3 { margin-top: 2rem; font-size: 1.0625rem; }
.prose h2:first-child, .prose h3:first-child { margin-top: 0; }
.prose p, .prose li { color: var(--ink-muted); }
.prose p { margin: 0 0 1rem; }
.prose strong { color: var(--ink); font-weight: 600; }
.prose ul, .prose ol { margin: 0 0 1.15rem; padding-left: 1.2rem; }
.prose li { margin-bottom: 0.4rem; }
.prose code {
  font-family: var(--mono); font-size: 0.85em;
  background: var(--hairline); color: var(--ink);
  padding: 0.1rem 0.35rem; border-radius: 4px;
}
.prose .code, .prose .out { margin: 0 0 1.25rem; }
.prose .code code { background: none; padding: 0; font-size: inherit; }
.prose table { width: 100%; border-collapse: collapse; margin: 0 0 1.25rem; font-size: 0.9375rem; }
.prose th, .prose td { text-align: left; padding: 0.55rem 0.7rem; border-bottom: 1px solid var(--hairline); }
.prose th { font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.09em; text-transform: uppercase; color: var(--ink-faint); font-weight: 500; }
.prose td { color: var(--ink-muted); vertical-align: top; }
.prose dl { margin: 0 0 1.25rem; }
.prose dt { font-family: var(--mono); font-size: 0.8125rem; color: var(--accent); margin-top: 0.8rem; }
.prose dd { margin: 0.15rem 0 0; color: var(--ink-muted); font-size: 0.9375rem; }

/* A SECTION LINK YOU CANNOT SEE UNTIL YOU WANT IT. `llms.txt` publishes these
   anchors and the docs are meant to be quoted section by section, so every
   heading has to be addressable — without putting a hash beside forty
   headings. */
.anchor { color: var(--ink-faint); text-decoration: none; margin-left: 0.4rem; opacity: 0; transition: opacity 0.15s; }
h2:hover .anchor, h3:hover .anchor, .anchor:focus-visible { opacity: 1; }

/* ── The docs' own two columns ───────────────────────────────────────────── */
.docs-layout { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 62rem) {
  .docs-layout { grid-template-columns: 13rem 1fr; gap: 3.5rem; }
  .toc { position: sticky; top: 5rem; }
}
.toc { font-size: 0.875rem; }
.toc h2 {
  font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-faint); margin: 0 0 0.7rem; font-weight: 500;
}
.toc ul { list-style: none; margin: 0 0 1.5rem; padding: 0; }
.toc li { margin: 0; }
.toc a {
  display: block; padding: 0.25rem 0; color: var(--ink-muted);
  text-decoration: none; border-left: 2px solid var(--hairline); padding-left: 0.75rem;
}
.toc a:hover { color: var(--ink); border-left-color: var(--accent); }

/* ═══ Footer ═══════════════════════════════════════════════════════════════ */

.site-foot {
  border-top: 1px solid var(--hairline);
  margin-top: 0;
  padding-block: 2.5rem 3rem;
  font-size: 0.875rem;
}
.site-foot .wrap { display: flex; flex-direction: column; gap: 0.55rem; }
.site-foot nav { display: flex; flex-wrap: wrap; gap: 0.35rem 0.75rem; align-items: center; }
.site-foot nav a {
  color: var(--ink-muted); text-decoration: none;
  /* At 14px type this was a 51×19 target — under the 24px WCAG 2.2 minimum, on
     the page most likely to be read on a phone. Padded rather than enlarged, so
     the type stays the size the footer wants. */
  display: inline-flex; align-items: center; min-height: 24px;
}
.site-foot nav a:hover { color: var(--ink); text-decoration: underline; }
/* A SEPARATOR BELONGS TO THE LINK BEFORE IT. The receipt-extraction link is
   hidden until the demo tier confirms that workflow is published (`demo.js`),
   and without this the footer carried a dangling middot on every deployment
   that does not publish it. */
.site-foot .sep { color: var(--ink-faint); }
.site-foot nav a[hidden] + .sep { display: none; }
.site-foot .tagline { color: var(--ink-faint); }
/* WHO STANDS BEHIND IT, which is what somebody weighing up a small service is
   actually asking. Ownership rather than authorship. */
.site-by { font-size: 0.85rem; }
.site-by a { color: var(--ink-muted); text-decoration: underline; text-underline-offset: 3px; }
.site-by a:hover { color: var(--ink); }

/* ═══ Skip link ════════════════════════════════════════════════════════════
   The first tab stop on every page. Off-screen until focused, which is the only
   way it is both present for a keyboard and absent for everybody else. */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-ink);
  padding: 0.6rem 1rem; border-radius: 0 0 var(--radius) 0; text-decoration: none;
}
.skip:focus { left: 0; }
