/* ===== 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:customers-hero ===== */
/* ═══════════════════════════════════════════════════════════════════════════
   customers-hero — the customers page opening.

   A two-line serif claim standing low on the left rail, one sentence under it,
   and an isometric terrace of shop units rising out of the right edge of the
   screen. No card, no rule, no pills, no buttons: two pieces of type and one
   drawing, on a lot of warm ground.
   ═══════════════════════════════════════════════════════════════════════════ */

.sec-customers-hero {
  /* Drawn width. The overhang is NOT a sibling constant — see .ch-street. */
  --ch-street-w: 730px;
  /* How far the far end of the terrace runs off the right of the SCREEN. */
  --ch-street-past: 124px;

  /* The terrace leaves the frame on purpose. The page root already clips
     horizontally; clipping here as well keeps the overhang a property of this
     section rather than of the document. */
  overflow: clip;
}

/*
 * THE GROUND UNDER THE DRAWING.
 *
 * The site wash blooms warm at 74%/30%, which on this page would put the warm
 * mass behind the headline and leave the drawing sitting on the palest, coolest
 * patch in the frame — a hairline drawing on near-white paper is the failure
 * mode ILLUSTRATION.md calls "effectively does not exist". Recomposed from the
 * same four tokens and moved OFF the drawing.
 *
 * The first attempt put the warm bloom behind the near shopfronts, on the
 * theory that a drawing wants a ground. Measured, it did the opposite: a 1.4px
 * hairline at .78 over saturated peach lost most of its contrast, and the
 * densest, most detailed corner of the terrace was the palest thing in the
 * frame. The bloom now sits in the gap BETWEEN the type and the terrace — the
 * one large empty region in the composition — so the colour has somewhere to
 * be, the drawing keeps near-paper behind it, and the wash stops competing
 * with the only ink in the section. Every stop is also ANCHORED TO AN EDGE and
 * sized past it: a bloom centred in open space reads as a stain on the paper
 * rather than as light, which is exactly how the first version of this rule
 * measured — a discrete orange disc floating between the two objects. Zero-alpha stops are written `transparent`;
 * gradient alpha is premultiplied in every current engine, so there is no grey
 * fringe and no colour outside the tokens.
 */
.sec-customers-hero > .wash {
  background:
    radial-gradient(58% 58% at -2% -4%, var(--wash-4) 0%, transparent 74%),
    radial-gradient(62% 56% at 24% -12%, var(--wash-3) 0%, transparent 76%),
    radial-gradient(1240px 760px at 28% 116%, var(--wash-1) 0%, transparent 72%),
    radial-gradient(1080px 820px at 102% 104%, var(--wash-2) 0%, transparent 74%);
}
/*
 * The site-wide seam fade is 200px, which reached up into the near end of the
 * terrace and bleached the ground out from under the one part of the drawing
 * that has to hold. 120px clears the A-board and is still a fade, not an edge.
 */
.sec-customers-hero > .wash::after { height: 120px; }

/* ——— stage —————————————————————————————————————————————————————————
   Two tracks, ONE row. The left is minmax(0,1fr) rather than auto so a font
   fallback in the headline can never push the drawing off its own overhang.

   The headline and the lede are ONE grid item, not two. As two items in a
   two-row grid the drawing spanned both rows, the surplus height was shared out
   between them, and the alignment then centred each piece of type inside its
   own inflated row — which silently opened a 145px hole between the headline
   and the sentence that belongs to it. That gap was an artefact of the row
   maths; the spacing below is a decision.

   END-aligned on the cross axis, which is the whole composition. The drawing is
   nearly three times the height of the type block, and its ink is not spread
   evenly through that height — at the LEFT edge of the drawing, the side the
   type meets, every mark belongs to the near shop and sits low. Centring the
   type left it floating opposite empty sky. Sitting it on the same line as the
   near shop's pavement ties the two into one band across the bottom of the
   frame and hands the whole upper left to the void, which is where the void
   belongs on a page whose nav sits over it.
   ———————————————————————————————————————————————————————————————————— */
.sec-customers-hero .ch-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  column-gap: var(--s48);
}

.sec-customers-hero .ch-type {
  grid-column: 1;
  /* Lifts the last line of the lede clear of the pavement rather than standing
     it exactly on it — the tie should read as alignment, not as a collision. */
  padding-bottom: var(--s64);
}

.sec-customers-hero .ch-title {
  /* Both authored lines set about 490px of ink at the 64px ceiling; this leaves
     roughly 70px of slack so the two-line rag survives a font fallback without
     re-wrapping into a shape nobody chose. */
  max-width: 560px;
}

.sec-customers-hero .ch-sub {
  /* Never wider than the headline above it. At the brief's 640px ceiling the
     lede out-ran the two serif lines and its right edge became the closest
     thing on the page to the terrace — 100px of clearance at 1440, which reads
     as a collision rather than as a gap. Tucked inside the headline measure,
     the type column tapers and the void between the two objects stays a void. */
  max-width: 570px;
  margin-top: var(--s32);
  /* At the full measure this sets as one long line and one short one; balanced
     it lands as two even lines that sit inside the headline's width, so the
     type column tapers instead of bulging under its own headline. */
  text-wrap: balance;
}

/* ——— the terrace ——————————————————————————————————————————————————
   THE OVERHANG IS MEASURED FROM THE SCREEN, NOT FROM THE PAGE MEASURE. A fixed
   negative margin cannot do this: the gap between the 1200px measure and the
   viewport edge is 120px at 1440 and 360px at 1920, so one constant is either a
   crop at one width or a comfortable stop at the other — and an earlier draft
   landed the far corner 30px INSIDE the frame, a terrace that stops, which is
   the one thing this drawing must not do. `(1200px - 100vw) / 2` is exactly the
   negative of that gap wherever the measure is capped, so the far end lands
   --ch-street-past beyond the edge at every width above the cap. Below the cap
   the wrap is gutter-bound and the gap simply IS the gutter.

   The negative margin also shrinks the track the grid reserves, so every pixel
   the drawing spends off-screen is handed back to the type column.
   ———————————————————————————————————————————————————————————————————— */
.sec-customers-hero .ch-street {
  grid-column: 2;
  /* END-aligned, and that is load-bearing above ~2000px. The overhang is large
     enough there that (width + margin) goes negative, the auto track floors at
     zero, and a start-aligned item would then be pinned to the track's start —
     landing its right edge back INSIDE the screen on a wide monitor, the one
     failure this whole rule exists to prevent. End alignment pins the margin
     box's right edge instead, so the negative margin keeps pushing the drawing
     past the screen edge however wide the display gets. */
  justify-self: end;
  width: var(--ch-street-w);
  margin-right: calc(var(--gutter) * -1 - var(--ch-street-past));
}
@media (min-width: 1264px) {
  .sec-customers-hero .ch-street {
    margin-right: calc((var(--page) - 100vw) / 2 - var(--ch-street-past));
  }
}

/* The kerb runs to the drawing's own lower edge; a hair of negative margin sits
   it on the section's baseline instead of leaving it floating above one. */
.sec-customers-hero .ch-illo { margin-bottom: -10px; }

/* ——— widths ——————————————————————————————————————————————————————
   Only the drawn width steps down; the share that leaves the screen is held by
   the rule above, so the crop stays about a sixth of the run at every width
   rather than swinging between a sliver and a third of it.
   ———————————————————————————————————————————————————————————————————— */
