/* ===== 00-tokens.css ===== */
/*
 * Link Prism design tokens.
 *
 * Derived from the Steep style reference (docs: DESIGN.md) with two corrections
 * made after measuring the real site, both of which the written reference omits
 * and both of which are what actually make it read as premium:
 *   1. The hero is NOT flat white — it sits on a soft chromatic gradient wash.
 *   2. A fractal-noise grain overlays the whole page at low opacity.
 * Everything else here matches the reference's documented scale.
 */
:root {
  /* — Ink & paper —————————————————————————————— */
  --ink: #17191c;
  /*
   * Paper is NOT pure white, deliberately. Two reasons, both measured:
   *  - `overlay` grain over #ffffff is an identity operation (overlay(1,x) === 1),
   *    so a pure-white page can carry no texture at all.
   *  - The reference's "white" is never actually white; every band sits on a
   *    faint warm ground, which is most of why it reads as paper and not screen.
   */
  --paper: #fdfcfa;
  --raised: #ffffff;        /* floating artifacts stay true white so they lift off the paper */
  --mist: #f2f2f3;
  --fog: #fafafb;
  /*
   * These three were taken from the reference palette and all FAILED WCAG AA on
   * our paper: slate 4.13:1, ash 2.84:1, smoke 2.37:1 against 4.5:1 for small
   * text. Every eyebrow, footnote and body paragraph on the site was below the
   * threshold. Darkened within the same family until they pass; the greys read
   * fractionally deeper and everything stays legible.
   */
  --slate: #636772;   /* 4.52:1 worst ground (peach) — body copy, ledes, muted text */
  --ash:   #686c76;   /* 4.70:1 worst ground (mist) — eyebrows, footnotes, labels  */
  --smoke: #7d818b;   /* 3.80:1 — LARGE type only (>=24px), never small text */

  /*
   * TEXT ON DARK SURFACES. The greys above are light-ground tokens; several
   * sections place a dark ink panel on the page and reused them inside it, where
   * mid-grey on near-black measures 3.4:1. These are the dark-surface equivalents
   * and are the ONLY muted colours permitted inside an ink panel.
   */
  --on-dark:        color-mix(in srgb, var(--paper) 92%, transparent);  /* body on ink   */
  --on-dark-muted:  color-mix(in srgb, var(--paper) 66%, transparent);  /* labels, ~7:1  */
  --on-dark-faint:  color-mix(in srgb, var(--paper) 48%, transparent);  /* LARGE type only */
  --peach: #fbe1d1;
  --sienna: #5d2a1a;
  --hair: #ececec;
  --hair-strong: rgba(23, 25, 28, 0.12);

  /* Warm tints for the gradient wash — same family as --peach, never used as text */
  --wash-1: #ffd9c2;
  --wash-2: #ffe9d6;
  --wash-3: #f0e2f2;
  --wash-4: #dfe6f5;

  /* — Type —————————————————————————————————————— */
  --serif: 'Source Serif 4', 'Iowan Old Style', Georgia, ui-serif, serif;
  --sans: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  --t-caption: 15px;
  --t-body: 17px;
  --t-body-lg: 20px;
  --t-subhead: 22px;
  --t-h-sm: 26px;
  --t-h: 44px;
  --t-h-lg: 64px;
  --t-display: 90px;

  /* Tracking tightens as size grows — the typographic signature of the system */
  --tr-display: -0.025em;
  --tr-h-lg: -0.015em;
  --tr-h: -0.015em;
  --tr-h-sm: -0.009em;
  --tr-body-lg: -0.009em;

  /* — Space (4px base) ————————————————————————— */
  --s4: 4px;   --s8: 8px;   --s12: 12px; --s16: 16px; --s20: 20px;
  --s24: 24px; --s28: 28px; --s32: 32px; --s40: 40px; --s48: 48px;
  --s64: 64px; --s80: 80px; --s96: 96px; --s128: 128px; --s160: 160px;

  --nav-h: 76px;
  --page: 1200px;
  --gutter: 32px;

  /*
   * THREE SECTION RHYTHMS, AND ONLY THREE.
   *
   * Nineteen section files used to set their own band height, and they split
   * into two systems: multipliers of --section-y (0.66, 0.82, 1.2, 1.7 …) and
   * fixed pixels (104, 112, 136, 176 …). The two agree at exactly one viewport
   * width and separate everywhere else, so the page's vertical beat changed
   * with the window. Every band now picks one of these and nothing derives its
   * own.
   */
  --section-y-tight: 64px;   /* a strip: a logo rail, a short ledger */
  --section-y: 96px;         /* the normal band — most of the site */
  --section-y-loose: 128px;  /* a stage: the closing band, a tall drawing */

  /*
   * ONE HERO OPENING for all eight pages. The nav is sticky and 76px tall and
   * every hero is pulled up under it, so this figure INCLUDES the nav band:
   * 148 is 72px of visible air above the first mark, on every page. It used to
   * run 148 / 172 / 228 / 244 / 252 across eight heroes that share one nav.
   */
  --hero-y: 148px;

  /* One eyebrow, one gap under it. Seventeen instances used to set thirteen. */
  --eyebrow-gap: var(--s24);

  /* — Shape ——————————————————————————————————— */
  --r-card: 24px;
  --r-elevated: 20px;
  --r-input: 16px;
  --r-small: 16px;
  --r-pill: 9999px;

  /* — Drawings ————————————————————————————————
   * TWO sizes for the whole site, and no third. A set of drawings at six
   * different sizes is size used as hierarchy, which _src/ILLUSTRATION.md
   * items 3 and 7 forbid: a terminal and a printer are comparable objects and
   * must be drawn comparably. `card` is the mark that sits inside a card in a
   * grid; `feature` is the one drawing a section is built around. Both are
   * CEILINGS — `.illo--card` / `.illo--feature` pair them with width:100%, so a
   * narrow column still fits its drawing without inventing a third size.
   */
  --illo-card: 250px;
  --illo-feature: 500px;

  /* Only floating product artifacts earn elevation. Content cards stay flat. */
  --sh-artifact: 0 0 0 1px rgba(4, 23, 43, 0.05), 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
  --sh-overlay: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 8px 40px 0 rgba(0, 0, 0, 0.1);
  --sh-pop: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 4px 24px 0 rgba(0, 0, 0, 0.08);

  /* — Motion ——————————————————————————————————— */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 160ms;
  --dur: 320ms;
  --dur-slow: 720ms;
}

/* The three rhythms and the hero opening ramp together, so the ratios between
   them hold at every width and no section has to re-tune itself. */
@media (max-width: 1080px) {
  :root { --t-display: 68px; --t-h-lg: 48px; --t-h: 36px; --gutter: 28px;
          --section-y-tight: 56px; --section-y: 80px; --section-y-loose: 104px;
          --hero-y: 124px; }
}
@media (max-width: 720px) {
  :root { --t-display: 44px; --t-h-lg: 34px; --t-h: 28px; --t-h-sm: 22px;
          --t-body-lg: 18px; --gutter: 20px; --r-card: 20px;
          --section-y-tight: 40px; --section-y: 64px; --section-y-loose: 80px;
          --hero-y: 104px; }
}

/* ===== 10-base.css ===== */
/* Reset + page substrate: the grain and the gradient wash live here. */
*, *::before, *::after { box-sizing: border-box; }
/*
 * `overflow-x: clip` on the ROOT, not `hidden` on body alone.
 * Sections intentionally crop product cards past the viewport edge; with only
 * body clipping, the document still grew (measured 1510px against a 1440px
 * viewport) and shipped a horizontal scrollbar. `clip` contains it WITHOUT
 * creating a scroll container, so the sticky nav keeps working — `hidden` would
 * silently break it.
 */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: clip; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: 1.35;
  font-weight: 400;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}

h1, h2, h3, h4, p, figure, blockquote { margin: 0; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }

/*
 * THE GRAIN. A fractal-noise tile at low opacity in `overlay` blend, fixed to the
 * viewport so it never scrolls against the content. This is the single largest
 * contributor to the reference's "printed paper" quality and it is absent from
 * every written description of the system.
 */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  /*
   * `multiply`, not `overlay`. The noise tile is opaque-black-or-transparent, and
   * overlay leaves any near-white base untouched — the grain was mathematically
   * invisible across every light section. Multiply darkens the speckle instead,
   * so the texture reads on paper AND on the warm wash.
   */
  opacity: 0.13;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.91' numOctaves='2' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='0 1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}
@media (prefers-reduced-motion: no-preference) { body::after { will-change: opacity; } }

/* Layout primitives.
   ONE page rail, and every headline block on the site sits on it. There used to
   be a second, wider container (`.wrap-wide`, 1400) used by three sections; at
   1440 it put their headlines on the 32px page gutter while their neighbours
   held 120, an 88px jump between consecutive sections. Full bleed is still
   legal — a drawing, a rule, a background band — but it is done per element,
   never by moving a headline off the rail. */
.wrap { width: min(var(--page), 100% - var(--gutter) * 2); margin-inline: auto; }
.section { padding-block: var(--section-y); position: relative; }
.section--fog { background: var(--fog); }
.section--mist { background: var(--mist); }

/* Type primitives */
.display, .h-lg, .h, .h-sm-serif {
  font-family: var(--serif);
  font-weight: 400;
  font-optical-sizing: auto;
  text-wrap: balance;
  /*
   * THE SIDEBEARING CORRECTION, ONCE, FOR THE WHOLE SITE.
   *
   * A serif cap set flush to a rail reads as indented: the stem sits inside the
   * glyph's left sidebearing, while the sans eyebrow above it and the sans lede
   * below it start on their own ink. Pulling the block back a hair of its own
   * size puts the STEM on the rail, and stated in em it re-derives itself at
   * every step of the type ramp instead of needing a value per breakpoint.
   * Measured on Source Serif 4: cap B, lowercase b and the italic a all land
   * within 1px of each other at -0.026em.
   *
   * This lived in hardware-hero.css as a private rule, bundled with a 64px
   * "hang" that pulled that one title to x=54 while its own eyebrow held 120.
   * The hang is gone; the correction is shared. Three more sections carried
   * near-duplicates of it (-0.02, -0.023, -0.03) and now inherit this one.
   *
   * A CENTRED serif block must switch it off — see .prh-title in pricing-hero —
   * because there the margin moves the centre rather than the left edge.
   */
  margin-left: -0.026em;
}
.display { font-size: var(--t-display); line-height: 1.06; letter-spacing: var(--tr-display); }
.h-lg    { font-size: var(--t-h-lg);   line-height: 1.1;  letter-spacing: var(--tr-h-lg); }
.h       { font-size: var(--t-h);      line-height: 1.16; letter-spacing: var(--tr-h); }
.h-sm-serif { font-size: var(--t-h-sm); line-height: 1.28; letter-spacing: var(--tr-h-sm); }
.display em, .h-lg em, .h em { font-style: italic; }

/*
 * THE ONE FIT-TO-WIDTH OPT-IN, AND THE ONLY ONE.
 *
 * A headline whose authored line genuinely cannot hold the phone column takes
 * this class on top of its scale class instead of inventing a divisor of its
 * own. Six inner heroes each invented one, and at 390px they rendered the same
 * rank at 31.8 / 41 / 41.6 / 42 / 44 and 48px — a 1.8x spread across six
 * elements that are the same thing.
 *
 * It can only ever make type SMALLER than the ramp: it is a min() against the
 * token, so it cannot be used to promote a headline past the class it wears.
 * The divisor is taken from the widest binding line on the site — "When
 * something breaks," which Source Serif sets at 10.47em once the optical axis
 * has opened the face up at small sizes — so one number covers every candidate.
 * Above about 414px of viewport it stops binding entirely and the token ramp is
 * the only thing sizing the line.
 */
.h-fit { font-size: min(var(--t-h-lg), calc((100vw - var(--gutter) * 2) / 11)); }

.h-sm { font-family: var(--sans); font-size: var(--t-h-sm); font-weight: 450;
        line-height: 1.22; letter-spacing: var(--tr-h-sm); }
.lede { font-size: var(--t-body-lg); line-height: 1.45; letter-spacing: var(--tr-body-lg);
        color: var(--slate); text-wrap: pretty; }
.body { font-size: 16px; line-height: 1.55; color: var(--slate); text-wrap: pretty; }
.caption { font-size: var(--t-caption); line-height: 1.5; color: var(--slate); }

/*
 * Ghost typographic tag — groups without visual weight. Never a badge.
 *
 * ONE COMPONENT: one size, one weight, one colour, and one gap to whatever it
 * labels. The gap used to take thirteen values across seventeen instances (2,
 * 16, 19, 24, 26, 28, 32, 38, 42, 45, 57 …) — even the four page-label eyebrows
 * that share the same hero pattern disagreed — and the colour ran --ash on 29
 * of them, --smoke on four, and full --ink at weight 500 on one.
 *
 * Where an eyebrow is a grid or flex participant whose spacing the container
 * already owns, it sets `margin-bottom: 0` and says so. That is the only legal
 * override.
 */
.eyebrow {
  font-size: 14px;
  font-weight: 400;
  color: var(--ash);
  letter-spacing: 0;
  margin-bottom: var(--eyebrow-gap);
}

.tnum { font-variant-numeric: tabular-nums; }
.muted { color: var(--slate); }