@media (max-width: 1439px) {
  .sec-customers-hero { --ch-street-w: 600px; --ch-street-past: 96px; }
  .sec-customers-hero .ch-wrap { column-gap: var(--s40); }
}
@media (max-width: 1279px) {
  .sec-customers-hero { --ch-street-w: 528px; --ch-street-past: 78px; }
  .sec-customers-hero .ch-title { max-width: 480px; }
  .sec-customers-hero .ch-sub { max-width: 470px; }
}
@media (max-width: 1119px) {
  .sec-customers-hero { --ch-street-w: 470px; --ch-street-past: 60px; }
  .sec-customers-hero .ch-wrap { column-gap: var(--s32); }
  .sec-customers-hero .ch-type { padding-bottom: var(--s48); }
  /* Both measures come in together here. Holding the lede at its desktop width
     while the terrace kept growing left closed the gap between them to 37px at
     1024 — the one width at which this composition genuinely collides. */
  .sec-customers-hero .ch-title { max-width: 380px; }
  .sec-customers-hero .ch-sub { max-width: 400px; }
}

/*
 * Below 940 the rail cannot hold the headline, the lede and a terrace wide
 * enough for a shopfront to still read as a shopfront, side by side, with air
 * between them. Rather than shrink the drawing until its glazing turns to lint,
 * the composition rotates: type across the full measure, the terrace underneath
 * it, still set right and still leaving the frame. The diagonal survives; only
 * the axis it hangs off changes.
 */
@media (max-width: 939px) {
  .sec-customers-hero .ch-wrap { grid-template-columns: minmax(0, 1fr); }
  .sec-customers-hero .ch-type { padding-bottom: 0; }
  .sec-customers-hero .ch-street {
    grid-column: 1;
    grid-row: 2;
    justify-self: end;
    width: min(560px, calc(100% + var(--gutter) + var(--ch-street-past)));
    margin-top: var(--s48);
    margin-right: calc(var(--gutter) * -1 - var(--ch-street-past));
  }
  .sec-customers-hero .ch-illo { margin-bottom: 0; }
  .sec-customers-hero .ch-title { max-width: none; }

  /* The ground follows the object: with the drawing now low and central-right,
     the warm mass drops with it instead of lighting an empty corner. */
  .sec-customers-hero > .wash {
    background:
      radial-gradient(46% 26% at 0% 3%, var(--wash-4) 0%, transparent 70%),
      radial-gradient(54% 26% at 36% -2%, var(--wash-3) 0%, transparent 72%),
      radial-gradient(116% 34% at -6% 38%, var(--wash-1) 0%, transparent 72%),
      radial-gradient(56% 26% at 8% 100%, var(--wash-2) 0%, transparent 74%);
  }
}

@media (max-width: 720px) {
  .sec-customers-hero { --ch-street-past: 48px; }
  /*
   * 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 7.8, capped
   * at 44), which is a divisor only this section knew about; the two authored
   * lines still stand on one line each at 34px.
   */
  .sec-customers-hero .ch-title {
    max-width: none;
  }
  .sec-customers-hero .ch-sub { margin-top: var(--s20); }
  .sec-customers-hero .ch-street { margin-top: var(--s40); }
}

/* ===== section:customers-proof ===== */
/* ============================================================================
   customers-proof — "Proof". Two customers, six figures, nothing else.

   The whole section is typographic on purpose. Two sections down, customers-more
   already puts three isometric drawings on paper cards over this same fog ground;
   a second card family carrying drawings would read as the same section twice.
   And a bar next to "1,840" would be a chart mark with no datum to measure
   against. So what is left is six numerals, and the way six numerals are made to
   look expensive is scale, air and hierarchy.

   THE FOUR DECISIONS THAT SURVIVED, AND THE THREE THAT REPLACED THEM.

   1. ONE REGISTRATION HOLDS THE SECTION, AND IT NOW HOLDS THE CONTENT TOO.
      --pf-drop is the y of the lead plate's ink masthead's BOTTOM edge, of
      NOOT's TOP edge, and of the datum. That much was already true and already
      worked. What did NOT work is that the drop was ALSO being inherited by
      everything inside the second plate: its name box was --pf-drop deep, so its
      lead figure started a second --pf-drop down the page, +34% sat at y528 and
      HK$1.9m at y650, and the row's leftover height was then handed to whichever
      plate was shorter as pure dead band — measured 201px in Calici against 80px
      in NOOT, in the same interval, between two objects of the same rank.

      Both faults are the same fault, so both have one fix: --pf-cap. It is the
      distance from the registration line to the top of a lead figure's box, and
      BOTH plates set their lead figure from it. Calici spends it as padding under
      the masthead; NOOT spends it as padding under its own top rim, less the
      padding and border that plate already carries. The two lead figures land on
      one cap-line and can be read across. The stagger now lives exactly where it
      was always supposed to: the plate edges, the masthead and the datum.

   2. THE SECOND NAME IS OUT OF THE PLATE. It was the mechanism that transmitted
      the stagger into the content — a name box sized to hold NOOT --pf-name-off
      BELOW the datum, which is --pf-drop of box for one word. It is absolutely
      positioned above its plate now, on the same baseline as the reversed name
      in the masthead, in ground that was empty. Two names, one line, one of them
      knocked out of ink. The plate underneath holds nothing but figures, which is
      precisely why the figures register.

   3. THE MASTHEAD IS SIZED FROM ITS TYPE. 132px of solid ink carrying one 20px
      word is a header bar bolted to a plate. --pf-drop is now derived from
      --pf-name plus the air above and below it, so the ink has something in it.

   4. THE PLATES ARE PAPER. They were `.card` — --mist, which is DARKER than the
      --fog ground — with the rim explicitly deleted, so they read as holes cut in
      the sheet rather than as paper laid on it. They are `.card--paper` now, on a
      warm mix, with a drawn rim; and the rim is the same 1px and the same colour
      as the datum, which is what lets the line run THROUGH the second plate.

   5. RANK IS STRUCTURAL, NOT A DISCOUNT ON THE FIGURES. Both plates set their
      lead figure at --pf-fig-a. Rank is carried by plate width (24 units against
      20) and by the ink masthead that only Calici gets. It is deliberately NOT
      carried by a colour field: customers-quotes ranks its pair with a peach
      surface, and two neighbours must not reach for the same device.

   6. EACH PLATE IS 1 + 2, NOT 3, and the two pairs are bottom-set, so the four
      supporting figures register on one baseline across both plates as well.

   7. TOPS STAGGER, BOTTOMS ALIGN — and below 1040 the stagger turns ninety
      degrees rather than switching off: Calici holds the left rail and runs off
      the right of the screen, NOOT starts off the LEFT of the screen and closes
      exactly on the right rail, and their text rails coincide. Mirrored crops,
      each plate with one clean rail edge — where before both plates missed a
      rail by a small unexplained amount.

   Every rule is scoped under .sec-customers-proof.
   ========================================================================== */