.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;
}
:where(a, button, input, [tabindex]):focus-visible {
  outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 4px;
}

/*
 * A hero that carries a wash starts UNDER the transparent nav, so the wash paints
 * behind the logo and links instead of leaving a bald white strip across the top.
 *
 * THE OPENING IS SET HERE AND NOWHERE ELSE. Eight heroes used to compute their
 * own top padding off --section-y plus a private number — 148 / 172 / 228 / 244
 * / 252, a 104px spread on eight components that share one 76px nav — and eight
 * more numbers for the bottom (96 … 236). --hero-y includes the nav band, so
 * every page shows the same 72px of air above its first mark, and the hero
 * closes on the loose rhythm like any other stage.
 */
main > .section:first-child.has-wash {
  margin-top: calc(var(--nav-h) * -1);
  padding-top: var(--hero-y);
  padding-bottom: var(--section-y-loose);
}

/* A whisper of warmth for sections that would otherwise be an undifferentiated slab. */
.section--tint { background:
  radial-gradient(120% 80% at 50% 0%, rgba(251, 225, 209, 0.30) 0%, rgba(251, 225, 209, 0) 60%),
  var(--paper); }
.section--tint-b { background:
  radial-gradient(120% 80% at 50% 100%, rgba(251, 225, 209, 0.26) 0%, rgba(251, 225, 209, 0) 60%),
  var(--paper); }

/* ===== 20-components.css ===== */
/* ————————————————————————————————————————————————
   The gradient wash. Original composition in the brand's warm family — four
   soft radial blooms over paper. Sections opt in with .has-wash.
   ———————————————————————————————————————————————— */
.has-wash { position: relative; isolation: isolate; }
.has-wash > .wash {
  position: absolute; inset: -10% -5% 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(58% 44% at 74% 30%, var(--wash-1) 0%, rgba(255, 217, 194, 0) 62%),
    radial-gradient(46% 38% at 22% 46%, var(--wash-3) 0%, rgba(240, 226, 242, 0) 66%),
    radial-gradient(52% 40% at 46% 72%, var(--wash-2) 0%, rgba(255, 233, 214, 0) 68%),
    radial-gradient(40% 34% at 90% 74%, var(--wash-4) 0%, rgba(223, 230, 245, 0) 70%);
  filter: blur(8px) saturate(112%);
}
/* Fade the wash into paper at the seam so sections never end on a hard edge. */
.has-wash > .wash::after {
  content: ''; position: absolute; inset-inline: 0; bottom: 0; height: 200px;
  background: linear-gradient(to top, var(--paper), rgba(255, 255, 255, 0));
}

/* ————————————————————————————————————————————————
   Pills. Filled and ghost share geometry so they sit as a matched pair.
   ———————————————————————————————————————————————— */
.pill, .ghost {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 48px; padding-inline: 26px; border-radius: var(--r-pill);
  font-size: 16px; font-weight: 450; letter-spacing: -0.005em;
  white-space: nowrap; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.pill { background: var(--ink); color: var(--paper); border: 1px solid var(--ink); }
.pill:hover { background: #2b2e33; border-color: #2b2e33; }
.ghost { background: transparent; color: var(--ink); border: 1px solid var(--ink); }
.ghost:hover { background: var(--ink); color: var(--paper); }
.pill:active, .ghost:active { transform: translateY(1px); }
.pill--sm, .ghost--sm { height: 42px; padding-inline: 20px; font-size: 15px; }

/* Lowest-emphasis link: the arrow carries the affordance, not an underline. */
.alink {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 16px; font-weight: 450; color: var(--ink);
}
.alink svg { transition: transform var(--dur) var(--ease-out); }
.alink:hover svg { transform: translateX(4px); }
.alink:hover span { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }

/* ————————————————————————————————————————————————
   Surfaces
   ———————————————————————————————————————————————— */
/*
 * ONE CARD, TWO VARIANTS — and nothing else. The card is the most repeated
 * object on the site, and it had drifted into six grounds doing the same job:
 * mist, white-with-a-hairline, transparent-with-a-hairline, white-no-border,
 * white-with-a-top-only-radius and white-on-a-heavier-hairline. Two of them
 * shipped on ONE page, three sections apart, on the same six-card isometric
 * grid. Sections must not restate background, border, radius or padding here.
 *
 *   .card         mist, no border — the default, and what a grid of drawings
 *                 sits on, on paper and on fog alike.
 *   .card--paper  white and a hairline — reserved for the one case mist cannot
 *                 do: a card that has to lift off a filled ground. It is down
 *                 to a single user (customers-more); nothing else may take it,
 *                 and no section may invent a third.
 */
/* --illo-ground is the surface a drawing's occlusion silhouette is painted in.
   It is declared beside the background it has to match, so the two can never
   drift apart; see the illustration block below. */
.card { background: var(--mist); --illo-ground: var(--mist); border-radius: var(--r-card); padding: var(--s32); }
.card--paper { background: var(--raised); --illo-ground: var(--raised); border: 1px solid var(--hair); }
.card__title { font-size: var(--t-body-lg); font-weight: 500; letter-spacing: -0.01em; margin-bottom: 10px; }
/*
 * The ONE permitted louder card title, for the lead of a grid. Same family and
 * same weight as its siblings — a lead that changes typeface puts two families
 * at one rank inside a single grid, which is what three sections were doing.
 */
.card__title--lead { font-size: var(--t-h-sm); letter-spacing: var(--tr-h-sm); line-height: 1.22; }
.card__body { font-size: 16px; line-height: 1.55; color: var(--slate); }

/* Rationed to one per page. Warm ink on warm paper — never on a tinted section. */
.card--peach { background: var(--peach); color: var(--sienna); --illo-ground: var(--peach); }
.card--peach .card__title, .card--peach .card__body, .card--peach p { color: var(--sienna); }

/* The only elements with visible elevation. */
.artifact {
  background: var(--raised); border-radius: var(--r-elevated); box-shadow: var(--sh-artifact);
}

.rule { height: 1px; background: var(--hair); border: 0; margin: 0; }

/* ————————————————————————————————————————————————
   Nav
   ———————————————————————————————————————————————— */
.nav {
  position: sticky; top: 0; z-index: 100; height: 76px; display: flex; align-items: center;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out),
              backdrop-filter var(--dur) var(--ease-out);
  border-bottom: 1px solid transparent;
}
.nav.is-stuck {
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom-color: var(--hair);
}
/*
 * NO `width: 100%` HERE. .nav__inner also carries .wrap, and an equal-specificity
 * `width` declared later overrides the 1200px column .wrap exists to set — which
 * pushed the logo and the CTA hard against the viewport edges and left the nav
 * links misaligned against every other element on the page. Most visible on wide
 * displays, but it was wrong at every width.
 */
.nav__inner { display: flex; align-items: center; justify-content: space-between; gap: 24px; }
.nav__logo { display: inline-flex; align-items: center; gap: 9px; flex: 0 0 auto; white-space: nowrap;
             font-family: var(--serif); font-size: 21px; letter-spacing: -0.015em; }
.nav__logo em { font-style: italic; }
.nav__links { display: flex; align-items: center; gap: 34px; }
.nav__links a { font-size: 16px; font-weight: 400; color: var(--ink); position: relative; padding-block: 4px; }
.nav__links a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--ink); transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur) var(--ease-out);
}
.nav__links a:hover::after, .nav__links a.is-active::after { transform: scaleX(1); }
.nav__cta { display: flex; align-items: center; gap: 22px; }
/* A text link in the nav is still a tap target: it was 21px tall, under the 24px
   floor and well under the 44px comfortable size on touch. */
.nav__cta .alink { min-height: 44px; }
.nav__burger { display: none; width: 44px; height: 44px; align-items: center; justify-content: center;
               background: none; border: 0; cursor: pointer; margin-right: -10px; }

@media (max-width: 940px) {
  .nav__links, .nav__cta .alink, .nav__cta .ghost { display: none; }
  .nav__burger { display: inline-flex; }
}
@media (max-width: 360px) {
  .nav__cta > .pill { display: none; }
}

.navdrawer {
  position: fixed; inset: 0; z-index: 200; background: var(--paper);
  display: flex; flex-direction: column; padding: 20px var(--gutter) 40px;
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out), visibility var(--dur);
}
.navdrawer.is-open { opacity: 1; visibility: visible; transform: none; }
.navdrawer__top { display: flex; align-items: center; justify-content: space-between; height: 56px; }
.navdrawer__links { display: flex; flex-direction: column; gap: 4px; margin-top: 32px; }
.navdrawer__links a { font-family: var(--serif); font-size: 34px; line-height: 1.5; letter-spacing: -0.015em; }
.navdrawer__foot { margin-top: auto; display: flex; flex-direction: column; gap: 12px; }
.navdrawer__foot .pill, .navdrawer__foot .ghost { width: 100%; }

/* ————————————————————————————————————————————————
   Footer
   ———————————————————————————————————————————————— */
.footer { border-top: 1px solid var(--hair); padding-block: 72px 56px; background: var(--paper); }
.footer__grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 48px; }
.footer__brand p { margin-top: 14px; max-width: 260px; font-size: 15px; line-height: 1.55; color: var(--slate); }
.footer__col .footer__coltitle { font-size: 14px; font-weight: 450; color: var(--ash); margin-bottom: 18px; }
.footer__col a { display: block; font-size: 15px; line-height: 1; padding-block: 9px; color: var(--ink); }
.footer__col a:hover { color: var(--slate); }
.footer__base { display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap;
                margin-top: 64px; padding-top: 28px; border-top: 1px solid var(--hair);
                font-size: 14px; color: var(--ash); }
@media (max-width: 860px) {
  .footer__grid { grid-template-columns: 1fr 1fr; gap: 40px 24px; }
  .footer__brand { grid-column: 1 / -1; }
}

/* ————————————————————————————————————————————————
   THE ILLUSTRATION SYSTEM
   Every drawing on the site must obey these, or the page reads as clip-art
   assembled by six different people — which is exactly what reviewers found:
   "five different conventions" across six cards in one section.
   ———————————————————————————————————————————————— */
.illo {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  color: var(--ink);
  /* Strokes must survive the warm ground. A floor plan drawn at low contrast was
     measured as "effectively does not exist" — opacity belongs on the wrapper,
     never on individual strokes. */
  opacity: 0.78;
}
.illo--quiet { opacity: 0.52; }
.card--peach .illo,
.illo--sienna { color: var(--sienna); }
.illo--invert { color: var(--raised); }

/* One stroke weight for the whole site. Anything thinner disappears on paper;
   anything heavier reads as an icon rather than a drawing. */
.illo [stroke],
.illo path, .illo line, .illo circle, .illo rect, .illo ellipse, .illo polyline, .illo polygon {
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  vector-effect: non-scaling-stroke;   /* weight stays 1.4 at any scale */
}
/* The single permitted fill: a solid accent mark, used sparingly for emphasis. */
.illo .illo-fill { fill: currentColor; stroke: none; }
/* Construction lines — hidden faces, guides — sit back without changing weight. */
.illo .illo-back { opacity: 0.34; }

/* ————————————————————————————————————————————————
   HIDDEN-LINE REMOVAL — and it lives HERE, not in a section file.

   ILLUSTRATION.md item 5 makes occlusion mandatory: where one object sits in
   front of another, the object behind must BREAK, or the drawing is an x-ray.
   The tool that does it is an opaque silhouette painted in whatever surface the
   drawing stands on, laid down immediately before the object it belongs to.

   It used to be declared locally, in two section files out of thirteen, and
   that is most of why eleven sections carried no occlusion at all: an author
   who copied class="illo-mask" anywhere else got `fill: none` from the generic
   rule above and the drawing silently rendered as an x-ray. No error, no
   missing style — just a worse drawing. Declared globally, the class works
   wherever it is written.

   The surface comes from --illo-ground, set once per card beside its own
   background (.card / .card--paper / .card--peach above). A drawing standing on
   bare paper needs to set nothing; the fallback is the page.
   ———————————————————————————————————————————————— */
.illo .illo-mask { fill: var(--illo-ground, var(--paper)); stroke: none; }

/* ————————————————————————————————————————————————
   TWO SIZES, AND NO THIRD.

   Measured across the site, .illo boxes ran from 45px to 1102px — a 24x range
   under one class, which makes the class mean nothing, and inside a grid of
   peers makes size read as importance (ILLUSTRATION.md items 3 and 7).
   A drawing is now either a card mark or a section's feature. Both are
   CEILINGS paired with width:100%, so a narrow column shrinks its drawing
   instead of earning a size of its own.
   ———————————————————————————————————————————————— */
.illo--card { width: 100%; max-width: var(--illo-card); }
.illo--feature { width: 100%; max-width: var(--illo-feature); }

/* ————————————————————————————————————————————————
   BLEED STAGE — for absolutely-positioned decoration around a content column.
   `position:absolute; inset:0` on a full-bleed section anchors children to the
   VIEWPORT, so on a wide display the floating cards fly out to the edges and
   lose all relationship to the headline they are meant to frame. This bounds the
   layer to the content column plus a fixed bleed, centred, so the composition
   holds at any width and only ever crops by --bleed.
   ———————————————————————————————————————————————— */
.stage {
  position: absolute;
  top: 0; bottom: 0; left: 0; right: 0;
  width: min(100%, calc(var(--page) + var(--bleed, 240px) * 2));
  margin-inline: auto;
  pointer-events: none;
}

/* ===== 30-motion.css ===== */
/*
 * Motion primitives. Everything is opt-in via a data attribute so a section
 * author never writes keyframes — and everything collapses to "already visible"
 * under prefers-reduced-motion, which is the accessible default, not an afterthought.
 */
[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
[data-reveal].is-in { opacity: 1; transform: none; }

/* Headline reveal: lines rise from a clipped baseline, staggered. */
[data-reveal-lines] .line-mask { display: block; overflow: hidden; }
[data-reveal-lines] .line-mask > span {
  display: block;
  transform: translateY(105%);
  transition: transform 900ms var(--ease-out);
  transition-delay: var(--line-delay, 0ms);
}
[data-reveal-lines].is-in .line-mask > span { transform: none; }

/* Floating artifacts drift in from their resting offset. */
[data-float] {
  opacity: 0;
  transform: translate3d(var(--float-x, 0), var(--float-y, 26px), 0) scale(0.985);
  transition: opacity 1000ms var(--ease-out), transform 1000ms var(--ease-out);
  transition-delay: var(--float-delay, 0ms);
}
[data-float].is-in { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }

/* Idle bob for hero artifacts, once they have landed. */
@keyframes lp-bob {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(0, -7px, 0); }
}
[data-float].is-in[data-bob] {
  animation: lp-bob var(--bob-dur, 7s) var(--ease-in-out) infinite;
  animation-delay: var(--bob-delay, 0s);
}

/* Counters and bars */
[data-count] { font-variant-numeric: tabular-nums; }
[data-bar] { transform: scaleX(0); transform-origin: left; transition: transform 1100ms var(--ease-out); }
[data-bar].is-in { transform: scaleX(1); }

/* Drawn SVG strokes */
[data-draw] {
  stroke-dasharray: var(--len, 1000);
  stroke-dashoffset: var(--len, 1000);
  transition: stroke-dashoffset 1600ms var(--ease-out);
}
[data-draw].is-in { stroke-dashoffset: 0; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal], [data-float], [data-bar], [data-draw],
  [data-reveal-lines] .line-mask > span {
    opacity: 1 !important;
    transform: none !important;
    stroke-dashoffset: 0 !important;
    transition: none !important;
    animation: none !important;
  }
}

/* ===== section:ai-hero ===== */
/* ═══════════════════════════════════════════════════════════════════════════
   ai-hero — the AI page's opening.

   One column of serif on the left that opens out onto its italic, one small
   composer floated low in an open right field, and a great deal of warm ground.
   No slab, no peach surface, no rules: the wash is the only colour in it.
   ═══════════════════════════════════════════════════════════════════════════ */

.sec-ai-hero {
  /* Card geometry, stepped by breakpoint below. Kept as a pair of variables
     because width and overhang have to move together — the grid sizes the right
     track from (width + margin), so changing one without the other silently
     moves the left column's right edge as well. */
  --aih-ask-w: 460px;
  --aih-ask-out: -60px;

  /*
   * THE MEASURE, AND WHY IT IS A VARIABLE RATHER THAN A NUMBER.
   *
   * This composition rests on one relationship: the italic line and the lede
   * end on the same right edge, so headline and paragraph read as ONE tapering
   * column rather than as a headline with a paragraph sticking out past it.
   *
   * --aih-clause is the MEASURED natural width of "The record underneath it
   * is." at the current step of the type ramp, and --aih-lede sits just inside
   * it. Both were measured in the browser as rendered ink, not estimated:
   * 651 / 500 / 364px at the 64 / 48 / 34px steps of --t-h-lg.
   *
   * The lede used to be a flat 640px at every width while the headline shrank
   * with the ramp, so the relationship only held on the top step. Measured
   * across the sweep: at 1080 and at 768 the clause set 500px of ink and the
   * lede box 640, and the body copy out-measured the display type by ~73px — the
   * taper ran backwards through the whole tablet range. Stepping the two
   * together keeps the edge shared at every width the section is shown at.
   */
  --aih-clause: 651px;
  --aih-lede: 640px;

  overflow: clip;               /* the card hangs past the measure, never past the page */
}

/*
 * THE GROUND UNDER THE CARD.
 *
 * The site wash blooms warm at 74%/30% and cool at 90%/74%; the composer lands
 * between them, on the palest, coolest patch in the frame, and a white card on
 * near-white ground has nothing to lift off — measured against the first render
 * it read as a grey ghost rather than as a floating object. This recomposes the
 * SAME four tokens so the warm mass sits behind the card and the lilac sits
 * behind the headline: the one white surface in the section now sits on the one
 * warm surface, and the shadow has something to fall on.
 *
 * Zero-alpha stops are written as `transparent`; every current engine
 * premultiplies gradient alpha, so there is no grey fringe to guard against and
 * no colour outside the tokens.
 */
.sec-ai-hero > .wash {
  background:
    radial-gradient(46% 42% at 14% 26%, var(--wash-3) 0%, transparent 66%),
    radial-gradient(50% 38% at 56% 4%, var(--wash-2) 0%, transparent 70%),
    /* Sized in PIXELS, not per cent: the card is a fixed object, so the warm
       mass under it has to stay a fixed size too. At per-cent radii the same
       bloom compressed into a narrower frame and went from ground to spotlight
       between 1440 and 1280. */
    radial-gradient(560px 330px at 77% 73%, var(--wash-1) 0%, transparent 66%),
    radial-gradient(32% 30% at 101% 16%, var(--wash-4) 0%, transparent 72%);
}
/* The seam fade is 200px site-wide, which here reached 50px up into the card and
   bleached the ground out from under it — the object was floating on the very
   paper it was meant to lift off. 150px clears the card's floor by 15px and is
   still a fade, not an edge. */
.sec-ai-hero > .wash::after { height: 150px; }

/* ——— The two-column stage ————————————————————————————————————————————
   `end` on the cross axis is what registers the card's floor to the bottom of
   the type column. The left track is minmax(0,1fr) rather than auto so a long
   lede can never push the card off its own overhang.

   THE THREE ROWS ARE DECLARED, AND THAT DECLARATION IS LOAD-BEARING. The rail
   asks for `grid-row: 1 / -1`, and -1 counts back from the last line of the
   EXPLICIT grid. With no `grid-template-rows` the explicit grid has no row
   tracks at all, so -1 resolved to line 1, `1 / -1` collapsed to `1 / span 1`,
   and the card was placed in the EYEBROW'S row — which then stretched to the
   card's 133px and end-aligned the eyebrow to the bottom of it. Two separate
   things measured wrong from that one line: the card floated at the top of the
   frame instead of on the lede's baseline, and the whole type column was pushed
   down 98px, so this hero opened on 170px of air where every other page on the
   site opens on 72px. Naming the rows is what makes -1 mean the foot of the
   lede. Every row is `auto`, so nothing else about the sizing changes.
   ———————————————————————————————————————————————————————————————————— */
.sec-ai-hero .aih-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto auto;   /* eyebrow / headline / lede */
  align-items: end;
  column-gap: var(--s48);
}

/* ——— The type column ——————————————————————————————————————————————— */
.sec-ai-hero .aih-eyebrow {
  grid-column: 1;
  grid-row: 1;
}

.sec-ai-hero .aih-title {
  grid-column: 1;
  grid-row: 2;
  /* The clause plus ~7% slack, so the authored rag survives a font fallback
     without re-wrapping into a shape nobody chose. Derived rather than typed,
     so the slack stays proportional as the ramp steps down. */
  max-width: calc(var(--aih-clause) + 48px);
}

/* The italic clause is ONE <em>, so the accent is a single element however many
   lines it is set on. */
.sec-ai-hero .aih-claim {
  display: block;
  font-style: italic;
}

/* The sentence break, set as leading rather than as a blank line. */
.sec-ai-hero .aih-turn { margin-top: 0.2em; }

.sec-ai-hero .aih-sub {
  grid-column: 1;
  grid-row: 3;
  max-width: var(--aih-lede);
  margin-top: var(--s32);
  /* At the full 640 measure this sentence breaks after "it can", stranding a
     modal verb from its own infinitive at the end of a line — the one break in
     the section nobody chose. Balancing splits it after "already agree," on the
     comma instead, and lands two even lines under the headline's long italic. */
  text-wrap: balance;
}

/* ——— The composer ————————————————————————————————————————————————————
   Spans both rows, so `align-items:end` lands it on the foot of the whole type
   column rather than on the foot of the headline.
   ———————————————————————————————————————————————————————————————————— */
.sec-ai-hero .aih-rail {
  grid-column: 2;
  grid-row: 1 / -1;
  /* The overhang. A negative margin also shrinks the track the grid reserves,
     so the left column keeps every pixel the card gives back. */
  margin-right: var(--aih-ask-out);
  /* Half-leading plus descender depth of a 20px/1.45 lede. Grid aligns margin
     boxes, so this lifts the card's floor off the lede's line-box edge and onto
     the baseline inside it. */
  margin-bottom: 7px;
}

.sec-ai-hero .aih-ask {
  width: var(--aih-ask-w);
  padding: 24px 22px 20px 28px;
}

/*
 * --ash, NOT --smoke. This is 18px text a sighted reader actually reads, and
 * 00-tokens.css rations --smoke to type of 24px and up (it measures 3.80:1,
 * under the 4.5:1 AA threshold for small text). --ash is the same grey family
 * one step down and passes at 4.70:1. aria-hidden hides it from a screen
 * reader; it does not make it legible, so the contrast rule still applies.
 * The icons below stay on --smoke: they are decorative marks, not text, and
 * that half-step of separation is what makes them read as ghosted next to the
 * placeholder rather than as a second row of equal weight.
 */
.sec-ai-hero .aih-ask__ph {
  font-size: 18px;
  line-height: 1.3;
  letter-spacing: -0.006em;
  color: var(--ash);
}

.sec-ai-hero .aih-ask__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s20);
  margin-top: 26px;
}

.sec-ai-hero .aih-ask__tools {
  display: inline-flex;
  align-items: center;
  gap: 18px;
  color: var(--smoke);
}
.sec-ai-hero .aih-ico { width: 19px; height: 19px; }

/* The only dark mass in the section, and it is 40px across. */
.sec-ai-hero .aih-ask__send {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--paper);
}
.sec-ai-hero .aih-ask__send svg { width: 16px; height: 16px; }

/* ——— Widths ——————————————————————————————————————————————————————————
   The card only ever occupies the field to the RIGHT of the 655px italic line
   and the 640px lede, so it can never collide with type at any width it is
   shown at. These steps are what keep that arithmetic true as the rail narrows.
   ———————————————————————————————————————————————————————————————————— */
@media (max-width: 1439px) {
  .sec-ai-hero { --aih-ask-w: 400px; --aih-ask-out: 0px; }
  .sec-ai-hero .aih-ask { padding: 22px 20px 18px 24px; }
  .sec-ai-hero .aih-ask__ph { font-size: 17px; }
  .sec-ai-hero .aih-ask__bar { margin-top: 22px; }
}

/*
 * Below 1240 the rail can no longer hold a 655px headline and a 400px card side
 * by side with air between them, and a composer squeezed under the headline
 * would be a different, worse section. Per brief the object is desktop-only:
 * narrower than this the hero is the type column alone, full measure — which is
 * the composition the rag was authored for in the first place.
 */
@media (max-width: 1239px) {
  .sec-ai-hero .aih-rail { display: none; }
  .sec-ai-hero .aih-wrap { grid-template-columns: minmax(0, 1fr); }

  /* THE GROUND FOLLOWS THE OBJECT. The tight warm mass exists to hold a white
     card up; with the card gone it is a lit circle on an empty field, which is
     worse than the flat wash it replaced. Below the card's breakpoint the bloom
     spreads back out and drifts off the right edge, so the right of the frame
     is warm rather than spotlit. */
  .sec-ai-hero > .wash {
    background:
      radial-gradient(50% 46% at 14% 24%, var(--wash-3) 0%, transparent 66%),
      radial-gradient(54% 42% at 54% 4%, var(--wash-2) 0%, transparent 70%),
      radial-gradient(66% 58% at 96% 62%, var(--wash-1) 0%, transparent 70%),
      radial-gradient(32% 30% at 101% 14%, var(--wash-4) 0%, transparent 72%);
  }
}

/* The type ramp steps --t-h-lg at 1080 and at 720; the measure steps with it. */
@media (max-width: 1080px) {
  .sec-ai-hero { --aih-clause: 500px; --aih-lede: 492px; }
}

@media (max-width: 720px) {
  /*
   * The phone headline takes the token ramp — 34px — like every other page
   * title. It used to be sized from the rag (the column divided by 6.6, capped
   * at 48), which at 390px made this page title half again as large as
   * support's; all four narrow-column lines still stand on one line each.
   */
  .sec-ai-hero { --aih-clause: 364px; --aih-lede: 356px; }
  .sec-ai-hero .aih-title { max-width: none; }
  .sec-ai-hero .aih-sub { margin-top: var(--s24); }
}