.sec-customers-proof {
  /* The gutter between the page rail and the screen edge, negated — the same
     bleed primitive the ink sections use. `overflow-x: clip` on the root contains
     it without creating a scroll box. */
  --pf-bleed: calc(max(var(--gutter), (100vw - var(--page)) / 2) * -1);
  /* How far past the screen edge a stacked plate is cut. Bigger than the corner
     radius, or the crop reads as a rounded corner that just missed. */
  --pf-crop: var(--s28);

  /*
   * ONE LINE WEIGHT FOR THE WHOLE REGISTRATION. The datum and both plate rims
   * take this single value, and that is the point of it: a rounded card cannot
   * meet a straight rule across its full width, so the rule stops tangent to the
   * corner arc and the card's OWN top rim carries the line across the span
   * between the two tangents. That only reads as one continuous line if the two
   * are literally the same ink. At 20% the step off the fog ground is about
   * thirty levels — a line you can follow across a gutter, and still a hairline.
   */
  --pf-line: color-mix(in srgb, var(--ink) 20%, transparent);
  /* Internal structure — the rule over the supporting pair, the divider between
     its two halves. Lighter than the datum on purpose: those bound, the datum
     means something. --hair is too pale to read on the warm plate. */
  --pf-hair: var(--hair-strong);
  /*
   * Warm paper, not white. --peach is rationed to one surface per page and
   * customers-quotes has spent it, so this is a mix, the same primitive
   * customers-more uses for its lead — but at half the strength and applied to
   * BOTH plates, because here it is the section's ground temperature and not a
   * ranked surface. Cool plates on a cool fog were the reason nothing in this
   * section was warm except the 14px governance rule.
   *
   * The base is --raised and not --paper on purpose: mixed into --paper the
   * result samples two levels DARKER than the fog ground, which is the exact
   * fault the old --mist plates were pulled for — paper laid on a sheet is never
   * darker than the sheet. Off --raised it lands level with the fog in luminance
   * and warm in hue, which is the whole of the effect wanted.
   */
  --pf-plate: color-mix(in srgb, var(--sienna) 3%, var(--raised));

  --pf-pad: var(--s32);
  --pf-gap: var(--s40);

  /* The name in the masthead, and its twin above the second plate. Set at the
     headline step, which is the size that makes the masthead a masthead: at the
     card-title 20px it was one small word in 132px of ink and the block read as a
     header bar bolted on. */
  --pf-name: var(--t-h);
  /* How far each name sits above the datum. */
  --pf-name-off: var(--s20);
  /* THE REGISTRATION. Masthead bottom = NOOT's top = the datum. Sized from the
     type it holds: air, the name, and the name's clearance off the line — so the
     ink is spent on something rather than the type floating in a fixed slab. */
  --pf-drop: calc(var(--s40) + var(--pf-name) + var(--pf-name-off));
  /* THE CAP-LINE. Registration line → top of a lead figure's box, on BOTH
     plates. This is the number that closed the 201px hole. */
  --pf-cap: var(--s64);

  /* Figure ramp. 9.4vw tracks the page measure below 1264px, where .wrap stops
     being capped; above that the measure is fixed and so is the figure. Both
     plates take --pf-fig-a for their lead figure; the supporting pair is a fixed
     fraction of it, so the internal step is the same 2.27 at every width. */
  --pf-fig-a: clamp(64px, 9.4vw, 124px);
  --pf-fig-c: calc(var(--pf-fig-a) * 0.44);
  /* Units ride subordinate to the numeral. This is not decoration: at 1em the
     string "HK$1.9m" is 3.9 ems wide and cannot stand at --pf-fig-a inside a
     twenty-unit plate at any sane padding. At 0.52 it is 2.5 ems and fits with
     room, which is exactly how the money number got its size back. */
  --pf-unit: 0.52em;
}

/* ——— Section grain ————————————————————————————————
   The page grain is body::after with `position: fixed`: it paints one viewport
   and stops. This band runs past that line, and the ground below it came back
   perfectly flat — same level, no tooth — which reads as a full-width horizontal
   seam straight across the sheet the plates stand on. Sampled, the LEVEL matched
   either side (250/250/251 on both), so only the texture had to be restored;
   there is no tone lift here, unlike the sections that needed one.

   It sits ABOVE the plates, like the page layer at z 9999, so the paper carries
   the same tooth as the ground it lies on. `overflow: clip` keeps the tile
   inside the band it belongs to. */
.sec-customers-proof .pf__grain {
  position: absolute;
  inset: 0;
  z-index: 4;
  overflow: clip;
  pointer-events: none;
}
.sec-customers-proof .pf__grain::before {
  content: '';
  position: absolute;
  inset: 0;
  opacity: 0.12;
  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;
}

/*
 * THE LIFT — the half of the grain fix that takes the TONE step out of the
 * ground. Our own grain restores the texture but not the level: the page's fixed
 * layer multiplies whatever viewport it covers by about 0.982 and nothing below
 * it, so with the tooth alone this band still measured 241.9 against 246.4 — a
 * hard full-width horizontal straight under the plates. This sheet gives those
 * levels back over exactly the band the page layer reaches.
 *
 * It is `sticky`, not `fixed`, because it has to track the viewport the way the
 * page layer does WITHOUT leaking outside this section; `margin-top: -100vh`
 * parks its unshifted position one viewport ABOVE the section, outside the clip,
 * so a capture in which the fixed layer never reaches this band gets no light
 * band compensating for a darkening that is not there. customers-more,
 * product-grow, product-service, ai-pipeline and home-ai all carry this pair.
 *
 * It sits BELOW the plates, which is a limit worth stating: a sheet above them
 * would flatten the figures, and one below cannot reach an opaque plate at all.
 * So the GROUND reads as one tone from the section's first pixel to its last,
 * while a plate crossing the line still carries the page layer's own 4.5 levels.
 */
.sec-customers-proof .pf__floor {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: clip;
  pointer-events: none;
}
.sec-customers-proof .pf__lift {
  display: block;
  position: sticky;
  top: 0;
  margin-top: -100vh;
  height: 100vh;
  background: var(--raised);
  opacity: 0.9;
}
/* The lift is a positioned box at z 0 and the page rail's contents are not
   positioned at all, so without this the sheet paints OVER the eyebrow and the
   headline and washes them to about 10% ink — which is exactly what the first
   render of it did. The rail is lifted above the floor and stays below the
   grain, which is the whole of the intended order: ground, lift, type, tooth. */
.sec-customers-proof .wrap {
  position: relative;
  z-index: 1;
}

/* ——— Head ————————————————————————————————————————
   Quiet and left-set. This is the only section on the customers page with an
   eyebrow and a headline at all — the hero, the quotes and the three supporting
   cards each open differently — so it does not need a device of its own. */
.sec-customers-proof .pf__title {
  max-width: 860px;
}

/* ——— The pair ————————————————————————————————————
   24 / 20 / 6. The last track carries nothing but the datum's run-off and the
   rail the governance note sits on. Every item is placed EXPLICITLY: the two
   datum runs share cells with the plates, and auto-placement would otherwise
   push a plate into a track of its own. */
.sec-customers-proof .pf__grid {
  position: relative;
  margin-top: var(--s96);
  display: grid;
  grid-template-columns: minmax(0, 24fr) minmax(0, 20fr) minmax(0, 6fr);
  /* Forty, not twenty-four: at twenty-four the two plates read as one mass with a
     slot cut in it, and the datum had almost no gutter to be seen crossing. */
  gap: var(--pf-gap);
  align-items: stretch;
}

.sec-customers-proof .pf__card--lead { grid-area: 1 / 1; }
.sec-customers-proof .pf__card--b    { grid-area: 1 / 2; }

/*
 * THE DATUM, IN TWO RUNS, MEASURED OFF THE GRID ITSELF.
 *
 * Percentage margins on a grid item resolve against the width of its grid area,
 * so a run placed in NOOT's own track can be stopped exactly one corner radius
 * in from that track's edge without anyone knowing how wide the track is. That
 * is the whole trick, and it is what makes the joint exact at every viewport.
 *
 *   run A  starts at the lead plate's right edge (back across the gutter by
 *          -gap) and ends at NOOT.left + --r-card: the point where the top-left
 *          arc finishes turning and the flat top edge begins.
 *   run B  starts at NOOT.right - --r-card, the mirror point, and runs out past
 *          the right of the screen.
 *
 * Between the two, the line IS NOOT's top rim — same width, same colour. The
 * previous build drew the line straight across both corners and let the arc hang
 * below it, which at 4x was an orphan quarter-arc dangling off a straight rule.
 */
.sec-customers-proof .pf__datum {
  align-self: start;
  height: 1px;
  margin-top: var(--pf-drop);
  background: var(--pf-line);
  transform-origin: left;
  z-index: 0;
}
.sec-customers-proof .pf__datum--a {
  grid-area: 1 / 2;
  margin-left: calc(var(--pf-gap) * -1);
  margin-right: calc(100% - var(--r-card));
}
.sec-customers-proof .pf__datum--b {
  grid-area: 1 / 3;
  margin-left: calc((var(--pf-gap) + var(--r-card)) * -1);
  margin-right: var(--pf-bleed);
}