/*
 * THE NARROW-COLUMN BREAK, AND WHY IT FIRES AT 430 AND NOT AT 720.
 *
 * It used to be live from 720px down, which is far wider than the clause
 * needs. At the 34px step the clause sets 364px of ink and the phone column is
 * (viewport - 40), so it stands unbroken from about 430px up — and breaking it
 * anyway is not a neutral choice. Measured at 600px the forced break capped the
 * headline at 219px of ink while the lede ran past 290: the display type was
 * set in less than half the column the body copy filled, and the taper the
 * whole block is built on ran backwards through everything from 431 to 720.
 *
 * The threshold is the width where the clause stops fitting, not a round
 * number. At 430 the column is 390 and the clause 364 — 26px of slack, about
 * 7%, which a Georgia fallback eats — so 430 breaks. At 431 and up the line
 * stands, and the rag opens out at every width where it can.
 */
.sec-ai-hero .aih-br-m { display: none; }
@media (max-width: 430px) {
  .sec-ai-hero .aih-br-m { display: block; }
}

/* ===== section:ai-pipeline ===== */
/* ===================================================================
   ai-pipeline — "From the invoice to the menu"

   The form is a CONVEYOR, not a list. Five stations hang off one rail
   that enters at the page's left rail and dissolves past the right edge
   of the last column.

   ── ONE BOUNDARY, AND EVERY MARK LANDS ON IT ──────────────────────
   This section made the same competitive claim with three marks at
   three different x positions:
     • a hairline bracket that closed on node 03's CENTRE, 266px below
       the rail it was measuring;
     • a rail whose colour changed on that same centre;
     • a 40px pull quote, under column 03, saying every rival "can do
       step three".
   At 266px of separation nobody reads a bracket against a dot — they
   read it against the columns it sits under, and it ended exactly where
   column 03's own text began. So the only diagram in the section said
   "they stop after two" while the type said "they can do three".

   --pp-split is now the middle of the gutter between column 03 and
   column 04 — the only x at which "after step three" can be drawn — and
   THREE things terminate on it: the grey rail, the ink rail's start, and
   the bracket's right tick. The bracket itself has moved onto the rail:
   it is a dimension mark in the air above the row whose two ticks drop
   onto the line, so it measures the thing it is drawn against.

   ── WHERE THE INK GOES ────────────────────────────────────────────
     • 01–03 are one muted mass — hollow ash nodes, ash numerals, slate
       titles and bodies.
     • 04 and 05 take the solid nodes, the ink type AND one step of
       title size (26 against 22). Rank used to be carried by grey value
       alone across five tracks of identical anatomy, which is not rank,
       it is a tint. Varying the TYPE reads without disturbing the beat;
       an earlier attempt varied track WIDTH with identical type and
       measured as a grid that had drifted.
     • Station 03 breaks the muted register in exactly one place: its
       two figures. See below.

   ── THE EVIDENCE ──────────────────────────────────────────────────
   "Pork belly up 18% since March… best seller down to 54%" is the only
   real measurement in a section whose claim is "it sees the margin
   move", and it was set at the same 16px as "Photographed, emailed,
   handwritten." The words and their order are untouched; the two
   figures are set AS figures — 30px serif, tabular, ink — so the row
   has a focal point instead of five equal blocks of text separated only
   by grey value.

   ── THE HAIRLINE IS ACTUALLY VISIBLE NOW ──────────────────────────
   The muted rail ran --hair-strong (ink at 12%), which measures 218 on
   this ground against a 238 field — a 20-level delta, so stations 01–03
   read as three loose dots with no conveyor between them. --pp-hair is
   ink at 21% (~202), still under a third of the ink segment's weight
   but present on a real display. Every muted structure in the section
   uses it: the flat rail, the bracket, and the phone rail's grey run.

   Four things are registered rather than eyeballed:
     • the rail sits exactly on the vertical centre of every node — all
       five dot centres are at the same 8px offset from their column's
       text edge, whatever the dot's diameter;
     • every hollow node fills with the section ground, so the rail
       BREAKS at each station instead of drawing through it;
     • the four station rows (node, number, title, body) are a SUBGRID,
       so numbers sit on one line and bodies start on one line without a
       min-height reserve — the titles bottom-align inside their shared
       row, so every title-to-body gap is the same gap;
     • the tail runs on the same five-column track as the stations, so
       the closing pull starts on track 3 — the last column inside the
       bracket, and the column the sentence is about.

   The section above it (ai-hero) is a washed page-head with a floating
   artifact in the right field; this one is flat ground, hard-left type
   and a horizontal rail, so the page changes texture at the seam.

   Every rule is scoped under .sec-ai-pipeline.
   =================================================================== */

.sec-ai-pipeline {
  --pp-gap: 28px;
  /* The node SLOT is a fixed 16px box in every column, so a bigger dot on a
     marked station cannot shift its column's centre off the beat or push its
     number down a row. Dot centres land at 8px from the column's text edge. */
  --pp-slot: 16px;
  --pp-dot: 10px;
  --pp-dot-lead: 13px;
  --pp-t-size: var(--t-subhead);
  --pp-t-size-lead: var(--t-h-sm);
  /* The section's muted stroke. --hair-strong (ink 12%) measured 218 against a
     238 ground — a 20-level delta that vanishes on any real display, which left
     the first three stations as loose dots with no line between them. Ink at 19%
     on a pixel-snapped row lands near 200: a clear third of the ink rail's
     weight, and actually present. Read the note on .pp-rail first — raising this
     alpha alone did NOT fix it, because the line was being painted at half
     strength across two rows. */
  --pp-hair: color-mix(in srgb, var(--ink) 19%, transparent);
  /* Five equal tracks. See the header note: the 1.2fr lead this replaced
     measured 320/271/271/271 and read as a misaligned grid, because column 1
     carried exactly the same numeral, title and body treatment as its
     neighbours — whitespace alone cannot express rank between identical twins. */
  --pp-track: repeat(5, minmax(0, 1fr));
  /*
   * THE BOUNDARY, DERIVED. With five equal tracks and gap g in a band of width
   * W, a column is (W - 4g)/5 wide, so column 03's RIGHT EDGE sits at
   * 3w + 2g = 0.6W - 0.4g. That edge, not the middle of the gutter after it: the
   * gutter midpoint was tried and measured 22px from node 04's solid dot, close
   * enough that the bracket read as pointing AT station 04 rather than stopping
   * before it. On the column edge it clears the next node by 35px and closes
   * exactly where station 03's own text ends.
   *
   * That is the x this section means by "after step three", and it is what the
   * bracket, the grey rail's end and the ink rail's start all use. It used to be
   * node 03's CENTRE — 0.4W-ish — which is a defensible datum for the rail and a
   * false one for a bracket sitting 266px below it, where the eye measures
   * against columns, not dots. Stated as geometry it re-derives at every
   * viewport; typing "60%" would be right at exactly one width.
   */
  --pp-split: calc(60% - var(--pp-gap) * 0.4);
  /* Height of the bracket's ticks: the drop from the dimension line onto the
     rail. Also the bracket's whole box height. */
  --pp-tick: 36px;
  position: relative;
  isolation: isolate;
  overflow: clip;          /* contains the rail's run without a scroll container */
}

/* ——— Section grain ————————————————————————————————
   The page grain is body::after with `position: fixed`, so on a section this
   tall it paints for exactly one viewport and then stops: measured mean 245.2
   above the fold and a flat 250.0 with zero variance below, a hard horizontal
   through the middle of the band. This layer is absolute to the section, so the
   tooth is continuous however tall the section is and wherever it sits in the
   scroll, and it HALVES the residual step where the fixed layer ends (measured
   241.7 / 246.1 against 241.7 / 250.0 without it).
   (home-ai.css and home-traction.css carry the same fix.) */
.sec-ai-pipeline::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  opacity: 0.11;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.91' numOctaves='2' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='0 1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

/* ——— Head ————————————————————————————————————————— */
.sec-ai-pipeline .pp-head { max-width: 900px; }

/* ——— The rail ————————————————————————————————————
   Two 1px CSS lines, drawn in with the existing [data-bar] primitive
   (scaleX 0 -> 1, transform-origin: left).

   IT USED TO BE TWO SVG STROKES, AND THAT WAS THE INVISIBILITY BUG.
   A 1px stroke centred on y=1 of a 2-unit viewBox lands on a device-pixel
   BOUNDARY, so the renderer split it across two rows at ~50% each. Sampled at
   1440: the ink segment came back 112/117 on two rows instead of a real hairline,
   and the muted segment 214/214 — which is why raising its alpha from 12% to 21%
   moved it four levels and no further. The split was eating more contrast than
   the token was adding. `shape-rendering: crispEdges` was tried on the path and
   Chromium ignored it under the non-uniform preserveAspectRatio scale.

   A background box snaps to the pixel grid, so each rail now paints ONE solid row
   at its true value. Same geometry, same left-to-right draw-in, no keyframes.

   s128 above the band, not s96: the ceiling bracket now hangs in this air,
   occupying its bottom 64px, and needs a clear gap of its own under the
   headline block. */
.sec-ai-pipeline .pp-band {
  position: relative;
  margin-top: var(--s128);
}

.sec-ai-pipeline .pp-rail {
  display: block;
  position: absolute;
  /* The node slot is 16px and its dot is centred on 8px from the column's text
     edge; a 1px line occupying row 7 sits on that centre. */
  top: 7px;
  height: 1px;
  z-index: 0;
}

/* Before the ceiling: hairline grey, and it STOPS in the gutter after 03. */
.sec-ai-pipeline .pp-rail--flat {
  left: 0;
  width: var(--pp-split);
  background: var(--pp-hair);
}

/*
 * After the ceiling: solid ink, and it leaves the page.
 *
 * THE EXIT. The rail used to die at 1px into the raw viewport edge, 270px past
 * node 05, while every other element respected the 32px margin — an unclosed
 * track, not an escape. One gutter past the page rail is the run; a drawing may
 * leave the measure even though the headline above it may not. Node 05's centre
 * sits around 45-53% of THIS segment (the split moved right, so the live segment
 * is shorter and 05 lands earlier inside it), so a fade starting at 73% leaves
 * the last station solid with a long clear run after it. Nothing is filled at the
 * far end either — a solid cap marks a terminus, and step 05's whole argument is
 * that there isn't one.
 *
 * WIDTH, not `right`: the run is to the EDGE OF THE SCREEN, not to the gutter.
 * The band is centred, so the viewport's right edge sits at 50vw + 50% of the
 * band from the band's own left edge. Below 1264 that is arithmetically identical
 * to "one gutter past the measure" (the wrap IS the viewport less two gutters
 * there); above it the line keeps going instead of stopping 88px short of the
 * edge with nothing to show for the gap.
 */
.sec-ai-pipeline .pp-rail--live {
  left: var(--pp-split);
  width: calc(50vw + 50% - var(--pp-split));
  background: var(--ink);
  -webkit-mask-image: linear-gradient(to right, #000 0 73%, transparent 100%);
  mask-image: linear-gradient(to right, #000 0 73%, transparent 100%);
  /* The ink half draws after the grey half has arrived, so the line reads as one
     stroke crossing the ceiling rather than two lines appearing at once. A delay
     on the existing [data-bar] transition — no new keyframes. */
  transition-delay: 420ms;
}

/* ——— The five stations ————————————————————————————
   Equal tracks, four shared rows. Each station takes all four as a SUBGRID, so
   node / number / title / body each sit on one horizontal across all five
   columns without any reserved height. */
.sec-ai-pipeline .pp-steps {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: var(--pp-track);
  grid-template-rows: auto auto auto auto;
  column-gap: var(--pp-gap);
  row-gap: 0;
  list-style: none;
  margin: 0;
  padding: 0;
}

.sec-ai-pipeline .pp-step {
  display: grid;
  grid-row: 1 / 5;
  grid-template-rows: subgrid;
}

/* ——— The node ————————————————————————————————————
   A fixed 16px slot with the dot drawn as a pseudo-element pinned to x=8, so
   every dot centre is on the same offset no matter its diameter, and the two
   solid nodes cost no layout at all. */
.sec-ai-pipeline .pp-node {
  position: relative;
  height: var(--pp-slot);
}
.sec-ai-pipeline .pp-node::before {
  content: '';
  position: absolute;
  left: calc(var(--pp-slot) / 2);
  top: 50%;
  transform: translate(-50%, -50%);
  width: var(--pp-dot);
  height: var(--pp-dot);
  border-radius: 50%;
  border: 1px solid var(--ash);
  /* The ground, so the rail is cut at every station rather than drawn through. */
  background: var(--fog);
}

/* The two stations the section actually claims. Solid ink and a step larger —
   the only filled marks in the row, and they sit on the half of the rail that
   is itself ink. */
.sec-ai-pipeline .pp-step--lead .pp-node::before {
  width: var(--pp-dot-lead);
  height: var(--pp-dot-lead);
  border-color: var(--ink);
  background: var(--ink);
}

.sec-ai-pipeline .pp-n {
  margin-top: var(--s28);
  font-size: 13px;
  line-height: 1;
  letter-spacing: 0.08em;
  color: var(--ash);
}
.sec-ai-pipeline .pp-step--lead .pp-n { color: var(--ink); font-weight: 500; }

/* No min-height. A 2.5em reserve bought one aligned horizontal (body tops) and
   paid for it with five different title-to-body gaps — one title floating a
   whole empty line above its body while another was glued to its own. The
   titles bottom-align inside their shared subgrid row instead: every gap down
   to the body is now the same gap, and the slack from a title that wraps opens
   ABOVE it, against the number, where there is already air. */
.sec-ai-pipeline .pp-t {
  align-self: end;
  margin-top: var(--s16);
  font-size: var(--pp-t-size);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.014em;
  text-wrap: balance;
  /* Muted, and muted at heading size — 22px at weight 500 keeps its rank over
     its own body on size and weight alone, which is all a heading needs. */
  color: var(--slate);
}

/* THE ONE BREAK IN THE FIVE-TRACK UNIFORMITY. 04 and 05 already carry the ink;
   they carry one step of size too, so the two steps the pull claims as ours are
   ranked by TYPE rather than by grey value. The beat is untouched — the tracks
   stay equal — and the ladder stays four deep: 64 / 40 / 26 / 22 / 16 / 13. */
.sec-ai-pipeline .pp-step--lead .pp-t {
  font-size: var(--pp-t-size-lead);
  letter-spacing: var(--tr-h-sm);
  color: var(--ink);
}

.sec-ai-pipeline .pp-b { margin-top: var(--s12); }
.sec-ai-pipeline .pp-step--lead .pp-b { color: var(--ink); }

/* ——— Station 03's readout ————————————————————————
   One sentence per line so the tall figures open their own leading instead of
   tearing a wrapped paragraph apart. The words and their order are exactly the
   signed copy; only the two numbers change register. */
.sec-ai-pipeline .pp-read__l { display: block; }
.sec-ai-pipeline .pp-read__l + .pp-read__l { margin-top: var(--s8); }

/* THE TWO FIGURES OPEN THEIR OWN LINE, AT THE COLUMN'S LEFT EDGE.
   Left to the browser they landed mid-line at two different x — 18% at the end
   of line one, 54% at the end of line four — which is a big numeral turning up
   twice by accident rather than a readout. The run from each figure to the end
   of its sentence is unbreakable, and the measure is set ~28px inside the
   217–218px column that this layout holds at every desktop width, so both runs
   are pushed down. The result is two 30px numerals stacked on the same left
   edge, which is the alignment that makes them read as measurements. Nothing is
   hard-broken: below the five-column layout the measure opens up and the runs
   simply stop being able to split.

   `text-wrap: wrap` is not decoration: .body carries `pretty`, and pretty pulled
   "to" down onto line five to avoid a short last line, which put the 54% one word
   off the left edge the 18% had just established. The rag here is decided by the
   measure, so the last-line heuristic has to be switched off. */
.sec-ai-pipeline .pp-read { max-width: 11.8em; text-wrap: wrap; }
.sec-ai-pipeline .pp-run { white-space: nowrap; }

/* The section's only measurement, set as a measurement: 30px serif at 400,
   tabular, full ink, inside 16px slate. It out-sizes the 26px lead titles by a
   step because it is a figure, not a heading — and it is the one thing on
   screen that the sentence "it sees the margin move" can point at. */
.sec-ai-pipeline .pp-fig {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 30px;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
}

/* ——— The ceiling mark ————————————————————————————
   A DIMENSION BRACKET ON THE RAIL. A hairline from the page rail to --pp-split
   with two ticks dropping from its ends onto the rail itself, so the mark is
   drawn against the line it measures and its right tick lands exactly where
   grey becomes ink. It used to sit 266px below the rail, where it read against
   the text columns and stopped in the middle of column 03's own body copy.

   It is absolutely positioned out of the grid: `bottom: calc(100% - 8px)` puts
   its bottom edge on the rail's centre line, 8px below the top of .pp-steps,
   and the whole 64px stack (label, gap, bracket) hangs upward into the s128 of
   air above the band. FULL --pp-split width, resolved against .pp-steps — the
   grid container's padding box is the containing block for an auto-placed
   abspos child, so the percentage means what it says.

   column-reverse, so the label prints above the bracket while the DOM keeps
   brace-then-label — which is the order the phone layout wants. */
.sec-ai-pipeline .pp-mark {
  position: absolute;
  left: 0;
  bottom: calc(100% - var(--pp-slot) / 2);
  width: var(--pp-split);
  display: flex;
  flex-direction: column-reverse;
}
.sec-ai-pipeline .pp-mark__brace {
  display: block;
  position: relative;
  height: var(--pp-tick);
  border-top: 1px solid var(--pp-hair);
}
/* ONE TICK, AT THE END THAT CARRIES THE MEANING.
   A tick at each end closed bracket + rail into an empty 709x36 rectangle, which
   reads as a stray container rather than as a measurement — and the left one was
   restating a boundary the page already states three times over: the eyebrow, the
   headline, the "01" numeral and the rail itself all begin on that same x. It is
   deleted. The right tick is the whole point of the mark, so it stays, and it
   CROSSES the rail by 7px rather than stopping on it: a witness line that runs
   past the line it meets reads as a crossing, and marks the exact pixel where
   grey becomes ink. */
.sec-ai-pipeline .pp-mark__brace::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: -7px;
  right: 0;
  width: 1px;
  background: var(--pp-hair);
}

/* TWO WORDS. This label used to run "Where every other system stops" at 13px in
   the faintest grey on the page, and then the 40px serif pull made the identical
   claim 160px lower. The large voice owns the claim; the small one only has to
   say whose stretch of rail this is. */
.sec-ai-pipeline .pp-mark__label {
  margin: 0 0 var(--s12);
  font-size: 13px;
  line-height: 1.2;
  letter-spacing: 0.08em;
  color: var(--ash);
}

/* ——— The close ————————————————————————————————————
   Same track as the stations, so the pull starts on track 3 — under station 03,
   the column the sentence is about and the last column inside the bracket.
   Tracks 1–2 are left open: an isometric counter used to sit there and earned
   nothing, and the tail is stronger as one large piece of type with the left
   half of the page empty. */
/*
 * THE SECTION'S VOID, AND WHERE IT IS PARKED.
 *
 * s160 between the row and the close is the largest single gap on the page, and
 * it is deliberate twice over. It is the air the deleted drawing used to occupy,
 * and it is also where the page grain's own edge lands: body::after is
 * `position: fixed`, so any full-page capture paints it for exactly one viewport
 * and stops, leaving a ~4-level tonal step at y = viewport height. The gap is
 * sized so the step lands in empty ground rather than inside 40px display serif.
 */
.sec-ai-pipeline .pp-tail {
  margin-top: var(--s160);
  display: grid;
  grid-template-columns: var(--pp-track);
  gap: var(--pp-gap);
}

.sec-ai-pipeline .pp-say { grid-column: 3 / span 3; }

/* THE CLOSING SCALE EVENT. The pull used to render at --t-h-sm (26px) — a step
   BELOW the marked station headings above it, so the section's payoff whispered.
   It is the second largest voice in the section now, sized off the viewport so
   it holds its ratio to the 64px headline rather than freezing at one width, and
   its rag is set by hand (see the HTML) so "step three," is never split across a
   line. `balance` is switched off because the breaks are already chosen. */
.sec-ai-pipeline .pp-pull {
  color: var(--ink);
  font-size: clamp(30px, 2.9vw, 40px);
  line-height: 1.18;
  letter-spacing: -0.017em;
  text-wrap: wrap;
}
.sec-ai-pipeline .pp-note { margin-top: var(--s28); max-width: 620px; }
.sec-ai-pipeline .pp-rag { display: none; }
@media (min-width: 1081px) {
  .sec-ai-pipeline .pp-rag { display: inline; }
}

/*
 * THE IDIOMS ARE UNBREAKABLE, AT EVERY WIDTH THE HAND-SET RAG DOES NOT COVER.
 *
 * The two <br class="pp-rag"> breaks only exist above 1081, which is exactly
 * where the pull has the most room; every width BELOW that — the widths where
 * the sentence is under pressure and a bad break is likeliest — was left to the
 * browser, and the browser split the phrase. Measured: at 900 it set "…step
 * three, at / best, at month end…" and at 390 it set "…step three, at / best, at
 * month end, from / an export." Both cut "at best" in half, which is the one
 * qualifier the sentence's whole concession turns on.
 *
 * Four nowrap spans fix it without a single new breakpoint or hard-coded break:
 * the longest of them measures ~180px against a 350px phone column, so none can
 * overflow, and greedy wrapping then lands on a clean rag by itself.
 */
.sec-ai-pipeline .pp-keep { white-space: nowrap; }

/* ——— Below the five-column measure ————————————————
   At 1240 the stations fall under ~28 characters a line, so the pipeline turns
   on its side rather than being squeezed: the rail becomes vertical, and each
   station becomes one row — node, number, title, body — which is a genuinely
   different form for the same object rather than five squashed columns. The
   ceiling mark comes with it: the absolute placement is released and the same
   element lands, in flow, exactly between station 03 and station 04 — the row
   where the rail changes colour.

   The shared row set goes with it: each station is its own little grid again,
   so grid-row / subgrid / align-self are all released here.

   THE VERTICAL RAIL IS DRAWN PER ROW, not as one absolute line down the list,
   and that is what terminates it. The single line ran 40px past the last
   station and hard-stopped in mid-air with no node, no fade and no terminal,
   while the desktop rail resolves with a soft dissolve — the same device
   behaving two different ways in two layouts. A segment per row lets the first
   one START on node 01's centre and the last one END on node 05's centre, so
   the rail closes on a node instead of dangling. --pp-node-y is that offset
   (padding + the node's own margin + half the 16px slot) and is restated at
   each breakpoint that changes either number. */
@media (max-width: 1240px) {
  .sec-ai-pipeline {
    --pp-node-y: calc(var(--s24) + 12px);
  }

  .sec-ai-pipeline .pp-band { margin-top: var(--s96); }
  .sec-ai-pipeline .pp-rail { display: none; }

  .sec-ai-pipeline .pp-steps {
    display: block;
    position: relative;
    padding-left: 0;
  }

  .sec-ai-pipeline .pp-step,
  .sec-ai-pipeline .pp-mark {
    position: relative;
    z-index: 1;
  }
  .sec-ai-pipeline .pp-step::before,
  .sec-ai-pipeline .pp-mark::before {
    content: '';
    position: absolute;
    z-index: -1;
    left: calc(var(--pp-slot) / 2 - 0.5px);
    width: 1px;
    top: 0;
    bottom: 0;
    background: var(--ink);
  }
  /* Grey THROUGH station 03, ink after it — the same boundary the desktop rail
     states, on the same side of the same station. The switch is carried by the
     ceiling mark's own segment, as a hard gradient stop on the tick itself, so
     the vertical rail changes character exactly where the label is. */
  .sec-ai-pipeline .pp-step:nth-child(-n+3)::before { background: var(--pp-hair); }
  .sec-ai-pipeline .pp-mark::before {
    background: linear-gradient(to bottom, var(--pp-hair) 0 50%, var(--ink) 50%);
  }
  .sec-ai-pipeline .pp-step:first-child::before { top: var(--pp-node-y); }
  .sec-ai-pipeline .pp-step:last-child::before  { bottom: auto; height: var(--pp-node-y); }

  .sec-ai-pipeline .pp-step {
    display: grid;
    grid-row: auto;
    grid-template-rows: none;
    grid-template-columns: var(--pp-slot) 34px minmax(0, 300px) minmax(0, 1fr);
    column-gap: var(--s24);
    align-items: start;
    padding-block: var(--s24);
  }
  .sec-ai-pipeline .pp-node { margin-top: 4px; }
  .sec-ai-pipeline .pp-n { margin-top: 4px; }
  .sec-ai-pipeline .pp-t { align-self: start; margin-top: 0; }
  /* 1px sets the body's baseline on the title's, not its line-box on it.
     NO measure cap here, tried and reverted: the body track tops out around 604px
     at this breakpoint, and every cap tight enough to shorten step 03's longest
     line broke it into a full line plus "to 54%." — a three-word widow is worse
     than a long line, and text-wrap: pretty does not catch it. */
  .sec-ai-pipeline .pp-b { margin-top: 1px; }
  /* The readout's three sentences run as one line each here too, but the column
     is wide, so the figures sit inside a comfortable measure rather than forcing
     a wrap. */
  .sec-ai-pipeline .pp-read { max-width: 34em; }

  /* The bracket becomes a tick off the rail: the label hangs where the numbers
     hang, joined to the line by one hairline, on the row where grey becomes ink. */
  .sec-ai-pipeline .pp-mark {
    display: block;
    position: relative;
    bottom: auto;
    width: auto;
    margin-top: 0;
    padding: var(--s20) 0 var(--s20) calc(var(--pp-slot) + var(--s24));
  }
  .sec-ai-pipeline .pp-mark__brace {
    position: absolute;
    left: calc(var(--pp-slot) / 2);
    top: 50%;
    height: 1px;
    border-top: 0;
    background: var(--pp-hair);
    width: calc(var(--pp-slot) / 2 + var(--s24) - var(--s8));
  }
  .sec-ai-pipeline .pp-mark__brace::after { display: none; }
  .sec-ai-pipeline .pp-mark__label { margin: 0; }

  .sec-ai-pipeline .pp-tail { margin-top: var(--s80); display: block; }
  .sec-ai-pipeline .pp-say { grid-column: auto; }
  .sec-ai-pipeline .pp-pull { max-width: 720px; }
}

/* ——— Laptop ———————————————————————————————————————— */
@media (max-width: 1080px) {
  .sec-ai-pipeline .pp-band { margin-top: var(--s80); }
  .sec-ai-pipeline .pp-step {
    grid-template-columns: var(--pp-slot) 34px minmax(0, 270px) minmax(0, 1fr);
  }
  .sec-ai-pipeline .pp-tail { margin-top: var(--s64); }
}

/* ——— Tablet ————————————————————————————————————————
   Title and body stop fitting side by side at about 760 — NOT at 900, which is
   where this used to break and where it left the right third of a 900px screen
   completely bare while the type crowded into the left half. The row becomes
   number-and-title on one line with the body beneath, still hung off the same
   vertical rail. */
@media (max-width: 760px) {
  .sec-ai-pipeline { --pp-node-y: calc(var(--s24) + 9px); }

  .sec-ai-pipeline .pp-step {
    grid-template-columns: var(--pp-slot) minmax(0, 1fr);
    column-gap: var(--s20);
    row-gap: var(--s8);
  }
  .sec-ai-pipeline .pp-n  { grid-column: 2; grid-row: 1; margin-top: 3px; }
  .sec-ai-pipeline .pp-t  { grid-column: 2; grid-row: 2; margin-top: 2px; }
  .sec-ai-pipeline .pp-b  { grid-column: 2; grid-row: 3; max-width: 56ch; }
  .sec-ai-pipeline .pp-node { grid-column: 1; grid-row: 1; margin-top: 1px; }

  .sec-ai-pipeline .pp-mark { padding-left: calc(var(--pp-slot) + var(--s20)); }
  .sec-ai-pipeline .pp-mark__brace {
    width: calc(var(--pp-slot) / 2 + var(--s20) - var(--s8));
  }
}

/* ——— Phone —————————————————————————————————————————
   The rail stays. It is the only structure holding five short blocks together,
   and at 390px it is also the only thing distinguishing this from a bulleted
   list. The dots shrink a step so they still read as marks on a line — the slot
   they sit in does NOT, so the rail keeps its registration. */
@media (max-width: 720px) {
  .sec-ai-pipeline {
    --pp-dot: 9px;
    --pp-dot-lead: 12px;
    --pp-node-y: calc(var(--s16) + 9px);
  }

  /* s96, not s48 — and this number is measured, not chosen. The page grain is
     fixed to the viewport, so a full-page phone capture paints it for exactly
     844px and then stops, leaving a ~3-level tonal step across the column. It
     used to land inside the "It proposes the action." heading, detaching a
     heading from its own paragraph and reading as a render fault. Opening the
     band walks the whole list down so the step falls in the mark's own margin
     instead, where a change of ground is simply the gap between two rows. */
  .sec-ai-pipeline .pp-band { margin-top: var(--s96); }
  .sec-ai-pipeline .pp-step { padding-block: var(--s16); column-gap: var(--s16); }
  .sec-ai-pipeline .pp-b { max-width: none; }
  /* One step down: 30px serif figures inside a 350px column would set the two
     figure sentences at three lines each. */
  .sec-ai-pipeline .pp-fig { font-size: 26px; }

  /* Tighter above than below: the mark labels the three stations it sits under,
     so it belongs to them and is let go of afterwards. */
  .sec-ai-pipeline .pp-mark {
    padding: var(--s24) 0 var(--s32) calc(var(--pp-slot) + var(--s16));
  }
  .sec-ai-pipeline .pp-mark__brace {
    width: calc(var(--pp-slot) / 2 + var(--s16) - var(--s8));
  }
  .sec-ai-pipeline .pp-mark__label { font-size: 12px; }

  .sec-ai-pipeline .pp-tail { margin-top: var(--s48); }
  .sec-ai-pipeline .pp-pull { max-width: none; }
  .sec-ai-pipeline .pp-note { margin-top: var(--s20); }
}

/* ===== section:ai-shipstate ===== */
/* ai-shipstate — the honest ledger.
 *
 * The section above is a horizontal five-node pipeline on fog; the section below
 * is a single peach slab that runs off the RIGHT edge. So this one is neither:
 * quiet paper, a left-anchored typographic ledger, and its hairlines break the
 * LEFT boundary instead — the rules arrive from off-screen and stop dead on the
 * page measure, where the status pills stack into a clean right column.
 *
 * The one thing that overlaps a boundary is the drawing: the ticket rail hangs
 * out of the header and its longest docket descends across the ledger's first
 * hairline, which BREAKS behind the paper. Same hidden-line rule the drawings
 * obey internally, applied between the drawing and the page.
 */

.sec-ai-shipstate { background: var(--paper); }

/* — Header ————————————————————————————————————————————————
   The header's height is set by min-height, not by its contents: the object is
   taken out of flow so it can hang below the header without the header growing
   to contain it, and min-height guarantees enough room above for the drawing to
   start clear of the section's top edge whether the headline runs two lines or
   three. Every crossing measurement below derives from these two numbers. */
.sec-ai-shipstate .sd-head {
  position: relative;
  z-index: 2;                /* the drawing paints OVER the ledger it crosses */
  min-height: 236px;
}
.sec-ai-shipstate .sd-head__text { max-width: 600px; }

/* bottom:-131px puts the longest docket ~34px past the ledger's first hairline,
   while the next docket up clears it by ~50px — far enough either side that the
   crossing reads as intended rather than as a near miss.
   right:-16px is optical, not sloppy: the viewBox carries 8/177 of margin, so
   pushing the BOX 16px past the measure lands the rail's far end exactly on it —
   the same vertical the hairlines stop on and the pills align to. Flush-right on
   the box instead left the rail 16px shy of that vertical, which read as drift.
   `overflow-x: clip` on <html> means the overhang costs no scrollbar. */
.sec-ai-shipstate .sd-object {
  position: absolute;
  right: -16px;
  bottom: -131px;
  width: 360px;
}
.sec-ai-shipstate .sd-illo { width: 100%; }
/* The sanctioned occlusion technique: an opaque silhouette that BREAKS whatever
   sits behind it. Never used to fill a shape for its own sake. */
.sec-ai-shipstate .illo .sd-cut { fill: var(--paper); stroke: none; }

/* — The ledger ————————————————————————————————————————————— */
.sec-ai-shipstate .sd-rows {
  list-style: none;
  margin: var(--s80) 0 0;
  padding: 0;
}

.sec-ai-shipstate .sd-row {
  position: relative;
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr) auto;
  column-gap: var(--s40);
  align-items: baseline;
  padding: var(--s28) 0 var(--s32);
}