/* ——— The plates ——————————————————————————————————
   Paper laid on the sheet, with a drawn rim at the datum's own weight. Opaque,
   and therefore the thing that occludes the line. */
.sec-customers-proof .pf__card {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  padding: var(--pf-pad);
  background: var(--pf-plate);
  border-color: var(--pf-line);
}

/* The stagger, and the ONLY place it now lives besides the masthead and the
   datum. Grid stretch resolves the row to max(A, B + drop), so the bottoms land
   on one line whichever plate is naturally taller. */
.sec-customers-proof .pf__card--b {
  --pf-rail: var(--pf-pad);
  margin-top: var(--pf-drop);
  padding-left: var(--pf-rail);
}

/* ——— Names ————————————————————————————————————————
   Sans, weight 500, at --pf-name on both plates. Serif here would be a second
   card-title family on the one page whose next section down draws three more
   cards; the figures are the serif voice, and the names must not borrow it. */
.sec-customers-proof .pf__name {
  flex: 0 0 auto;
  margin: 0;
  display: flex;
  font-size: var(--pf-name);
  font-weight: 500;
  letter-spacing: var(--tr-h);
  line-height: 1;
  color: var(--ink);
}

/*
 * THE MASTHEAD. A solid ink block, full plate width, whose bottom edge IS the
 * registration line — so the datum does not merely pass behind the lead plate,
 * it thickens into a block there and thins back to a hairline the moment it
 * leaves. The negative margins are pad PLUS the plate's 1px rim, so the ink
 * covers the rim rather than leaving a pale halo between black and fog, and the
 * block's bottom edge lands on --pf-drop exactly rather than one pixel under it.
 */
.sec-customers-proof .pf__card--lead .pf__name {
  align-items: flex-end;
  height: var(--pf-drop);
  margin: calc((var(--pf-pad) + 1px) * -1) calc((var(--pf-pad) + 1px) * -1) 0;
  padding: 0 calc(var(--pf-pad) + 1px) var(--pf-name-off);
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--r-card) var(--r-card) 0 0;
}

/*
 * THE SECOND NAME, LIFTED OUT OF ITS PLATE.
 *
 * It used to be a --pf-drop-deep box inside the card holding one word, which is
 * how the plate stagger got transmitted into the content and opened the hole
 * this pass exists to close. Out of flow, it sits --pf-name-off above the plate's
 * top edge — which is the datum — so its baseline and the reversed name's
 * baseline are the same line. `bottom: 100%` is the card's border box, so the
 * clearance is measured off the same edge the line lands on; `left` is the plate
 * rail, which is why it moves with the padding when the plate crops on a phone.
 *
 * The +1px is the plate's own top rim. An absolutely positioned box resolves
 * `bottom: 100%` against its containing block's PADDING box, which starts one
 * pixel inside the border box the datum lands on — without it the two names sat
 * on baselines one pixel apart, measured, which on a section whose entire
 * argument is "three objects, one y" is the wrong pixel to be casual about.
 */
.sec-customers-proof .pf__card--b .pf__name {
  position: absolute;
  left: var(--pf-rail);
  bottom: calc(100% + var(--pf-name-off) + 1px);
  margin: 0;
}

/* ——— Measures ————————————————————————————————————
   1 + 2, with the pair bottom-set. Both plates hand their leftover height to the
   interval between the lead figure and the pair's rule — but because the two
   plates now carry the SAME anatomy below the registration line, that leftover is
   only ever the difference in label line counts, a couple of dozen pixels, and
   never again the 201px the old `flex: 1 1 auto` on the hero could open. Bottom-
   setting the pair is also what registers the four supporting figures on one
   baseline across both plates. */
.sec-customers-proof .pf__ms {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

/*
 * THE CAP-LINE. The hero takes its height from its content and NOTHING else —
 * this was `flex: 1 1 auto`, i.e. "absorb whatever the stretched row hands the
 * plate", which is exactly how an interval ends up being an accident of which
 * sibling is taller.
 *
 * Calici measures the cap from the masthead's bottom edge. NOOT measures it from
 * its own top rim, which sits on the same y, less the rim and the padding that
 * plate already spends getting to its content box. Both lead figures therefore
 * start at --pf-drop + --pf-cap.
 */
.sec-customers-proof .pf__hero {
  flex: 0 0 auto;
}
.sec-customers-proof .pf__card--lead .pf__hero { padding-top: var(--pf-cap); }
.sec-customers-proof .pf__card--b .pf__hero {
  padding-top: calc(var(--pf-cap) - var(--pf-pad) - 1px);
}

.sec-customers-proof .pf__pair {
  flex: 0 0 auto;
  margin-top: var(--s40);
  border-top: 1px solid var(--pf-hair);
  padding-top: var(--s28);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--s32);
}
/* One vertical hairline between the supporting pair. It is the only mark in the
   section that divides rather than bounds, and it is what makes the pair read as
   a footing under the lead figure instead of as two more of the same. */
.sec-customers-proof .pf__pair .pf__m + .pf__m {
  border-left: 1px solid var(--pf-hair);
  padding-left: var(--s32);
}

.sec-customers-proof .pf__fig {
  display: block;
  font-family: var(--serif);
  font-weight: 400;
  font-optical-sizing: auto;
  line-height: 0.94;
  letter-spacing: -0.024em;
  color: var(--ink);
  white-space: nowrap;
}
.sec-customers-proof .pf__hero .pf__fig { font-size: var(--pf-fig-a); }
.sec-customers-proof .pf__pair .pf__fig { font-size: var(--pf-fig-c); }

/* Currency, multiplier and percent are units, not numerals. They sit on the same
   baseline a step down, which is how a printed table sets them and how HK$1.9m
   ends up the same height as +34% instead of a third smaller. */
.sec-customers-proof .pf__u {
  font-size: var(--pf-unit);
  letter-spacing: -0.008em;
}

/*
 * OPTICAL ALIGNMENT. Tabular figures pad every glyph to one advance, which is
 * what keeps the count-up from jittering — but it also means "1", "+", "−" and
 * "H" carry side bearings a "3" does not, and at 124px an unequal bearing reads
 * as the figure being indented against a label rail it is supposed to share.
 *
 * These were MEASURED, not guessed: the section was rendered at 2x, the leftmost
 * pixel darker than 140 was found inside each figure's box and inside its
 * label's, and the offset is whatever puts the two on the same column. The HK
 * value is the H's bearing scaled by --pf-unit, because that glyph is now set a
 * step down. Values are em, so they hold at every step of the ramp.
 */
.sec-customers-proof .pf__fig--sign { margin-left: -0.018em; }
.sec-customers-proof .pf__fig--one  { margin-left: -0.0625em; }
.sec-customers-proof .pf__fig--hk   { margin-left: -0.014em; }

.sec-customers-proof .pf__lbl {
  display: block;
  margin-top: var(--s16);
  font-size: var(--t-caption);
  line-height: 1.45;
  color: var(--slate);
  text-wrap: pretty;
  max-width: 28ch;
}
.sec-customers-proof .pf__pair .pf__lbl {
  margin-top: var(--s12);
  font-size: 14px;
  max-width: 22ch;
}

/* ——— The governance marker ————————————————————————
   Required copy, VISIBLE, and the section's only chroma. Right-set on the page
   rail — the rail of the empty third track — so the composition closes on a
   diagonal from the eyebrow at top-left.

   It is set as a margin note, not as page copy. Sienna italic at 14px on a 380px
   measure under a 32px sienna rule reads as a stamp applied to the sheet by
   someone other than the person who typeset the figures, which is exactly what it
   is. The rule is what does that work: without it the line reads as a caption the
   two plates own, and a caption saying the figures above it are unverified sits
   at the wrong rank — it has to sit ABOVE them in authority. */