/* The hairline runs from off the left of the screen and stops on the measure. */
.sec-ai-shipstate .sd-row::before {
  content: '';
  position: absolute;
  top: 0;
  left: calc(50% - 50vw);
  right: 0;
  height: 1px;
  background: var(--hair);
}
/* The one rule that is not a hairline: the break between what is shipped and
   what is not. It is the only place the section raises its voice. */
.sec-ai-shipstate .sd-row--lead::before { background: var(--hair-strong); }
.sec-ai-shipstate .sd-row--lead::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: calc(50% - 50vw);
  right: 0;
  height: 1px;
  background: var(--hair);
}

/* All four verbs share one size. The headline stays the largest type here. */
.sec-ai-shipstate .sd-verb {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 34px;
  line-height: 1;
  letter-spacing: var(--tr-h);
  color: var(--ink);
}

.sec-ai-shipstate .sd-phrase {
  font-size: var(--t-body-lg);
  line-height: 1.45;
  letter-spacing: var(--tr-body-lg);
  color: var(--slate);
  text-wrap: pretty;
}

/* The governance marker. Required, visible, inline with the claim it qualifies.
   The dot is the only thing keeping it from being read as more of the sentence. */
.sec-ai-shipstate .flag {
  font-family: var(--sans);
  font-style: italic;
  font-size: 14px;
  line-height: 1.4;
  color: var(--sienna);
  white-space: nowrap;
  margin-left: 12px;
}
.sec-ai-shipstate .flag::before {
  content: '';
  display: inline-block;
  width: 4px;
  height: 4px;
  margin-right: 9px;
  border-radius: var(--r-pill);
  background: currentColor;
  vertical-align: 0.24em;
}

.sec-ai-shipstate .sd-chip {
  justify-self: end;
  align-self: start;
  display: inline-flex;
  align-items: center;
  height: 34px;
  padding-inline: 17px;
  border-radius: var(--r-pill);
  font-size: 14px;
  font-weight: 450;
  letter-spacing: -0.002em;
  white-space: nowrap;
  margin-top: 3px;
}
.sec-ai-shipstate .sd-chip--live { background: var(--ink); color: var(--paper); }
.sec-ai-shipstate .sd-chip--soon { background: var(--mist); color: var(--slate); }

/* The row the headline is actually about. It is not made louder by a bigger
   word — it is made different: more air above and below, the section's one
   non-hairline rule over it, the only unfilled pill on the page, and a phrase
   that changes register from sans to serif. */
.sec-ai-shipstate .sd-row--lead { padding: var(--s48) 0 var(--s48); }
.sec-ai-shipstate .sd-row--lead .sd-phrase {
  font-family: var(--serif);
  font-weight: 400;
  font-size: var(--t-h-sm);
  line-height: 1.28;
  letter-spacing: var(--tr-h-sm);
}

/* — Narrower ————————————————————————————————————————————— */
@media (max-width: 1080px) {
  .sec-ai-shipstate .sd-head { min-height: 200px; }
  .sec-ai-shipstate .sd-object { width: 300px; bottom: -110px; right: -14px; }
  .sec-ai-shipstate .sd-rows { margin-top: var(--s64); }
  .sec-ai-shipstate .sd-row { grid-template-columns: 136px minmax(0, 1fr) auto; column-gap: var(--s28); }
  .sec-ai-shipstate .sd-verb { font-size: 30px; }
}

@media (max-width: 860px) {
  /* Stacked, and the crossing is dropped rather than squashed: at this width the
     docket would hang straight over the first row's verb. The object returns to
     flow, pushed to the right rail so the composition stays asymmetric — the
     headline holds the left, the drawing answers from the right. */
  .sec-ai-shipstate .sd-head { min-height: 0; display: flex; flex-direction: column; }
  .sec-ai-shipstate .sd-object {
    /* clamped rather than a flat percentage: a percentage alone left the drawing
       at ~160px on a phone (a thumbnail) and a flat px left it at 39% of an
       800px header (marooned in the void it created). */
    --obj-w: clamp(240px, 44%, 340px);
    position: static;
    align-self: flex-end;
    width: var(--obj-w);
    /* same optical trim as desktop: cancel the viewBox's 8/177 margin so the
       rail's far end lands on the vertical the pills and hairlines stop on */
    margin: var(--s24) calc(var(--obj-w) * -0.045) 0 0;
  }
  .sec-ai-shipstate .sd-rows { margin-top: var(--s40); }

  /* Verb and status share the top line; the phrase drops under both. Placement
     is explicit — auto-placement pushes the chip onto a third row of its own. */
  .sec-ai-shipstate .sd-row {
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: var(--s20);
    row-gap: 10px;
    align-items: center;
    padding: var(--s24) 0 var(--s28);
  }
  .sec-ai-shipstate .sd-verb { grid-area: 1 / 1; }
  .sec-ai-shipstate .sd-chip { grid-area: 1 / 2; align-self: center; margin-top: 0; }
  .sec-ai-shipstate .sd-phrase { grid-area: 2 / 1 / 3 / -1; }
  .sec-ai-shipstate .sd-row--lead { padding: var(--s32) 0 var(--s40); }

  /* The marker becomes a hanging note. Inline, it wrapped mid-phrase and read as
     more of the sentence in a different colour. */
  .sec-ai-shipstate .flag {
    display: block;
    margin: 8px 0 0;
    white-space: normal;
  }
}

/* 720px is where the tokens drop --t-h to 28px. The verbs have to step down with
   it or they out-rank the headline: at 30px against a 28px H2 the ledger read as
   the loudest thing in the section, which inverts the whole hierarchy. This
   breakpoint exists to track the token, not to fix a layout. */
@media (max-width: 720px) {
  .sec-ai-shipstate .sd-verb { font-size: 24px; }
}

/* ===== section:ai-trust ===== */
/* ============================================================================
   ai-trust — "Answers you can trust." The AI page's closing exhale and its
   single chromatic surface.

   THE SHAPE IS THE COMPOSITION.

   Previous round: the card was drawn full rail width (1199x345 at 1440, a
   3.48:1 banner) and held one 400px text cluster plus a hairline. That is not
   the ink-coverage complaint — void on paper IS this site's aesthetic — but
   void inside a shape the designer CHOSE to draw at that size reads as an
   unfilled panel. The fix is to make the shape smaller, never to fill it.

   So the card is 680px on the right rail at 1440 — sized by its own type, not
   by the rail — and the 520px of paper beside it carries the emptiness, which
   is paper's job. Everything else follows from that one move:

   1. THE CROSSING BECOMES A CORNER CROSSING. There is now paper to the LEFT
      of the card at headline height, so the headline can hang past the card's
      left rail and the card's top-left CORNER can land inside its line box.
      See .tr__h.

   2. A FOCAL POINT EXISTS. Both sentences used to be .h-sm-serif per the
      brief; measured against the h2 that is 27px vs 44px = 1.63x, near-parity,
      and the label fought the payload. The thesis takes .display, the
      qualification keeps .h-sm-serif: 90 / 44 / 26. Three ranks off the site's
      own ramp — no size is minted here.

   3. THE RULE EARNS ITS PLACE. It divides the thesis from the qualification at
      the statement block's own width, ink above it and ink below it, instead
      of running the card's full inner width past 700px of nothing.

   NOTHING WAS ADDED to give the section mass. Three marks and a hairline, as
   before. Its neighbours (the pipeline rail, the ship-state ledger) are dense;
   the value this one adds is the air around one sentence.
   ========================================================================== */

.sec-ai-trust {
  /* A stage: the page's closing argument, so it takes the loose rhythm. */
  padding-block: var(--section-y-loose);
}

/* ── The frame: paper column + card column ────────────────────────────────
   TWO columns, and the left one is deliberately empty. It is what makes the
   peach a resolved object rather than a background band, and it is what gives
   the headline paper to start on.

   THE CARD IS SIZED BY ITS OWN TYPE, NOT BY THE RAIL. That is the whole fix.
   A pixel card width is only correct at the width it was tuned at: the first
   attempt at 660 measured 1.47:1 at 1440 and then went back to a 923x289
   banner in the 820–980 band, because a fixed card plus a shrinking type ramp
   means the sentence stops wrapping and the box stops being filled.

   --tr-col is the thesis's measure and everything else derives from it.
   6.4em of the display size, because "It never invents a number." sets at
   7.53em on one line and its first line "It never invents" sets at 5.83em: any
   measure between those two produces the same two-line break, and 6.4 sits in
   the middle with ~0.57em of slack at either end, so the rag cannot flip under
   a font substitution or a 10% zoom. It re-derives at all three steps of the
   ramp — 576 / 435 / 282 — so the card is 680 / 513 / 334 and is filled by its
   contents at every one of them.

   --tr-overhang is how far the headline reaches back over the paper column. It
   must stay inside that column: measured, the tightest width above the phone
   breakpoint is 480 (column 106, overhang 52). */
.sec-ai-trust .tr {
  --tr-col: calc(var(--t-display) * 6.4);
  --tr-pad-x: clamp(26px, 3.6vw, 52px);
  --tr-pad-y: clamp(40px, 4.6vw, 66px);
  --tr-card-w: calc(var(--tr-col) + var(--tr-pad-x) * 2);
  --tr-overhang: clamp(0px, 11vw, 150px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) min(var(--tr-card-w), 100%);
  position: relative;
}

/* ── The headline, written ACROSS the card's top-left CORNER ───────────────
   Two boundaries at once, which is why this is no longer the descender kiss
   the section shipped two rounds ago.

   HORIZONTALLY: the block sits in the card's column and is pulled back by
   --tr-overhang, so "Answers" and part of "you" are on bare paper and the rest
   of the line is over the card. The card's left rail cuts the line vertically.

   VERTICALLY: -0.53em. Source Serif 4 at line-height 1.16 puts the line-box
   bottom 0.291em under the baseline (0.211em of descent + 0.08em of
   half-leading), so -0.291em would land the edge exactly ON the baseline and
   only the descenders would break into the peach — a 9px overlap on a 51px
   line, which at a glance reads as a heading resting on a box. -0.53em puts
   the edge 0.239em ABOVE the baseline; the x-height is ~0.475em, so the cut
   lands just above the middle of the lowercase bodies. Past the x-height line
   (-0.766em) the card starts swallowing the word.

   TRACKING AND HANG. .h ships -0.015em, which is loose for a serif at 44px, so
   this one takes -0.024em. The base sidebearing correction (-0.026em, set once
   in 10-base for the whole site) is folded into the margin here because the
   overhang overwrites margin-left — and it is opened to -0.034em because the
   first glyph is a diagonal cap A, which needs more hang than the stem letter
   the shared value was measured on.

   z-index + position:relative is what paints the type OVER the peach; the card
   follows it in DOM order. The headline keeps --ink rather than the card's
   --sienna because it does not belong to the card — it belongs to the page the
   card interrupts, and half of it is standing on that page. */
.sec-ai-trust .tr__h {
  grid-column: 2;
  grid-row: 1;
  position: relative;
  z-index: 1;
  margin-left: calc(-0.034em - var(--tr-overhang));
  margin-bottom: -0.53em;
  letter-spacing: -0.024em;
}

/* ── The peach ─────────────────────────────────────────────────────────────
   The page's one chromatic surface. Flat, no shadow, no border — the grain in
   10-base multiplies over it, which is the whole reason it reads as stock and
   not as a filled rectangle.

   Top padding is measured from the CARD EDGE and the headline's descenders now
   hang ~0.45em (about 20px at 1440) below it, so the first beat inside has to
   clear them and then breathe: 1.22x the bottom padding rather than the usual
   optical 0.94x. The extra is descender depth, not a new rhythm. */
.sec-ai-trust .tr__card {
  grid-column: 2;
  grid-row: 2;
  padding: calc(var(--tr-pad-y) * 1.22) var(--tr-pad-x) var(--tr-pad-y);
  border-radius: var(--r-card);
  box-shadow: none;
}

/* One column inside the card, one left rail, three marks down it. The block is
   the card's inner width, and the card's inner width is --tr-col — the measure
   the thesis was sized to. That is what stops the rule ever being wider than
   the type it divides. */
.sec-ai-trust .tr__say {
  display: block;
}

/* ── The thesis: the biggest thing in the section ──────────────────────────
   .display off the ramp, not a bespoke size, and it is the payload rather than
   the label — so the section finally has something that wins. 90 against the
   headline's 44 is a 2.05x jump; the two sentences at one rank against a 44px
   headline were 1.63x, which is near-parity, which is why nothing won.

   The measure is restated here rather than left to the column, because on the
   phone the card is the rail and the column is whatever the rail is. Capped, the
   two-line break holds at every width the section is ever rendered at.

   line-height comes down from the ramp's 1.06 because a two-line stack at 90px
   wants its own leading; tracking is the ramp's -0.025em, already correct. */
.sec-ai-trust .tr__thesis {
  max-width: var(--tr-col);
  line-height: 1.0;
}

/* The one sienna detail the brief allows, at low opacity: a ledger rule at the
   statement block's own width, with the thesis above it and the qualification
   below it. Its previous incarnation ran the card's full inner width with
   ~700px of nothing above or below its left two-thirds and divided nothing.

   Asymmetric gaps: more air above than below, because the mark above it is 3.5x
   the size of the mark below it and an optically centred rule would drift. */
.sec-ai-trust .tr__rule {
  display: block;
  height: 1px;
  background: color-mix(in srgb, var(--sienna) 24%, transparent);
  margin-top: clamp(26px, 3.1vw, 44px);
  margin-bottom: clamp(20px, 2.4vw, 32px);
}

/* ── The qualification ────────────────────────────────────────────────────
   Stays at .h-sm-serif, i.e. it is allowed to lose — and now it loses in
   COLOUR too. .card--peach paints every <p> inside it full --sienna, so the
   26px note and the 90px thesis were the same ink and the entire hierarchy
   rested on size. 84% sienna over peach measures 6.2:1, which clears AA for
   small text with room to spare, so the note can recede without ever becoming
   a legibility problem.

   `pretty`, never `balance`:
   balance is what split "it says / so" across two lines on the one sentence
   this section exists to deliver. Paired with the nbsp in the markup, the line
   now breaks after the comma at every width the card is wide enough to hold
   two lines at all. */
.sec-ai-trust .tr__note {
  text-wrap: pretty;
  max-width: 34ch;
  color: color-mix(in srgb, var(--sienna) 84%, transparent);
}

/* ── Below two columns ────────────────────────────────────────────────────
   Everything above this point holds all the way down to a 480px viewport,
   because the card shrinks with the ramp instead of with the rail: measured,
   the paper column never falls under 106px while the overhang never exceeds
   52px, so the corner crossing survives the whole tablet band rather than
   being the first device to go.

   Below 460 the paper column is thinner than the overhang and there is nothing
   left for the headline to start on. The card takes the rail and the overhang
   goes to zero.

   THE HEADLINE THEN GOES TO THE PAGE RAIL, NOT TO THE CARD'S INNER RAIL. It
   used to take the card's padding, which put it at x=41 on a 390px phone while
   the h2 of every other section on the page — and the card under it — sat at
   x=20. That is a 21px break in the page's only remaining alignment, on the
   one device where a page IS its left edge. On the rail it also overhangs the
   card's rounded corner, so the cap A stands on paper and the rest of the line
   is taken by the peach: the same event as the desktop corner crossing, at the
   only scale a phone can express it.

   The CROSSING is what survives, because it costs no width. It is the last
   device to drop rather than the first, and the phone keeps it. */
@media (max-width: 460px) {
  .sec-ai-trust .tr {
    --tr-pad-x: 22px;
    --tr-pad-y: 30px;
    --tr-overhang: 0px;
    grid-template-columns: minmax(0, 1fr);
  }
  .sec-ai-trust .tr__h,
  .sec-ai-trust .tr__card {
    grid-column: 1;
  }
  .sec-ai-trust .tr__h {
    margin-left: -0.034em;
  }
  /* The rail is the only structure left, so the qualification is allowed the
     whole of it — at 390 a 34ch cap would leave it a 268px measure and turn
     three lines into five. */
  .sec-ai-trust .tr__note {
    max-width: none;
  }
}

/* Below ~880 the headline's descender depth (which scales with --t-h) and the
   card's top padding (which scales with vw) diverge, and the first beat inside
   the card starts climbing into the descenders. */
@media (max-width: 880px) {
  .sec-ai-trust .tr__card {
    padding-top: calc(var(--tr-pad-y) * 1.1 + 0.3em);
  }
}

/* ===== section:cta-band ===== */
/* ————————————————————————————————————————————————
   cta-band — the closing punctuation, shared by all eight pages.

   Round 4. Round 3 lost on FINISH, not on idea: the two largest objects in the
   frame were hand-drawn placeholder-bar SVGs (a 264×426 field and a 290×214
   fragment), so a third of the composition read as content-TBD; every surface
   in the band sat between #FFF and #EAEAEA, so there was no mass anywhere; and
   ~550×290 of empty gradient sat between the headline's descender and the
   card's left edge. Three changes, in order of size:

   1. THE SLAB. The card is now the only dark mass in any page's closing band —
      var(--ink), white keys, paper values, no shadow (elevation is reserved for
      .artifact and this is not one). It is the "one genuinely heavy element"
      the band never had.
   2. REAL TYPE, NOT BARS. The menu inside it is SET: 29 priced rows under six
      small-caps category headers, prices on one shared tabular column per
      column of the menu. The kitchen ticket at the other corner is set the same
      way. Nothing in this section is a placeholder bar any more, and the slab
      earns its area with character density rather than filling it.
   3. INTERLOCK. The slab is taller (768px against a ~690px band) and pinned so
      its top rides ABOVE the headline's first line rather than starting below
      it, and the file tile is now a CHILD of the slab, hung across its left
      edge — so the overlap is measured from the slab and holds at every width.
      The hole in the middle is closed by the tile, not by moving the rail.

   The crop is still measured: the slab overhangs the right edge by 108px and
   its inner padding-right is that overhang plus 28px, so every price and every
   value ends 28px inside the cut and the edge falls through row whitespace.
   ———————————————————————————————————————————————— */
.sec-cta-band {
  padding-block: var(--section-y-loose);
  border-top: 1px solid var(--hair);
  /* Every object here is meant to be cut by these edges, and the bloom must
     never bleed onto whichever section happens to precede us. */
  overflow: hidden;
}

/* — The light —————————————————————————————————
   Recomposed for a left-anchored frame: the warm primary bloom sits behind the
   display head at 30/26 rather than on the axis, a lilac holds the object half,
   and a low warm one sits under the cut cards so the bottom of the band never
   flattens out. The cool --wash-4 stays dropped (it read as a blue-grey corner
   on --fog) and the component's paper-coloured bottom fade is removed — this
   band sits on --fog, so that fade would paint a white shelf across the close
   of every page. */
.sec-cta-band > .wash {
  inset: 0;
  opacity: 0.72;
  filter: blur(18px) saturate(106%);
  background:
    radial-gradient(38% 46% at 24% 20%, var(--wash-1) 0%, transparent 66%),
    radial-gradient(48% 54% at 58% 32%, var(--wash-2) 0%, transparent 70%),
    radial-gradient(40% 46% at 14% 76%, var(--wash-3) 0%, transparent 72%),
    radial-gradient(40% 46% at 88% 80%, var(--wash-1) 0%, transparent 72%);
}
.sec-cta-band > .wash::after { content: none; }

/* — Stacking ———————————————————————————————————
   Type above everything; the objects above the closing rule; the rule beneath
   both. That ordering is what lets the full-measure hairline disappear under
   the slab instead of drawing a grey line across it. */
.sec-cta-band .ctab__wrap { position: relative; z-index: 2; }
.sec-cta-band .ctab__footwrap { position: relative; z-index: 0; }

/* — The rail ———————————————————————————————— */
.sec-cta-band .ctab {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  max-width: 760px;
}