.sec-customers-proof .pf__flag {
  margin-top: var(--s64);
  margin-left: auto;
  max-width: 380px;
  text-align: right;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  font-style: italic;
  color: var(--sienna);
  text-wrap: pretty;
}
.sec-customers-proof .pf__flag::before {
  content: '';
  display: block;
  width: 32px;
  height: 1px;
  margin: 0 0 var(--s16) auto;
  background: currentColor;
}

/* ——— Narrow desktop ————————————————————————————————
   The second plate's measure shrinks faster than its figure does, so the padding,
   the name and the registration all come in before the layout stacks. */
@media (max-width: 1180px) {
  .sec-customers-proof {
    --pf-pad: var(--s28);
    --pf-drop: calc(var(--s32) + var(--pf-name) + var(--pf-name-off));
    --pf-cap: var(--s48);
  }
  .sec-customers-proof .pf__grid { margin-top: var(--s80); }
  .sec-customers-proof .pf__pair { margin-top: var(--s32); padding-top: var(--s24); gap: var(--s24); }
  .sec-customers-proof .pf__pair .pf__m + .pf__m { padding-left: var(--s24); }
}

/* ——— Stack ——————————————————————————————————————
   Below 1040 the twenty-unit plate can no longer hold its lead figure beside the
   other one. The plates do NOT go back to the rail and the datum does NOT switch
   off; the stagger turns ninety degrees.

   MIRRORED CROPS, one clean rail edge each. Calici sits on the left rail and runs
   off the right of the screen; NOOT starts off the LEFT of the screen and closes
   exactly on the right rail; the datum becomes a single full-bleed rule in the
   gap between them, crossing both screen edges. The previous build had NOOT stop
   9% short of the right rail while Calici cleared the right edge by 20px, which
   read as two boxes that had each missed a mark. Their TEXT rails coincide —
   NOOT's padding-left absorbs its own bleed — so what is asymmetric is the
   plates' edges, not the type. */
@media (max-width: 1040px) {
  .sec-customers-proof .pf__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s32);
    margin-top: var(--s64);
  }

  .sec-customers-proof .pf__card--lead {
    grid-area: auto;
    order: 1;
    margin-right: calc(var(--pf-bleed) - var(--pf-crop));
  }

  /* Run A becomes the whole datum: a free full-bleed rule between the plates,
     touching no corner, so the tangent geometry it was cut for no longer
     applies. Run B has nothing left to emerge from. */
  .sec-customers-proof .pf__datum--a {
    grid-area: auto;
    order: 2;
    align-self: auto;
    margin: 0;
    margin-inline: calc(var(--pf-bleed) - var(--pf-crop));
  }
  .sec-customers-proof .pf__datum--b { display: none; }

  .sec-customers-proof .pf__card--b {
    --pf-rail: calc(var(--pf-pad) + var(--gutter) + var(--pf-crop));
    grid-area: auto;
    order: 3;
    margin-top: 0;
    margin-left: calc(var(--pf-bleed) - var(--pf-crop));
    margin-right: 0;
  }

  /*
   * THE SECOND NAME COMES BACK INSIDE. Stacked, there is no cap-line to hold —
   * the plates are not beside each other — and the gutter above NOOT is 32px,
   * which is not enough air to hang a 38px name in without it colliding with the
   * datum. So it returns to normal flow at the top of its own plate, and both
   * heroes go back to a plain measured clearance.
   */
  .sec-customers-proof .pf__card--b .pf__name {
    position: static;
    margin-bottom: var(--s28);
  }
  .sec-customers-proof .pf__card--lead .pf__hero { padding-top: var(--s40); }
  .sec-customers-proof .pf__card--b .pf__hero { padding-top: 0; }

  .sec-customers-proof .pf__flag { margin-top: var(--s48); }
}

/* ——— Phone ——————————————————————————————————————
   Same composition, smaller. The masthead, the mirrored crops, the full-bleed
   datum and the 1 + 2 anatomy all survive; only the numbers change. The
   supporting pair stays two-up — collapsing it to a column is exactly what turned
   this section into six evenly-weighted stops in an earlier build. */
@media (max-width: 720px) {
  .sec-customers-proof {
    --pf-pad: var(--s24);
    --pf-crop: var(--s24);
    --pf-name-off: var(--s16);
    --pf-drop: calc(var(--s24) + var(--pf-name) + var(--pf-name-off));
    --pf-fig-a: 72px;
    --pf-fig-c: 33px;
  }
  .sec-customers-proof .pf__title { max-width: none; }
  /* The measure decides the break on a phone; a set line break here would leave a
     short first line and a wrapped second. "before us." is held together with a
     non-breaking space, because without it the wrap strands "us." at the head of
     line two — the exact fault the desktop break exists to avoid. */
  .sec-customers-proof .pf__br { display: none; }

  .sec-customers-proof .pf__grid { margin-top: var(--s48); gap: var(--s24); }

  .sec-customers-proof .pf__card--b .pf__name { margin-bottom: var(--s24); }
  .sec-customers-proof .pf__card--lead .pf__hero { padding-top: var(--s32); }
  .sec-customers-proof .pf__pair {
    margin-top: var(--s28);
    padding-top: var(--s20);
    gap: var(--s16);
  }
  .sec-customers-proof .pf__pair .pf__m + .pf__m { padding-left: var(--s16); }
  .sec-customers-proof .pf__lbl { margin-top: var(--s12); max-width: none; }
  .sec-customers-proof .pf__pair .pf__lbl { font-size: 13px; line-height: 1.4; }

  /* Right-setting a two-line note on a 350px measure reads as an accident, so the
     flag returns to the rail and keeps its colour. Its rule turns with it — left
     on the rail the text now sits on, not stranded on the right margin the text
     no longer uses. */
  .sec-customers-proof .pf__flag {
    text-align: left;
    margin-left: 0;
    margin-top: var(--s40);
    max-width: none;
  }
  .sec-customers-proof .pf__flag::before { margin-inline: 0 auto; }
}

/* ===== section:customers-quotes ===== */
/* ————————————————————————————————————————————————
   customers-quotes
   Two quote cards on paper, sized by rank rather than by grid.
   ———————————————————————————————————————————————— */

.sec-customers-quotes {
  background: var(--paper);
  /*
   * The distance from the viewport edge to the page rail, derived from the same
   * two numbers .wrap uses. It is never smaller than the gutter, so the lead
   * card's field reaches the screen edge at EVERY desktop width — and because
   * the card's padding-left is this same value, its type lands exactly on the
   * page rail whether or not a scrollbar is taking width off 100vw.
   */
  --cq-bleed: calc((100vw - min(var(--page), 100vw - var(--gutter) * 2)) / 2);
  /* The two ranks of speech, declared once and stepped per breakpoint together
     so the interval between them can never drift. */
  --cq-lead: 44px;
  --cq-ans: var(--t-h-sm);
  /* How far the opening quotation mark hangs off the measure. Stated in em so
     one number serves both ranks and every breakpoint. */
  --cq-hang: 0.40em;
  overflow: clip;                 /* the bleed may overshoot by a scrollbar's width */
  padding-block: var(--section-y);
}

.sec-customers-quotes .cq {
  display: grid;
  grid-template-columns: minmax(0, 1.36fr) minmax(0, 1fr);
  gap: clamp(40px, 5vw, 92px);
  /* ONE shared bottom line; the tops stay ragged. This is the whole composition. */
  align-items: end;
}

.sec-customers-quotes .cq__q {
  display: flex;
  flex-direction: column;
  border-radius: var(--r-card);
}