.sec-cta-band .ctab__h {
  /* SECTION-HEAD SCALE, NOT DISPLAY. This partial is byte-identical on all
     eight pages, so a display-scale sign-off made the last words on six of them
     the loudest words on the page — louder than the page's own H1. The site's
     one display is the home hero; every section headline, this one included,
     sits on .h. The measure still makes the two decks read as a natural break
     off the rail rather than as a balanced pair of centred lines. */
  font-size: var(--t-h);
  line-height: 1.16;
  letter-spacing: var(--tr-h);
  max-width: 15ch;
}
/* Give the clipped line masks room for the italic 'y' descender, then take the
   extra leading back so the two decks keep the rhythm .h sets for them. */
.sec-cta-band .ctab__h .line-mask { padding-bottom: 0.14em; margin-bottom: -0.14em; }

/* Dropped from --t-body-lg. At 20px the lede competed with the headline; at
   17px it becomes the quiet second voice, and at 44ch it sets three short lines
   against the rail instead of two long centred ones. */
/* The measure is also a CLEARANCE. The slab's left edge marches left as the
   viewport narrows (it is 53vw wide against a fixed 108px overhang) while the
   rail's text stays pinned at the gutter, so the lede is the line that would
   meet the file tile first. Every step below is set to keep ~40px of air
   between the end of this paragraph and the tile's send button. */
.sec-cta-band .ctab__sub {
  margin: var(--s24) 0 0;
  font-size: var(--t-body);
  line-height: 1.5;
  max-width: 40ch;
}

/* One filled pill and one plain link, both flush to the rail. */
.sec-cta-band .ctab__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s12) var(--s28);
  margin-top: var(--s40);
}
.sec-cta-band .ctab__wa {
  /* .alink is a text link; give it a real hit target anyway. */
  min-height: 44px;
  padding-inline: 4px;
}

/* — The close ————————————————————————————————
   Full page measure, flush left, with a 2px ink tab thickening its left end and
   the fine print hanging off exactly that end. */
.sec-cta-band .ctab__foot {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  margin-top: var(--s48);
}
.sec-cta-band .ctab__rule {
  position: relative;
  display: block;
  width: 100%;
  height: 1px;
  background: var(--hair-strong);
}
.sec-cta-band .ctab__rule::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 76px;
  height: 2px;
  background: var(--ink);
}
.sec-cta-band .ctab__fine { margin-top: var(--s20); color: var(--ash); }

/* — The objects ———————————————————————————————
   Placed against the section, not the wrap, so their crops are measured from
   the viewport edge and stay identical at every desktop width. */
.sec-cta-band .ctab__stage {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}

/* THE SLAB. Anchored to the BOTTOM, never the top: the shared shell gives a
   first-child section 44px of extra top padding, and bottom anchoring is the
   only way the crop and the offset against the type read identically on a real
   page and in the isolated preview. Its HEIGHT is declared rather than derived,
   because the menu inside deliberately overruns it — that overrun is what
   guarantees the slab is never a part-empty dark field at any width. */
.sec-cta-band .ctab__art {
  position: absolute;
  right: -108px;
  bottom: -130px;
  /* 53vw keeps the overhang constant while the slab narrows with the viewport,
     so it can never reach across into the rail. At 1440 the slab's left edge
     lands at x=788, ~200px off the headline's ragged right — the hole the last
     round left between the two masses was 550px wide. */
  width: min(760px, 53vw);
  height: 768px;
}
.sec-cta-band .ctab__card {
  /* Overhang + 28px, so the price column ends well inside the cut and the edge
     always falls through whitespace. */
  --crop: 136px;
  /* The one dark mass. No shadow: elevation belongs to .artifact, and a slab
     this heavy separates on value alone. */
  --slab-hair: color-mix(in srgb, var(--paper) 58%, transparent);
  height: 100%;
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--r-card);
  text-align: left;
  overflow: hidden;
}

.sec-cta-band .ctab__card-top {
  display: flex;
  align-items: center;
  gap: var(--s16);
  padding: 24px var(--crop) 20px 30px;
  border-bottom: 1px solid var(--slab-hair);
}
.sec-cta-band .ctab__card-title {
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.008em;
  color: var(--paper);
  white-space: nowrap;
}
/* The single confirmation beat in the whole band, so it is the highest-contrast
   thing in it: paper on ink, on the same flush right column as every value and
   every price below it. */
.sec-cta-band .ctab__chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  height: 24px;
  padding-inline: 11px;
  border-radius: var(--r-pill);
  background: var(--paper);
  color: var(--ink);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: -0.004em;
  white-space: nowrap;
}

.sec-cta-band .ctab__list { list-style: none; margin: 0; padding: 0; }
.sec-cta-band .ctab__list li {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 13px var(--crop) 13px 30px;
  font-size: 13.5px;
  line-height: 1.3;
}
.sec-cta-band .ctab__list li + li { border-top: 1px solid var(--slab-hair); }
.sec-cta-band .ctab__k { color: var(--smoke); white-space: nowrap; }
/* One flush right edge shared by the chip, every value here and every price in
   the menu below — the column that makes the slab read as one object. */
.sec-cta-band .ctab__v {
  margin-left: auto;
  color: var(--paper);
  font-weight: 450;
  white-space: nowrap;
}

/* — The menu, set ————————————————————————————
   28 priced rows under six small-caps headers. It overruns the slab's declared
   height by ~70px at 1440 and by more as the columns narrow, so the band always
   cuts it mid-row rather than leaving dark air at the bottom. */
.sec-cta-band .ctab__menu {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 36px;
  border-top: 1px solid var(--slab-hair);
  padding: 24px var(--crop) 0 30px;
}
.sec-cta-band .ctab__cat {
  font-size: 10.5px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--smoke);
  padding-bottom: 8px;
  border-bottom: 1px solid var(--slab-hair);
}
.sec-cta-band .ctab__rows { list-style: none; margin: 9px 0 22px; padding: 0; }
.sec-cta-band .ctab__rows li {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding-block: 4.5px;
  font-size: 12.5px;
  line-height: 1.34;
  letter-spacing: -0.002em;
  color: var(--paper);
}
.sec-cta-band .ctab__it { min-width: 0; }
/* The qualifier is part of the item name, not a second field — set in the same
   line at the same size, one step down in value. */
.sec-cta-band .ctab__it i { font-style: normal; color: var(--smoke); }
.sec-cta-band .ctab__pr { margin-left: auto; color: var(--paper); white-space: nowrap; }

/* — The file tile ————————————————————————————
   A CHILD of the slab wrapper, hung across the slab's left edge at a fixed
   offset, so the object-on-object overlap is measured from the slab and cannot
   drift into the rail as the viewport narrows. It is also what closes the
   ~550px hole that used to sit between the headline and the card. */
.sec-cta-band .ctab__art3 {
  position: absolute;
  /* Crosses the slab's edge by 30px — exactly the slab's own left padding, so
     the overlap occludes surface and never a glyph. Vertically it straddles the
     hairline between the fact table and the menu, so it cuts a structural line
     rather than a row of type. */
  left: -192px;
  top: 232px;
  width: 214px;
}
.sec-cta-band .ctab__tile {
  display: flex;
  align-items: center;
  gap: 11px;
  /* Raised white, ringed rather than shadowed — elevation is reserved for
     .artifact. Most of the tile lies on the pale gradient, where an unringed
     white card has no edge at all. */
  background: var(--raised);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-small);
  padding: 12px 14px;
  transform: rotate(-2.5deg);
}
.sec-cta-band .ctab__tile-ico {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 11px;
  background: var(--mist);
  color: var(--slate);
}
.sec-cta-band .ctab__tile-txt { min-width: 0; }
.sec-cta-band .ctab__tile-name {
  display: block;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -0.006em;
  color: var(--ink);
  white-space: nowrap;
}
.sec-cta-band .ctab__tile-meta {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  line-height: 1.3;
  color: var(--slate);
  white-space: nowrap;
}
/* The second ink mass, hanging off the tile's left edge onto the gradient — a
   third overlap, at a third scale. */
.sec-cta-band .ctab__send {
  position: absolute;
  left: -19px;
  top: 12px;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--paper);
}

/* — The ticket ————————————————————————————————
   What the built shop prints: light, flat, no elevation, cut by the OPPOSITE
   edge and by the bottom. It sits below every line of type, so it can never
   collide with the rail at any viewport width. */
.sec-cta-band .ctab__art2 {
  position: absolute;
  left: -60px;
  width: 400px;
  /* Height is declared so the standing part is EXACTLY 130px at every width.
     The band's bottom padding is a constant 160px, so the ticket's top edge is
     always 30px clear of the fine print however the type above it reflows —
     anchoring by content height alone let it climb over the closing line at
     1024. */
  height: 260px;
  bottom: -130px;
}
.sec-cta-band .ctab__slip {
  height: 100%;
  background: var(--mist);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-card);
  /* Left padding is deeper than the 60px crop, so the viewport edge cuts empty
     card and never a glyph — the object is cropped, the type is not. */
  padding: 20px 24px 0 88px;
  overflow: hidden;
  /* Pivoted on its top-right corner, so the rotation can only ever drop the
     ticket away from the fine print, never lift it into the text. */
  transform: rotate(-4deg);
  transform-origin: 100% 0;
}
.sec-cta-band .ctab__slip-top {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--hair-strong);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--slate);
}
.sec-cta-band .ctab__slip-top span:last-child { margin-left: auto; }
.sec-cta-band .ctab__slip-rows { list-style: none; margin: 10px 0 0; padding: 0; }
.sec-cta-band .ctab__slip-rows li {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding-block: 5px;
  font-size: 12.5px;
  line-height: 1.34;
  letter-spacing: -0.002em;
  color: var(--ink);
  white-space: nowrap;
}
.sec-cta-band .ctab__q { flex: none; width: 12px; color: var(--slate); }
.sec-cta-band .ctab__slip-rows i { font-style: normal; color: var(--slate); }

/* — Narrower desktops —————————————————————————
   The objects are anchored to the viewport, so the crops hold all the way down;
   only the type measure and the menu's column count need to give. */
@media (max-width: 1180px) {
  .sec-cta-band .ctab { max-width: 620px; }
  .sec-cta-band .ctab__sub { max-width: 32ch; }
  .sec-cta-band .ctab__menu { column-gap: 28px; }
}
@media (max-width: 1080px) {
  .sec-cta-band .ctab { max-width: 500px; }
  .sec-cta-band .ctab__sub { max-width: 30ch; }
  .sec-cta-band .ctab__card { --crop: 126px; }
  .sec-cta-band .ctab__art3 { left: -166px; top: 206px; width: 196px; }
}

/* — Stacked ————————————————————————————————————
   Below 980 there is no room beside the rail, so the slab drops into flow
   between the CTA and the closing line. It is NOT a full-width block inside
   both gutters: it keeps its left edge on the rail, runs 24px past the right
   edge of the screen, and keeps the file tile lying across its top-left corner
   — the crop-and-overlap idea survives the breakpoint instead of being deleted
   at it. The stage is a sibling BETWEEN the two wraps in source for exactly
   this reason, so the page still ends on the closing line. */
@media (max-width: 980px) {
  .sec-cta-band .ctab { max-width: none; }
  .sec-cta-band .ctab__sub { max-width: 46ch; }

  .sec-cta-band .ctab__stage {
    position: relative;
    inset: auto;
    width: 100%;
  }
  .sec-cta-band .ctab__art {
    position: relative;
    inset: auto;
    width: auto;
    /* Tuned so the cut lands mid-row, never on a category header — an orphan
       header at the crop reads as a bug, a half-row reads as a crop. */
    height: 620px;
    /* Left edge on the rail; right edge 24px past the screen, cropped by the
       section's own overflow. */
    margin: var(--s64) -24px 0 var(--gutter);
    animation: none;
  }
  /* Room above the title for the tile to cross the corner without covering it. */
  .sec-cta-band .ctab__card { --crop: 48px; }
  .sec-cta-band .ctab__card-top { padding: 52px 48px 18px 26px; }
  .sec-cta-band .ctab__list li { padding: 13px 48px 13px 26px; }
  .sec-cta-band .ctab__menu {
    grid-template-columns: minmax(0, 1fr);
    padding: 22px 48px 0 26px;
  }
  .sec-cta-band .ctab__mcol:last-child { display: none; }

  .sec-cta-band .ctab__art3 {
    left: 10px;
    top: -30px;
    width: 200px;
    animation: none;
  }
  .sec-cta-band .ctab__art2 { display: none; }

  .sec-cta-band .ctab__foot { margin-top: var(--s64); }
}

/* — Phones ————————————————————————————————————
   The action row is capped to the measure of the copy above it, so it can never
   bulge wider than the sentence it belongs to. */
@media (max-width: 720px) {
  .sec-cta-band .ctab__h { max-width: 13ch; }
  .sec-cta-band .ctab__sub { margin-top: var(--s16); max-width: 32ch; }
  .sec-cta-band .ctab__actions {
    margin-top: var(--s32);
    max-width: 34ch;
    gap: var(--s8) var(--s20);
  }
  .sec-cta-band .ctab__art { margin-top: var(--s48); height: 480px; }
  .sec-cta-band .ctab__art3 { left: 6px; top: -28px; width: 184px; }
  .sec-cta-band .ctab__foot { margin-top: var(--s48); }
  .sec-cta-band .ctab__rule::before { width: 56px; }
}