/* — 1. the lead —————————————————————————————— */
.sec-customers-quotes .cq__q--lead {
  margin-left: calc(var(--cq-bleed) * -1);
  padding: 64px 72px 56px var(--cq-bleed);
  /* Square off the corners that are past the screen edge: a rounded corner that
     nobody can see is a sliver waiting to appear at some scrollbar width. */
  border-start-start-radius: 0;
  border-end-start-radius: 0;
  min-height: 340px;
}

/* — 2. the answer ————————————————————————————— */
.sec-customers-quotes .cq__q--b {
  padding: 44px;
}

/* — the speech ———————————————————————————————— */
.sec-customers-quotes .cq__said {
  margin: 0;
  font-size: var(--cq-ans);
  line-height: 1.36;
  /*
   * THE MEASURE, CAPPED IN EM — so the line holds the same NUMBER OF CHARACTERS
   * at every width instead of the same number of pixels. It does not bind in
   * either column layout (there the card is already narrower than this), and it
   * exists entirely for the stacked band: at 900px the cards go full width and
   * the lead was setting 90 characters to the line, which is a paragraph of
   * prose, not a pull quote. 17em is the desktop measure expressed in its own
   * type size, so the tablet reads exactly like the desktop, just smaller.
   */
  max-width: 19em;
  /* Hang the opening quotation mark into the margin so the first line is
     optically flush with the rest of the block. */
  text-indent: calc(var(--cq-hang) * -1);
  /* .h-sm-serif balances lines, which is right for a three-word heading and
     wrong for a six-line quote — it starves the last lines. */
  text-wrap: pretty;
}

/* The accent. Source Serif 4 ships a real italic at 400 on this page, so this
   is the drawn face and not a slant — which is the only reason it is allowed. */
.sec-customers-quotes .cq__said em { font-style: italic; }

.sec-customers-quotes .cq__said--lead {
  font-size: var(--cq-lead);
  line-height: 1.21;
  max-width: 17em;
  /* Tracking tightens as size grows. .h-sm-serif's -0.009em is set for 26px and
     is visibly loose four steps up the ramp; this is the --tr-h figure, which is
     what the system uses at this size everywhere else. */
  letter-spacing: var(--tr-h);
}

/* — the credit ———————————————————————————————— */
.sec-customers-quotes .cq__by {
  margin-top: auto;               /* holds the credit on the card's bottom edge */
  padding-top: 36px;
  display: flex;
  align-items: center;
  /* Flush right, under the end of the quote. A quote sets ragged on its right,
     so its last line always leaves the bottom-right of the card empty; the
     credit is the one element small enough to occupy that corner without
     competing, and it turns the card's interior into a diagonal. */
  justify-content: flex-end;
  gap: 14px;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--slate);
}
.sec-customers-quotes .cq__q--lead .cq__by { color: var(--sienna); padding-top: 44px; }

.sec-customers-quotes .cq__by-rule {
  flex: none;
  width: 28px;
  height: 1px;
  background: var(--hair-strong);
}
/* Even the credits carry the rank: the lead's rule is drawn longer. */
.sec-customers-quotes .cq__q--lead .cq__by-rule {
  width: 48px;
  background: color-mix(in srgb, var(--sienna) 34%, transparent);
}

/* — narrower desktops ————————————————————————— */
@media (max-width: 1200px) {
  .sec-customers-quotes { --cq-lead: 38px; }
  .sec-customers-quotes .cq__q--lead { padding: 52px 52px 44px var(--cq-bleed); }
}

@media (max-width: 1080px) {
  /* Both quotes step down together. Holding the answer at 26px while the lead
     dropped cost the pair its diagonal: measured at 1000px the two cards came
     within 6px of the same height and the composition went flat. */
  .sec-customers-quotes { --cq-lead: 33px; --cq-ans: 22px; }
  .sec-customers-quotes .cq__q--lead { padding: 44px 44px 40px var(--cq-bleed); min-height: 300px; }
  .sec-customers-quotes .cq__q--b { padding: 36px; }
  .sec-customers-quotes .cq__by { padding-top: 30px; }
  .sec-customers-quotes .cq__q--lead .cq__by { padding-top: 36px; }
}

/* — the phone ————————————————————————————————
   Stacks at 960, not at the usual 860: measured at 900px the answering card had
   grown to within 45px of the lead's height and the shared bottom line stopped
   reading as a composition — the pair went flat before it went narrow.

   It is stacked, not unstacked. The lead KEEPS its bled left edge — the field
   still runs off the screen and its type still lands on the rail — and the
   answer steps in from that rail and stops on the right one. So the phone reads
   as a stair descending to the right, anchored to one edge, rather than as two
   centred boxes; the boundary event and the asymmetry both survive.

   (The section above, customers-proof, already alternates its plates off the
   left and right edges on the phone. This one commits to a single edge instead,
   so two consecutive sections don't use one device.)
   ———————————————————————————————————————————— */
@media (max-width: 960px) {
  .sec-customers-quotes { --cq-lead: 30px; --cq-ans: 21px; }
  .sec-customers-quotes .cq {
    grid-template-columns: minmax(0, 1fr);
    gap: 36px;
    align-items: stretch;
  }
  .sec-customers-quotes .cq__q--lead {
    padding: 40px 40px 36px var(--cq-bleed);
    min-height: 0;
  }
  .sec-customers-quotes .cq__q--b {
    /* The step. Held to a share of the column so it is the same gesture at 390
       and at 900 instead of a fixed indent that vanishes on the wider phone. */
    margin-left: 11%;
    padding: 32px;
  }
}

/* — the tablet band ——————————————————————————
   Between the two-column layout and the phone, a stacked card runs the full
   844px of the rail and becomes a band: nine tenths grey, one tenth speech. The
   measure cap above fixes the LINE, and this fixes the FIELD — both cards stop
   short of the right rail so paper stays in the composition and the pair keeps
   its stair. Stated as a share of the column, never a pixel, so there is no
   width at which the remaining paper narrows to a sliver and reads as an error.
   ———————————————————————————————————————————— */
@media (min-width: 721px) and (max-width: 960px) {
  .sec-customers-quotes .cq__q--lead { max-width: calc(var(--cq-bleed) + 72%); }
  .sec-customers-quotes .cq__q--b { max-width: 66%; }
}

@media (max-width: 560px) {
  .sec-customers-quotes { --cq-lead: 27px; --cq-ans: 19px; }
  .sec-customers-quotes .cq { gap: 30px; }
  .sec-customers-quotes .cq__q--lead { padding: 32px 26px 28px var(--cq-bleed); }
  .sec-customers-quotes .cq__q--b { margin-left: 13%; padding: 26px 24px; }
  .sec-customers-quotes .cq__by { padding-top: 24px; gap: 12px; }
  .sec-customers-quotes .cq__q--lead .cq__by { padding-top: 28px; }
  .sec-customers-quotes .cq__by-rule { width: 20px; }
  .sec-customers-quotes .cq__q--lead .cq__by-rule { width: 34px; }
}

/* ===== section:customers-more ===== */
/* ————————————————————————————————————————————————————————————————
   customers-more — three supporting customers.

   THE PROBLEM THIS SOLVES: the brief hands this section three cards and no
   headline at all. Three identical plates on a repeat(3, 1fr) would be the whole
   section, and nothing in it would have been decided.

   THE COMPOSITION — one shelf line, three positions on it:
     · .cm-datum is a warm two-pixel rule that crosses the FULL viewport, not
       the page measure, so both screen edges crop it. It used to be a one-pixel
       --hair-strong line measuring about 10/255 against the fog: a datum the
       file staked its whole composition on that could only be found by sampling
       pixels. It now measures about 45 levels down from the ground, warm rather
       than grey, over two full rows.
     · the LEAD stands clear above it by --cm-rise, so the line runs behind its
       own fill; the SECOND rests its top edge exactly on it; the THIRD hangs
       --cm-drop below it. One line, three relationships, and the plates
       therefore descend left to right instead of sitting in a row. The previous
       build put a wide lifted plate on the left and an exact matched pair on the
       right, which is the reference's composition move for move.

   THE FOCAL EVENT is chromatic, not dimensional. The lead takes a warm paper
   fill (7% sienna in white), sienna for its name and the same sienna for its
   drawing's ink. It used to be separated from its rowmates by 32% of width and a
   5/255 fill delta, and that reads as a card that failed to align rather than as
   a card that was promoted — a reviewer parsed it as a layout bug. Nothing here
   spends the page's ONE peach SURFACE; customers-quotes owns that, and this
   section borrows only the ink that goes with it.

   THE TYPE: the lead's name is set at --t-h against the rowmates' --t-h-sm — 44
   against 26, same family, same 400 weight, each tracked by its own step of the
   ramp. That interval is the section's typographic event; a 26/16 jump on its
   own is a UI heading ratio, not a display step.

   THE DRAWINGS: three different scenes on the same 30° axes, ONE shared 92×92
   viewBox and one rendered size, so the objects stay comparable and only the
   staging differs — a bean sack standing low and wide, a receding row of boards,
   a tall display stand with a parcel in front of it. Their ink measures 79×58,
   78×78 and 59×82 inside that one frame, which is item 7 of ILLUSTRATION.md
   working as intended: the same box, and the smaller scene sits smaller in it.
   The previous build gave each scene its own frame width off a shared --cm-k,
   which is the machinery that made three drawings come out as one podium.
   ———————————————————————————————————————————————————————————————— */

.sec-customers-more {
  position: relative;
  isolation: isolate;

  /* How far the lead climbs above the shelf line, and how far the third plate
     hangs below it. Both are space tokens, not numbers typed to taste: the
     break has to be unmistakably a step and not a misalignment. */
  --cm-rise: var(--s64);
  --cm-drop: var(--s32);

  --cm-card-h: 500px;   /* the two rowmates, an exact matched pair */
  /* 564, not 556: at 556 the lead's bottom edge landed 8px above its first
     rowmate's — a near-miss reads as a mistake, where a clean shared baseline
     reads as intent. The lead and card B now close on the same line, and only
     card C steps down, by a single deliberate --cm-drop. */
  --cm-lead-h: 564px;   /* the lead carries a second line of 44px name */
  --cm-pad: var(--s32); /* the .card component's own padding, named so the
                           lead's drawing can measure its overrun against it */

  /* Warm paper for the lead. A mix, not a token surface: --peach is rationed to
     one surface per page and customers-quotes has spent it. --illo-ground is
     declared beside it wherever it is used, so the drawing's occlusion
     silhouettes are painted in exactly the fill they stand on. */
  --cm-tint: color-mix(in srgb, var(--sienna) 7%, var(--raised));
}

/* ——— Section grain ————————————————————————————————
   The page grain is body::after with `position: fixed`, so it paints one
   viewport and stops: on the phone this section runs past 844px and everything
   below that line came out flat, with a hard horizontal seam straight across the
   second card (measured: ground 245.6 above, 250.0 below; card fill 250.5 above,
   255.0 below). 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 sits ABOVE the plates — like the page layer, which is at z 9999 — so the
   cards carry the same tooth as the ground they stand on.
   (product-grow.css, product-service.css, ai-pipeline.css and home-ai.css all
   carry a version of this fix.) */
.sec-customers-more .cm-grain {
  position: absolute;
  inset: 0;
  z-index: 4;
  overflow: clip;   /* the tile can never spill past the band it belongs to */
  pointer-events: none;
}
.sec-customers-more .cm-grain::before {
  content: '';
  position: absolute;
  inset: 0;
  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.86' numOctaves='2' seed='19' 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: 190px 190px;
}
/*
 * THE LIFT — the half of the fix that takes the TONE step out of the ground.
 *
 * Our own grain restores the texture but not the level: the page's fixed layer
 * multiplies whatever viewport it covers by about 0.982 and nothing below it, so
 * the band it reaches measured 242.3 against 246.9 further down — a hard
 * full-width horizontal, which is what the reviewer saw. This sheet gives those
 * levels back over exactly that band.
 *
 * It is `sticky`, not `fixed`, because it has to track the viewport the way the
 * page layer does WITHOUT leaking outside this section; `margin-top: -100vh`
 * parks its unshifted position one viewport ABOVE the section, outside the clip,
 * so a capture in which the fixed layer never reaches this band gets no light
 * band compensating for a darkening that is not there. product-grow.css sets out
 * the same mechanism at length.
 *
 * It sits BELOW the plates, and that is a limit worth stating: a sheet placed
 * above them would flatten the type and the drawings with it, and one placed
 * below cannot reach an opaque white card at all. So the ground reads as one
 * tone from the section's first pixel to its last, while a card crossing the
 * band still carries the page layer's own 4.5 levels. Every section on the site
 * that paints its own grain takes that trade; the other half of the answer here
 * is that the phone column was cut from 2800px to about 950, so the band is a
 * sliver of the section rather than its bottom third.
 */
.sec-customers-more .cm-floor {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: clip;
  pointer-events: none;
}
.sec-customers-more .cm-lift {
  display: block;
  position: sticky;
  top: 0;
  margin-top: -100vh;
  height: 100vh;
  background: var(--raised);
  opacity: 0.9;
}

/* ---- the shelf line -------------------------------------------------- */
/*
 * Warm, and two pixels. At one pixel of --hair-strong this measured about 10/255
 * against the fog — a line the file's own comment staked the composition on and
 * that could only be found by sampling pixels. It is the one thing in the
 * section that is allowed to be a rule rather than a hairline, because it is the
 * datum the three plates are placed against.
 */
.sec-customers-more .cm-datum {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--section-y) + var(--cm-rise));
  height: 2px;
  background: color-mix(in srgb, var(--sienna) 30%, transparent);
  z-index: 0;
  transform-origin: left center;   /* [data-bar] wipes it in from the left */
}

/* ---- the bed --------------------------------------------------------- */
.sec-customers-more .cm-bed {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1.34fr 1fr 1fr;
  gap: 26px;
  align-items: start;
}

.sec-customers-more .cm-card {
  display: flex;
  flex-direction: column;
  height: var(--cm-card-h);
  /* Background, border, radius and padding all come from .card / .card--paper.
     Nothing here restates them; only the lead's fill is overridden, below. */
}

/* The three positions on the datum.
 *
 * The two rowmates are the SAME height, so the descent they share reads as one
 * diagonal: their top edges and their bottom edges are parallel. Previously each
 * card had its own height as well as its own offset, so both edges closed ragged
 * (bottoms at 651 / 659 / 691) and the stagger read as three cards that had
 * slipped rather than three placed on a slope. The lead keeps its own height —
 * it is the one card allowed to differ, because it is the one that leads. */
.sec-customers-more .cm-card--lead { height: var(--cm-lead-h); }
.sec-customers-more .cm-card--b,
.sec-customers-more .cm-card--c { height: var(--cm-card-h); }
.sec-customers-more .cm-card--b { margin-top: var(--cm-rise); }
.sec-customers-more .cm-card--c { margin-top: calc(var(--cm-rise) + var(--cm-drop)); }

/* The lead's warm plate. The border warms with it so the plate does not read as
   a tint dropped inside a grey outline. */
.sec-customers-more .cm-card--lead {
  background: var(--cm-tint);
  --illo-ground: var(--cm-tint);
  border-color: color-mix(in srgb, var(--sienna) 18%, var(--hair));
}

/*
 * THE NAME IS A MASTHEAD. It wears a type primitive and this file gives it
 * nothing but a colour and a place in the flex column: no size, no weight, no
 * family, no tracking — all four belong to .h / .h-sm-serif, including the
 * shared sidebearing correction that puts the serif stem on the same rail as the
 * sans sentence beneath it.
 */
.sec-customers-more .cm-card__title {
  margin-bottom: 0;
  flex: 0 0 auto;
}
.sec-customers-more .cm-card--lead .cm-card__title { color: var(--sienna); }

/*
 * The drawing zone is the growing child, so leftover card height is spent as air
 * above and below the drawing rather than as a hole under the title.
 *
 * The rowmates centre their drawing; the LEAD sets its own hard against the
 * right and lets it run the full width of the card's padding, so the one
 * promoted plate is also the one whose drawing is not sitting dead centre in a
 * box. Its ink still stops about 18px short of the border — a card edge must
 * never amputate a drawing (ILLUSTRATION.md), so the overrun is of the TEXT
 * RAIL, not of the plate.
 */
.sec-customers-more .cm-card__art {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  place-items: center;
  padding-block: var(--s12);
}
.sec-customers-more .cm-card--lead .cm-card__art {
  place-items: center end;
  margin-right: calc(var(--cm-pad) * -1);
}

/* ONE rendered size for the set, lowered from the site's card ceiling; the lead
   is lowered from the feature ceiling instead (product-grow promotes its lead
   the same way). Neither number is invented and neither raises a ceiling. */
.sec-customers-more .cm-illo { width: 100%; max-width: calc(var(--illo-card) * 0.96); }
.sec-customers-more .cm-illo--lead { max-width: calc(var(--illo-feature) * 0.52); }

/* Hidden-line removal is the global `.illo-mask` (20-components.css), painted in
   --illo-ground — set by .card--paper for the rowmates and re-set above for the
   lead's warm fill. Nothing to declare here. */

.sec-customers-more .cm-card__body {
  flex: 0 0 auto;
  max-width: 46ch;
}
/* The section's one italic, on the two words the section is about. */
.sec-customers-more .cm-card__body em { font-style: italic; }

/* The metric rail: the quietest line in the section. It sits on its own hairline
   and is pushed right, so each plate closes on a ledger figure instead of on
   more left-set prose. 14/400/--ash with no tracking is the site's smallest
   type, exactly as .eyebrow states it. */
.sec-customers-more .cm-card__metric {
  flex: 0 0 auto;
  margin-top: var(--s20);
  padding-top: var(--s16);
  border-top: 1px solid var(--hair);
  text-align: right;
  font-size: 14px;
  font-weight: 400;
  color: var(--ash);
}
.sec-customers-more .cm-card--lead .cm-card__metric {
  border-top-color: color-mix(in srgb, var(--sienna) 18%, var(--hair));
}

/* ——— Narrow desktop ———————————————————————————————— */
@media (max-width: 1080px) {
  .sec-customers-more {
    --cm-rise: var(--s48);
    --cm-drop: var(--s24);
    --cm-card-h: 452px;
    --cm-lead-h: 496px;
    --cm-pad: var(--s28);
  }
  .sec-customers-more .cm-card { padding: var(--cm-pad); }
  .sec-customers-more .cm-bed { gap: 20px; }
  .sec-customers-more .cm-illo { max-width: calc(var(--illo-card) * 0.84); }
  .sec-customers-more .cm-illo--lead { max-width: calc(var(--illo-feature) * 0.45); }
}

/* ——— Tablet and phone —————————————————————————————————
   NOT A STACK. Three full-width plates carrying three full-size drawings ran to
   2800px of scroll for a supporting row, and every trace of hierarchy went with
   it. The phone keeps the lead as a plate — name, drawing, sentence, figure —
   and turns the two rowmates into COMPACT ROWS: name and sentence on the left,
   the drawing small and right-set beside them, the figure on its rail across the
   foot. Both step in from the left, so the lead holds the column's full measure
   and the two supporting plates sit inside it — the desktop's hierarchy read
   down a column instead of across a row.

   THE SHELF LINE IS RE-REGISTERED, NOT SHRUNK. A phone column has no gap for a
   line to surface in, so the datum moves to the top edge of the SECOND plate and
   is drawn by that plate: full bleed out of both screen edges, continuous
   through the plate's own top border. The lead is above it and the third plate
   below it, which is the desktop's idea turned through ninety degrees. */
@media (max-width: 940px) {
  .sec-customers-more {
    --cm-rise: 0px;
    --cm-drop: 0px;
    --cm-pad: var(--s28);
    --cm-card-h: auto;
    --cm-lead-h: auto;
    --cm-indent: var(--s24);   /* how far the two supporting plates step in */
  }
  .sec-customers-more .cm-datum { display: none; }
  .sec-customers-more .cm-bed {
    grid-template-columns: minmax(0, 560px);
    gap: 16px;
  }
  .sec-customers-more .cm-card {
    height: auto;
    padding: var(--cm-pad);
  }
  .sec-customers-more .cm-card--b,
  .sec-customers-more .cm-card--c {
    margin-top: 0;
    margin-left: var(--cm-indent);
    display: grid;
    grid-template-columns: minmax(0, 1fr) 100px;
    grid-template-areas: 'title art' 'body art' 'metric metric';
    column-gap: var(--s20);
    align-items: start;
  }
  .sec-customers-more .cm-card--b .cm-card__title,
  .sec-customers-more .cm-card--c .cm-card__title { grid-area: title; }
  .sec-customers-more .cm-card--b .cm-card__art,
  .sec-customers-more .cm-card--c .cm-card__art {
    grid-area: art;
    align-self: center;
    padding-block: 0;
  }
  .sec-customers-more .cm-card--b .cm-card__body,
  .sec-customers-more .cm-card--c .cm-card__body { grid-area: body; margin-top: var(--s8); }
  .sec-customers-more .cm-card--b .cm-card__metric,
  .sec-customers-more .cm-card--c .cm-card__metric { grid-area: metric; }

  /* The datum, drawn by the plate that rests on it. */
  .sec-customers-more .cm-card--b { position: relative; }
  .sec-customers-more .cm-card--b::before {
    content: '';
    position: absolute;
    top: -1px;
    /* Anchored to the SCREEN's left edge, not centred on the plate: the plate is
       indented, so a viewport-wide rule centred on it stops short of the left
       edge by exactly that indent — the one thing a full-bleed line must not do.
       The bed is left-aligned inside .wrap at every width in this range, so the
       plate's distance from the screen edge is the gutter plus the indent and
       nothing else. */
    left: calc((var(--gutter) + var(--cm-indent)) * -1);
    width: 100vw;
    height: 2px;
    background: color-mix(in srgb, var(--sienna) 26%, transparent);
    z-index: -1;
  }

  .sec-customers-more .cm-illo { max-width: 100px; }
  .sec-customers-more .cm-illo--lead { max-width: calc(var(--illo-card) * 0.76); }
  /* The rail tightens with everything else on the phone. */
  .sec-customers-more .cm-card__metric { margin-top: var(--s12); padding-top: var(--s12); }
  .sec-customers-more .cm-card--lead .cm-card__art {
    place-items: center;
    margin-right: 0;
    padding-block: var(--s16);
  }
  .sec-customers-more .cm-card__body { max-width: none; }
}

@media (max-width: 620px) {
  .sec-customers-more { --cm-pad: var(--s24); --cm-indent: var(--s20); }
  .sec-customers-more .cm-bed { gap: 14px; }
  .sec-customers-more .cm-card--b,
  .sec-customers-more .cm-card--c {
    grid-template-columns: minmax(0, 1fr) 84px;
    column-gap: var(--s16);
  }
  .sec-customers-more .cm-illo { max-width: 84px; }
  .sec-customers-more .cm-illo--lead { max-width: calc(var(--illo-card) * 0.7); }
}

/* ===== 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; }
}
