/* ===== 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:product-hero ===== */
/* ===========================================================================
   product-hero — the Product page hero.

   THE PROBLEM THIS SOLVES. The homepage hero owns centre stage: a 90px display,
   four artifacts, a near-black slab cropped by the frame. An inner page hero
   that tried the same thing would flatten the site's rhythm — every page would
   open at maximum volume. So this one is deliberately quieter and left-set:

     · ONE step down in type. 64px serif (--t-h-lg) against the homepage's 90px.
     · ONE artifact instead of four, ~300px wide, and it carries no chart —
       nothing here is measured, so nothing here can measure wrong.
     · NO buttons. The nav already holds the page's actions.

   WHAT MAKES IT A COMPOSITION RATHER THAN A STACK. Three moves, and only three:

     1. THE HEADLINE OUTRUNS THE COLUMN. It is set across the full 1200px
        measure while the lede below it is held short, so the block is a wedge,
        not a rectangle, and a wide band of bare wash opens between the lede's
        rag and the card's left edge. That void is the point. Measured at 1440:
        headline ink ends at x=978, lede ink at x=729 — the display outruns the
        body by 249px. THE SAME SIGN IS REQUIRED AT 390 (see the mobile block);
        an earlier build let the lede run 70px WIDER than the headline there,
        which inverted the section's only structural idea on the one viewport
        that has no artifact left to carry the eye.
     2. THE CARD ABSORBS THE RULE. A hairline runs in from off-frame, closes the
        hero, and is pulled up so it lands on the third row divider inside the
        card — one continuous line that the artifact lifts off the paper as it
        crosses. It terminates at the page measure, under the card, and does not
        come out the other side. The rail takes z-index so the rule vanishes
        beneath it, and above 1340px the card steps 32px past the page measure
        into the gutter.
     3. THE PAGE LABEL IS THE SHARED EYEBROW, unadorned. It used to lead with a
        34px hairline on a 14px gap — an ornament no other page label carried.

   THREE THINGS THAT LOOK LIKE FUSS AND ARE NOT:
     · line-height 1.12 on the title. The masked-line reveal clips to the line
       box; at the stock 1.1 an italic descender ('everything' has two) lands
       within ~0.002em of the clip and can shave. 1.12 clears it with room, and
       being unitless it holds at 64px, 48px and 41px alike.
     · The rule's left end dissolves instead of hitting the frame square. A
       hairline that meets the frame at full strength reads as a section border,
       which is the next section's job, not this one's.
     · The serif cap sits right of where the sans lede below it starts, so the
       title carries the shared -0.026em sidebearing correction from 10-base.css
       like every other serif headline on the site.

   Everything is scoped under .sec-product-hero.
   =========================================================================== */

.sec-product-hero {
  /* Both ends of the stage come from the shared hero rule in 10-base.css.
     This file used to set a 124px foot of its own. */

  /* CLIP THE X AXIS. `.has-wash > .wash` is inset -5% on both flanks, and an
     element overhanging to the RIGHT still counts toward document scrollWidth
     even under the root's overflow-x:clip. Unclipped, this section measured
     1512 wide in a 1440 frame and 410 in a 390 frame — every capture carried a
     flat, grainless column down the right edge with a hard vertical seam where
     the wash stopped. Clipping at the section box keeps the wash's bleed doing
     its job (softening the flanks) without widening the document.

     X ONLY. `overflow: clip` on both axes would guillotine the artifact's cast
     shadow at the section's bottom edge and rule a grey band across the frame —
     the same trap home-hero hit. One axis clipped and the other visible is the
     one pairing CSS keeps as specified rather than forcing both to auto. */
  overflow-x: clip;
  overflow-y: visible;
}

/* — The headline ————————————————————————————————————— */
.sec-product-hero .ph-title {
  max-width: 15.6em;          /* ~1000px at 64px: line one all but fills the measure */
  line-height: 1.12;
  text-wrap: pretty;          /* not `balance` — the breaks are authored, not negotiated */

  /* The cap-stem sidebearing correction is the shared one in 10-base.css; this
     section used to carry its own -0.023em copy of it. */
}
.sec-product-hero .ph-title em {
  /* Source Serif's italic sits marginally left of its roman at the same size;
     a hair of positive tracking keeps the accent from looking compressed. */
  letter-spacing: -0.011em;
}
/* The narrow-column break is inert until the mobile block turns it on. */
.sec-product-hero .ph-br-m { display: none; }

/* — The lower row: lede on the rail, card in the right column ——————— */
.sec-product-hero .ph-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  align-items: start;
  gap: 40px;
  margin-top: 34px;
}
.sec-product-hero .ph-sub {
  max-width: 620px;
  color: var(--slate);
}

/* z-index is the whole trick: the rule is a later sibling, but a positioned
   element with z-index 1 paints over an unpositioned one regardless of order. */
.sec-product-hero .ph-rail {
  position: relative;
  z-index: 1;
  justify-self: end;
  width: 300px;
}

/* — The artifact ————————————————————————————————————— */
.sec-product-hero .ph-card {
  /* Asymmetric on purpose: the last row already carries 11px of its own bottom
     padding, so 8px here lands the ink 19px off the floor against 20px off the
     ceiling — even to the eye, where a matched 20px would read bottom-heavy. */
  padding: 20px 22px 8px;
}
/*
 * THE CARD CARRIES ITS OWN RAMP. At 13/18/14/13.5 the four type sizes inside
 * this card spanned 1.38x while the section around them spans 3.5x, so the card
 * read as one flat grey texture no matter how well spaced. 12/22/14/13 spans
 * 1.83x: the order line is now unmistakably the card's subject and the rows are
 * unmistakably its detail, which is the same relationship the page has between
 * its headline and its lede, one octave down.
 */
.sec-product-hero .ph-card__eye {
  font-size: 12px;
  line-height: 1;
  color: var(--ash);
  letter-spacing: 0;
}
.sec-product-hero .ph-card__id {
  margin-top: 10px;
  font-size: 22px;
  font-weight: 450;
  line-height: 1.2;
  letter-spacing: -0.014em;
  color: var(--ink);
}
.sec-product-hero .ph-card__id i {
  font-style: normal;
  color: var(--smoke);
  /* At 2px the middot read as a speck stuck to the 2 of 1042. A separator wants
     more air than the word-space it stands in for, not less. */
  padding-inline: 5px;
}

.sec-product-hero .ph-list {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
}
.sec-product-hero .ph-list li {
  display: grid;
  grid-template-columns: 16px 1fr auto;
  align-items: center;
  gap: 10px;
  padding-block: 11px;
  border-top: 1px solid var(--hair);
}
.sec-product-hero .ph-list svg {
  width: 16px;
  height: 16px;
  color: var(--smoke);
}
.sec-product-hero .ph-list__k {
  font-size: 14px;
  font-weight: 450;
  letter-spacing: -0.006em;
  color: var(--ink);
}
/*
 * THE ONE FIGURE COLUMN, AND AN HONEST ACCOUNT OF WHAT IT BUYS.
 *
 * The values used to be 'Fired 7:12pm', '3 items out', 'Visit 8' — ragged
 * phrases with a digit buried in each. They carried .tnum spans, and tabular
 * figures in a ragged phrase align nothing: the claim was decoration. Three
 * stamps of identical shape, set right, is a column that actually stacks, so
 * the values were rewritten to be one. One order, three systems, three times.
 *
 * What the tabular figures are worth here, measured rather than asserted: with
 * them the three spans render at exactly 48.14px from exactly x=1281.86, so the
 * colons sit in one column; without them they stagger by 0.22px, because
 * Inter's proportional digits are already close to tabular. That is a small
 * gain and it is stated as a small one — it is kept because this is the only
 * place in the section where figures stack, not because it rescues the row.
 */
.sec-product-hero .ph-list__v {
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--slate);
  text-align: right;
  white-space: nowrap;
}

/* — The closing rule ————————————————————————————————— */
/*
 * THE LINE RUNS IN FROM OUTSIDE THE FRAME AND IS ABSORBED BY THE RECORD.
 *
 * Geometry: left edge at x=0 (off the text rail, off the page measure, hard
 * against the frame), right edge exactly at the page measure. Written as a
 * bleed off `.wrap` rather than the usual 100vw + translateX centring trick,
 * because the two ends want different treatments and a centred 100vw box can
 * only be described symmetrically.
 *
 * WHY IT ENDS AT THE MEASURE RATHER THAN THE FRAME. Round one ran it full-bleed
 * with the right end dissolving from 86%, on the theory that it would pass
 * behind the card and re-emerge. Measured, it does not: at 1280 the card's right
 * edge IS the measure's right edge, leaving 40px to the frame, and the fade had
 * already consumed all of it. The line simply stopped, invisibly, behind a card
 * — a claimed effect that no width actually rendered. Terminating at the measure
 * makes the same overlap true at every desktop width instead: from 901px the
 * terminus sits under the card (flush with its right edge below 1340, a clear
 * 32px inside it above, once the card steps into the gutter), so the rule always
 * disappears beneath the artifact and never re-appears. One reading, one line,
 * no width where it lies.
 *
 * The left dissolve is a fixed 56px, not a percentage: a percentage tuned to
 * look right at 1440 was still ramping 60px past the text rail at 1024.
 */
.sec-product-hero .ph-rule {
  border: 0;
  height: 1px;
  margin-left: calc(50% - 50vw);
  width: calc(50vw + 50%);

  /*
   * THE PULL, AND WHY IT SURVIVED A CHALLENGE.
   *
   * It lands the rule on the third row's hairline inside the card. The card is
   * 218.06px tall and that border sits 168.17px down, so the pull is the 49.89px
   * remainder. (Widening the card's type ramp grew the card by 4.79px and moved
   * the divider down by the same 4.79px — everything that grew sits ABOVE the
   * list — so the remainder is unchanged. That is a coincidence of this layout,
   * not a law: change the ROW metrics and this number moves.)
   *
   * A review sampled the raster and reported a 0.93px step — rule centroid at
   * y=481.94 against divider at y=481.01 — and called the arithmetic a bluff.
   * Re-measured at deviceScaleFactor 2 it is not: both lines ink device rows
   * 972 and 973 with the same split, 0.4988 vs 0.4955 of their weight in the
   * lower row, which is a 0.002px difference in CSS pixels. Layout agrees:
   * rule box top 486.422, li border top 486.406.
   *
   * The 0.93px is a MEASUREMENT artifact, and the trap is worth writing down.
   * These two hairlines sit on different grounds — the rule crosses the warm
   * gradient wash, the divider sits on the card's flat white — so a centroid
   * taken against one background level per line reads the wash's own vertical
   * ramp as ink and drags the rule's centroid upward. Estimate the background
   * per column from the rows immediately above and below the band, and the
   * difference collapses to zero. Method preserved so the next challenge is
   * settled the same way rather than re-argued.
   */
  margin-top: -49.89px;

  background: linear-gradient(
    to right,
    transparent 0,
    var(--hair-strong) 56px,
    var(--hair-strong) 100%
  );
}

/* Above this width the gutter is deep enough that the card can step past the
   page measure without its shadow ever reaching the frame. */
@media (min-width: 1340px) {
  .sec-product-hero .ph-rail { margin-right: -32px; }
}

@media (max-width: 1080px) {
  .sec-product-hero .ph-row { gap: 32px; }
  .sec-product-hero .ph-title { max-width: 16.5em; }
}

/* Below 900px the artifact goes, per brief. What is left is a chapter mark, a
   headline, a paragraph and a rule — and that is a complete page opening, not a
   desktop layout with a hole in it: the measure resets and the rule drops back
   under the text instead of over a card that is no longer there. */
@media (max-width: 900px) {
  .sec-product-hero .ph-rail { display: none; }
  .sec-product-hero .ph-row { grid-template-columns: minmax(0, 1fr); }

  /* With the card gone there is nothing left to absorb the line, so a terminus
     at the page measure would just leave a bald gutter on the right and read as
     a rule that ran out of paint. Here it goes symmetric and full-bleed: it
     dissolves into the frame at both ends, which is the honest way to close a
     hero that no longer has an object in it. */
  .sec-product-hero .ph-rule {
    margin-left: 50%;
    width: 100vw;
    transform: translateX(-50%);
    margin-top: 48px;
    background: linear-gradient(
      to right,
      transparent 0,
      var(--hair-strong) 13%,
      var(--hair-strong) 87%,
      transparent 100%
    );
  }
}

/* ===========================================================================
   MOBILE — the wedge has to survive the loss of the card, or the section has
   no idea left in it.

   THE FAILURE THIS BAND EXISTS TO PREVENT. Capping the lede at 34ch produced,
   measured at 390: headline ink ending at x=290, lede ink at x=360. The body
   copy was set SEVENTY PIXELS WIDER than the headline, so the block went
   top-light and the one compositional claim the section makes — the headline
   outruns the column — read backwards. (The 34ch cap was also inert: 34ch of
   Inter at 18px is 385px, wider than the 350px column it was supposed to
   restrain. It never applied at any width.)

   THE SIZE IS NOT THIS SECTION'S TO SET. --t-h-lg is 34px through this band and
   the h1 takes it, the same as the other five inner heroes. The per-section
   ramp that used to live here (0.122vw - 5px, capped at 41) was one of six such
   divisors across six page titles; measured at 390 they rendered 31.8, 41,
   41.6, 42, 44 and 48px for the same rank.

   WHAT THE BAND DOES OWN IS THE LEDE MEASURE — 6.3x the headline's size, i.e.
   about 80% of the headline's longest line at whatever size the ramp hands out,
   so the wedge holds proportionally rather than at one width. At 390 that is
   214px of lede against a 268px longest headline line: the body sits a clear
   54px INSIDE the headline, the same sign as the 249px the desktop opens.

   AND ONE BREAK, AUTHORED. `text-wrap: balance` was doing the breaking here,
   and balance optimises line LENGTH, not line ENDINGS: it produced 'Everything
   the / counter needs, and', an article stranded at the end of line one. The
   break is authored at the same place so the article stays where it was set,
   rather than being re-negotiated by the wrapper at every width.
   =========================================================================== */
@media (max-width: 720px) {

  .sec-product-hero .ph-title {
    max-width: none;
  }
  .sec-product-hero .ph-br-m { display: block; }

  .sec-product-hero .ph-row { margin-top: 26px; }
  .sec-product-hero .ph-sub { max-width: calc(var(--t-h-lg) * 6.3); }
  .sec-product-hero .ph-rule { margin-top: 40px; }
}

/* ===== section:product-counter ===== */
/* ===================================================================
   product-counter — "Six things the counter does."

   The first capability field on the Product page, and the one that has to
   stay distinguishable from the second.

   ——— WHAT WAS WRONG WITH THE LAST CUT ————————————————
   It was an L of SIX IDENTICAL PLATES. The headline took the first two cells
   of a four-track field and six mist cards filled the rest, which kept this
   section clear of product-grow two bands down — but the bottom row was a
   flush four-up of cards at one tint, one padding, one drawing size, each
   object centred in its own box. After the headline the eye had nowhere to go,
   because nothing in the field outranked anything else and nothing crossed a
   boundary. The file's own most-defended rules were what cost it: "six plates,
   one rank" makes an inert field, and "every drawing stops 11 units short of
   its viewBox on all four sides" guarantees nothing ever overlaps or crops.

   ——— THE FIVE DECISIONS THAT CARRY IT NOW ————————————

     1. ONE PLATE IS THE LEAD, RANKED BY TINT AND POSITION. "Take the payment."
        is the component's own `.card--peach`: warm ground, sienna ink, sienna
        drawing, and the only chromatic surface on product.html. Peach is
        rationed to one surface per page and this page had spent none of it —
        product-counter and product-grow BOTH carried a comment saying the
        ration was not theirs to spend, and no other section on the page (hero,
        service, guest, record, trio, cta-band) touches it either. It is spent
        here, once.

        It also takes two of the four tracks and two of the three rows, and it
        starts --pc-rise ABOVE the row line its neighbours start on. Rank by
        tint and offset is not rank by drawing size, which is what items 3 and
        7 of the drawing system forbid; the lead's drawing is `.illo--feature`,
        the site's second size, not a number invented here.

     2. THE FIELD IS WOVEN, NOT BANDED. Four tracks, three rows. The head sits
        in the top two cells, ONE plate sits alone at top right with the void
        between them, the peach panel runs down the left, and the five mist
        plates fill the two right tracks — with the middle track dropped
        --pc-drop below the outer one. No plate in the field shares a top edge
        with the plate beside it, so there is no row to read as a rule of
        cards. The drop is `position: relative`, so it moves the plates without
        moving the tracks; the field carries the matching padding at its foot.

     3. EXACTLY ONE DRAWING BREAKS ITS PLATE, and it is the lead's. The
        contactless card floats clear above the reader and climbs out through
        the panel's top edge onto the fog. It is the only element in the set
        that crosses a boundary and the only one that is not centred in a box.
        Nothing in this file clips: there is no `overflow` rule anywhere, which
        is what lets the overhang happen at all.

        The overhanging part carries NO `.illo-mask`. Masks paint themselves in
        --illo-ground, which on this plate is peach; a mask above the plate's
        edge would have shown as a peach patch on the fog. Every mask in that
        drawing belongs to the reader, which sits entirely inside the panel.

     4. SIX SILHOUETTES, NOT SIX BOXES. One projection, six different shapes:
        a rail with a docket hanging off it, two swing tags hanging off a ring,
        an open crate, the reader with a card floating above it, a Z-fold day
        report standing folded, and the week board. The till drawer that used
        to sit on "Close the day." was the produce crate again — the same
        isometric cuboid at the same size, three cards apart — so it is gone;
        a standing zigzag of printed panels cannot be mistaken for a crate.
        Two objects have left the shared ground plane (the tags, the card).

        Stroke weight, caps, joins, fill and contrast are the global `.illo`
        class's job. THIS FILE SETS NONE OF THEM, and no element inside any
        drawing carries its own stroke, fill or opacity — that is the whole
        mechanism by which the site's drawings cannot drift apart again.

     5. ONE APPARENT SIZE ACROSS THE FIVE MIST PLATES — which is not one scale
        factor. In isometric a drawn width follows only the footprint and a
        drawn height only how tall a thing stands, so a hanging tag and a
        sitting crate can never share a bounding box however they are scaled.
        Each is normalised on the geometric mean of its projected box instead,
        which measures 94.6 / 101.3 / 101.4 / 103.0 / 103.3 across the five —
        a 1.09x spread. The box comes from `.illo--card`; this file only LOWERS
        that ceiling to leave air inside a four-track plate, which is the one
        adjustment the drawing system allows a section to make.

   THE MIST PLATE IS TITLE / DRAWING / BODY, top to bottom, with the drawing
   zone as the flex child that GROWS, so surplus height becomes symmetric air
   around the object rather than a hole under the title. THE LEAD IS THE OTHER
   WAY UP — drawing first, copy on the floor of the panel — so it does not read
   as a bigger version of its neighbours.

   Every rule is scoped under .sec-product-counter.
   =================================================================== */

.sec-product-counter {
  --pc-pad: var(--s24);
  --pc-mark-gap: var(--s20);
  /* The drawing's ceiling, LOWERED from --illo-card so a four-track plate
     keeps air either side of the object instead of running it to the padding.
     The site's card mark is still what sizes it; this is the same ceiling set
     lower, never a size of this section's own. */
  --pc-mark-cap: 172px;
  /* How far the middle track is dropped below the outer one, and how far the
     peach panel rises above its own row line. */
  --pc-drop: 56px;
  --pc-rise: 40px;
  /* How far the floating card climbs out through the panel's top edge. */
  --pc-breach: 62px;
  /* The lead's share of --illo-feature. It is the drawing this section is
     built around, so it takes the site's second size — lowered, never raised,
     and never a pixel figure of its own. */
  --pc-lead-illo: 0.76;
}

/* ——— The field ————————————————————————————————————
   Four tracks, three rows, held equal so the mist plates read as one family:
   where one body runs to a third line the surplus is spent as air around the
   drawing in the others, never as a gap under a title. The foot padding is the
   drop, so the middle track's last plate does not eat the section's rhythm. */
.sec-product-counter .pc-field {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  gap: var(--s24);
  padding-bottom: var(--pc-drop);
}

/* ——— The head, as the first two cells ————————————————
   Padded to the plates' own padding so the eyebrow starts on the same optical
   line as the card titles beside it — the head sits IN the field, on the
   field's own rail, rather than floating above it. The measure is held short
   of the two tracks it spans: the headline breaks where it is authored to
   break, and the rest of the cell is deliberate void. */
.sec-product-counter .pc-head {
  grid-column: 1 / span 2;
  grid-row: 1;
  padding: var(--pc-pad) var(--s48) 0 0;
}
/* The break is authored in the markup, so balancing must not fight it. */
.sec-product-counter .pc-title { text-wrap: wrap; }

.sec-product-counter .pc-card {
  display: flex;
  flex-direction: column;
  padding: var(--pc-pad);
}

/* ——— Placement ————————————————————————————————————
   a alone at top right, the peach panel down the left across rows 2-3, and the
   remaining four woven between the two right tracks. */
.sec-product-counter .pc-card--a { grid-column: 4; grid-row: 1; }
.sec-product-counter .pc-card--b { grid-column: 3; grid-row: 2; }
.sec-product-counter .pc-card--c { grid-column: 4; grid-row: 2; }
.sec-product-counter .pc-card--d { grid-column: 3; grid-row: 3; }
.sec-product-counter .pc-card--e { grid-column: 4; grid-row: 3; }

/* The middle track drops half a plate. `position: relative` moves the plates
   and leaves the tracks where they are, so the peach panel and the outer track
   keep their own alignment and only the middle one runs late. */
.sec-product-counter .pc-card--b,
.sec-product-counter .pc-card--d { position: relative; top: var(--pc-drop); }

.sec-product-counter .pc-card--lead {
  grid-column: 1 / span 2;
  grid-row: 2 / span 2;
  margin-top: calc(var(--pc-rise) * -1);
  padding: var(--s32);
}

.sec-product-counter .pc-mark {
  /* Takes the whole of the surplus and centres what it holds. */
  flex: 1 1 auto;
  display: grid;
  place-items: center;
  padding-block: var(--pc-mark-gap);
  /* A grid item's default `min-height: auto` would refuse to shrink below the
     drawing's intrinsic height and push the body off the foot of the plate. */
  min-height: 0;
}

.sec-product-counter .pc-mark .illo { max-width: var(--pc-mark-cap); }

/* The lead's drawing: left on the panel's own rail, and pulled up through the
   top edge by --pc-breach. Its ceiling is --illo-feature lowered, exactly as
   the mist plates lower --illo-card. */
.sec-product-counter .pc-mark--lead {
  place-items: start;
  padding-block: 0;
  margin-top: calc((var(--s32) + var(--pc-breach)) * -1);
  margin-bottom: var(--s24);
}
.sec-product-counter .pc-mark--lead .illo {
  /* The lead's frame is portrait, so this ceiling buys HEIGHT as much as
     0.76 of --illo-feature is 380px across and 532px down, which is the
     tallest object that still leaves the panel air at its foot. */
  max-width: min(100%, calc(var(--illo-feature) * var(--pc-lead-illo)));
}
.sec-product-counter .pc-card--lead .card__body { max-width: 34ch; }
.sec-product-counter .pc-lead-txt .card__body { margin-top: var(--s8); }

/* ——— Narrow desktop ————————————————————————————————
   --t-h drops to 36px at 1080, which shortens the headline; the field keeps
   its four tracks and the drawings step down one notch with them. */
@media (max-width: 1180px) {
  .sec-product-counter { --pc-mark-cap: 174px; --pc-drop: 44px; --pc-breach: 58px; }
  .sec-product-counter .pc-head { padding-right: var(--s24); }
}

/* ——— Tablet ————————————————————————————————————————
   Four tracks cut a mist plate to a ~18-character measure, which is where the
   body copy starts breaking mid-phrase, so the field drops to two. The head
   takes the whole of the first row and the peach panel the whole of the
   second: at two tracks neither can sit beside a plate without becoming a
   column of nothing. The lead keeps its tint, its anatomy and its overhang —
   they are what rank it — and only its two-track span is spent. */
@media (max-width: 1020px) {
  .sec-product-counter {
    --pc-pad: var(--s28);
    --pc-mark-gap: var(--s16);
    --pc-mark-cap: 172px;
    --pc-drop: 36px;
    --pc-rise: 0px;
    --pc-breach: 44px;
    /* Lowered again: at two tracks the copy sits BESIDE the drawing, and a
       380px object left it a 200px measure. */
    --pc-lead-illo: 0.6;
  }
  .sec-product-counter .pc-field {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    /* The head's row is explicitly AUTO. Left on the field's `1fr`, the row
       holding a two-line headline was stretched to a full plate's height and
       opened a 250px hole between the headline and the first card — the
       equal-row rule is for the plates, and the head is no longer one of
       them once it takes a row of its own. */
    grid-template-rows: auto auto;
  }
  .sec-product-counter .pc-head {
    grid-column: 1 / -1;
    max-width: 30ch;
    /* Clearance for the card that climbs out of the panel below. */
    padding: 0 0 var(--s32) 0;
  }
  /* AT TWO TRACKS THE LEAD TURNS SIDEWAYS. Full width with the copy still on
     the floor left three quarters of the panel as warm nothing; drawing on the
     left rail, copy set against it and dropped to the same baseline, and the
     panel is a band rather than a tall box with a hole in it. */
  .sec-product-counter .pc-card--lead {
    grid-column: 1 / -1;
    grid-row: 2;
    padding: var(--s28);
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: var(--s48);
    align-items: end;
  }
  .sec-product-counter .pc-lead-txt { padding-bottom: var(--s16); }
  .sec-product-counter .pc-card--a { grid-column: 1; grid-row: 3; }
  .sec-product-counter .pc-card--b { grid-column: 2; grid-row: 3; }
  .sec-product-counter .pc-card--c { grid-column: 1; grid-row: 4; }
  .sec-product-counter .pc-card--d { grid-column: 2; grid-row: 4; }
  .sec-product-counter .pc-card--e { grid-column: 1; grid-row: 5; }
  .sec-product-counter .pc-card--b,
  .sec-product-counter .pc-card--d { top: 0; }
  .sec-product-counter .pc-card--a,
  .sec-product-counter .pc-card--c,
  .sec-product-counter .pc-card--e { position: relative; top: var(--pc-drop); }
  .sec-product-counter .pc-mark--lead {
    margin-top: calc((var(--s28) + var(--pc-breach)) * -1);
  }
}

/* ——— Phone —————————————————————————————————————————
   THE MIST PLATE TURNS ON ITS SIDE, and it is the only breakpoint where it
   does.

   Stacked vertically the plates ran to nearly two thousand pixels of scroll,
   most of it the air above and below six centred drawings — a desktop column
   narrowed, not a phone layout. Here the mist plate is re-composed instead:
   text on the left rail, the drawing held on the right and centred against the
   pair of them. Every title starts on the same rail as the section headline,
   the drawings make one vertical run of objects down the right, and the field
   comes in at little over half the height.

   THE LEAD DOES NOT TURN. It keeps the desktop anatomy — drawing first at
   full width, copy on the floor — because that is what still ranks it once the
   two-track span and the offset are gone. Its drawing keeps the overhang; the
   plate above it is a phone-width mist plate whose own drawing sits on the
   right, so the card climbing out on the left has clear ground to climb into.

   Rows go back to their own content: equalising them at 390px would drag every
   plate up to the tallest body in the set. */
@media (max-width: 720px) {
  .sec-product-counter {
    --pc-pad: var(--s20);
    --pc-mark-gap: 0px;
    --pc-mark-cap: 112px;
    --pc-drop: 0px;
    --pc-breach: 30px;
  }
  .sec-product-counter .pc-field {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    grid-auto-rows: auto;
    gap: var(--s12);
    padding-bottom: 0;
  }
  .sec-product-counter .pc-head {
    max-width: none;
    padding-bottom: var(--s12);
  }
  /* One column: every plate takes the track it is given, in document order. */
  .sec-product-counter .pc-head,
  .sec-product-counter .pc-card { grid-column: 1; grid-row: auto; }
  .sec-product-counter .pc-card--b,
  .sec-product-counter .pc-card--d,
  .sec-product-counter .pc-card--a,
  .sec-product-counter .pc-card--c,
  .sec-product-counter .pc-card--e { position: static; top: auto; }

  .sec-product-counter .pc-card {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "title mark"
      "body  mark";
    align-content: center;
    column-gap: var(--s16);
  }
  .sec-product-counter .card__title { grid-area: title; }
  .sec-product-counter .card__body  { grid-area: body; }
  .sec-product-counter .pc-mark {
    grid-area: mark;
    align-self: center;
    /* The drawing's column is exactly the drawing: no padding block to open a
       gap the flex column needed and this one does not. */
    width: var(--pc-mark-cap);
  }

  .sec-product-counter .pc-card--lead {
    display: flex;
    flex-direction: column;
    margin-top: var(--s16);
    padding: var(--s20);
    column-gap: 0;
  }
  .sec-product-counter .pc-lead-txt { padding-bottom: 0; }
  .sec-product-counter .pc-mark--lead {
    width: auto;
    margin-top: calc((var(--s20) + var(--pc-breach)) * -1);
    margin-bottom: var(--s16);
  }
  .sec-product-counter .pc-mark--lead .illo { max-width: min(100%, calc(var(--illo-feature) * 0.42)); }
  .sec-product-counter .pc-card--lead .card__title { grid-area: auto; }
  .sec-product-counter .pc-card--lead .card__body { grid-area: auto; max-width: none; }
}

/* ===== section:product-service ===== */
/* ===================================================================
   product-service — "At the counter"

   THE SHAPE OF THE SECTION — three moves, in order of weight:

     1. A head set hard on the page rail with the right half of its band left
        empty. Nothing is coming to fill it.

     2. ONE wide artifact, and it is the focal point: the product's floor screen,
        starting on the rail and running off the right edge of the viewport. The
        room inside is wider than the plate at EVERY width, so the floor always
        continues past the frame.

     3. Six claims, ONE of them ranked, on a field that is deliberately not a
        grid: 2+1 / 3 / 1, no shared top edges, and two empty tracks at the foot
        that answer the empty half-band under the headline.

   WHAT WAS WRONG WITH THE VERSION THIS REPLACES — two measured faults.

   (a) NO GRAIN BELOW THE FOLD. The page grain is `body::after` with
       `position: fixed`, so on a section this tall it paints for exactly one
       viewport and stops dead: sampled in the left margin, sigma 2.40 above
       y=900 and sigma 0.000 — a mathematically flat digital canvas — below it,
       with a hard full-width horizontal at the seam. On a section whose whole
       surface argument is "paper", the texture evaporated exactly where the six
       claims sit. Fixed below, the same way ai-pipeline / home-ai /
       home-traction fix it.

   (b) SIX IDENTICAL PLATES IN AN EVEN 3x2. Same width, same height, same
       hairline, same internal layout, on near-white ground where a hairline
       barely registers — a row with no reason to look anywhere. It is also the
       page's third grid of six in a row. The field is now unequal in count,
       in ground, in title size and in top edge.

   WHY HAIRLINE PLATES AND A MIST LEAD
   This band sits between product-counter and product-grow, and BOTH of those
   are fog sections built out of isometric drawings. .card--paper is the
   system's second card variant and .card is its first; the lead uses the mist
   ground so that rank is carried by an existing surface rather than by a new
   one. Nothing here invents a ground, a radius or a border weight.

   WHY THE PLAN IS NOT AN .illo
   _src/ILLUSTRATION.md governs ILLUSTRATIONS — true isometric line art of real
   objects — and both neighbours are built out of it wall to wall. The plate here
   is not a drawing of a room; it is a rendering of the floor screen, the same
   class of thing as the order card inside .artifact in product-hero, which is
   also plain inline SVG. What this file does take from the drawing system is its
   discipline: ONE stroke weight for every mark, contrast set by colour and never
   by fading individual strokes, and no numbers invented that the tokens already
   name.

   Every rule is scoped under .sec-product-service.
   =================================================================== */

.sec-product-service {
  /*
   * THE PHONE/TABLET CROP, IN ROOM UNITS. Below 1080 the field is sized so that
   * exactly this many of the drawing's 1560 horizontal units are visible, at
   * every viewport width in the band — so the cut always lands in the same
   * authored gap of bare floor instead of wandering into a table as the window
   * changes. 1180 sits in the gap between T5/T10 (ends 1143) and T6 (starts
   * 1218); 940 sits between T4/T9 (ends 891) and T5/T10 (starts 999); 735 sits
   * between T3 (ends 707) and T9 (starts 762).
   */
  --svc-window: 1180;
  isolation: isolate;
}

/* ——— Section grain ————————————————————————————————
   `body::after` is `position: fixed`, so it paints one viewport and stops: this
   section measured sigma 2.402 / mean 247.5 above the fold and sigma 0.000 /
   mean 252 below it, a hard horizontal running the full width straight through
   the claim field. This layer is absolute to the SECTION, so the tooth is
   continuous however tall the section is and wherever it sits in the scroll.
   Same 180px tile, same multiply and the SAME 0.13 as the page grain, so the
   tooth below the fold measures the same as the page's own tooth (sigma ~2.4 in
   the left margin, against 0.000 before) rather than a lighter substitute for
   it. Where the fixed layer is still painting the two overlap and the top
   viewport reads a little grainier; that overlap is a full-page-screenshot
   artifact — a fixed layer follows the viewport, so a reader never sees a seam —
   and it cannot be cancelled from inside a section.
   (ai-pipeline.css, home-ai.css and home-traction.css carry the same fix.) */
.sec-product-service::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  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;
}

/* ——— Head ————————————————————————————————————————— */

.sec-product-service .svc__title {
  /* 660 puts the break after "for", so the italic clause lands whole on the
     second line. `pretty` rather than the stock `balance`: balance evens the two
     lines by pulling "wait for" down, which splits the accent across the turn. */
  max-width: 660px;
  text-wrap: pretty;
}

/* ——— The plan ————————————————————————————————————— */

/*
 * The plate starts on the page rail and its right edge lands exactly on the
 * viewport edge — `50% - 50vw` measures from the .wrap column, so it re-derives
 * itself at every width instead of guessing a bleed in pixels. Nothing is
 * declared here that .artifact already owns: ground, radius and elevation are
 * the component's.
 */
.sec-product-service .svc__plan {
  margin: var(--s96) 0 0;
  margin-right: calc(50% - 50vw);
  overflow: hidden;
  /* Squared where it leaves the screen. A radius on the off-screen corners
     curves the plate back INTO the viewport by 20px and the bleed reads as a
     card that merely stops short — which is the opposite of the intent. */
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

/*
 * THE ROOM IS WIDER THAN THE GLASS. On a desktop the field is a fixed intrinsic
 * width, not 100%: it keeps one constant scale and the frame simply shows more
 * of it as the window grows, so a table is 72px at 1200 and at 1600 and only
 * the crop moves. At 1440 that cut falls through the six-top on the east wall,
 * which is the intent — a table continuing past the glass.
 */
.sec-product-service .svc__field {
  display: block;
  width: max(1560px, 100%);
  max-width: none;
  height: auto;
}

/*
 * ONE WEIGHT for every mark in the field, and hierarchy carried entirely by
 * tone: architecture and seating whisper, the tables speak, and occupancy is
 * the only thing that gets solid ink. `non-scaling-stroke` keeps the hairline at
 * 1.2px whether the field is drawn at 1560 or at 720, so the plan never
 * thickens on a desktop or dissolves on a phone.
 */
.sec-product-service .svc__arch,
.sec-product-service .svc__seat,
.sec-product-service .svc__tbl {
  fill: none;
  stroke-width: 1.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.sec-product-service .svc__seat { stroke: var(--hair-strong); }
.sec-product-service .svc__arch,
.sec-product-service .svc__tbl { stroke: var(--smoke); }

/* Occupancy: the one solid mark in the field. */
.sec-product-service .svc__tbl--on { fill: var(--ink); stroke: none; }

/* Covers seated, set on the ink. Tabular, so the figures on three plates of
   different shapes sit on one optical axis. */
.sec-product-service .svc__cov {
  fill: var(--raised);
  font-family: var(--sans);
  font-size: 20px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

/* ——— The six ——————————————————————————————————————
 *
 * Three tracks, three rows, and NOT six equal cells:
 *
 *      ┌───────────────┬───────┐
 *      │  Table mgmt   │ Fast  │   row 1 — 2 + 1, and Fast hangs 48px lower
 *      ├───────┬───────┼───────┤
 *      │Kitchen│ Split │Retail │   row 2 — 3, stepping 0 / 24 / 48 down
 *      ├───────┴───────┴───────┤
 *      │Handhld│               │   row 3 — 1 on the rail, two tracks empty
 *      └───────┴───────────────┘
 *
 * Auto-placement does all of it: the lead is the only item carrying a span, and
 * the remaining five fall into the gaps it leaves, so DOM order and reading
 * order are the same order.
 *
 * `align-items: start` is load-bearing, not tidying. Stretched cells made every
 * short plate as tall as the tallest in its row, which put the last body line
 * far off its own foot and read as a bottom-heavy plate — the fault was the
 * stretch, not the padding, so the component's padding is left alone.
 */
.sec-product-service .svc__grid {
  margin-top: var(--s80);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s24);
  align-items: start;
}

/* The two steps that break the top edges. Applied as margin rather than
   align-self so a plate keeps its own content height and only its start moves. */
.sec-product-service .svc__card--d1 { margin-top: var(--s24); }
.sec-product-service .svc__card--d2 { margin-top: var(--s48); }

/* THE LEAD. Two tracks wide, the mist ground, the component's one permitted
   louder title, and its claim set on the plate's foot — so the whole height of
   row 1 (which the 48px step under Fast makes taller than a plate) is used as
   air between the two, instead of collecting under the copy. */
.sec-product-service .svc__card--lead {
  grid-column: span 2;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--s48);
}

/* The gap under a title, and a tighter leading on it: at the inherited body
   leading a 20px title carries ~4px of half-leading above its cap, which is
   what made the space above a title read looser than the space under the last
   body line. 1.28 balances the two without touching the component's padding. */
.sec-product-service .svc__card .card__title {
  line-height: 1.28;
  margin-bottom: var(--s12);
}
.sec-product-service .svc__card--lead .card__title { margin-bottom: 0; }

/* ——— Narrower ————————————————————————————————————— */

@media (max-width: 1080px) {
  .sec-product-service .svc__plan { margin-top: var(--s80); }
  /*
   * From here down the CROP is the constant and the scale floats: the field is
   * sized so exactly --svc-window of the drawing's 1560 units fill the glass,
   * whatever the viewport. The alternative — a fixed field width — walks the cut
   * across the room as the window narrows and sooner or later severs a table
   * through its cover count, which reads as overflow rather than as a crop.
   */
  .sec-product-service .svc__field {
    width: calc((100vw - var(--gutter)) * 1560 / var(--svc-window));
  }
  .sec-product-service .svc__grid { margin-top: var(--s64); }
}

/* Two tracks. Three cells of body copy in an 844px column runs a 25-character
   measure, which is a caption, not a paragraph. The lead takes the full width
   and keeps its rank by ground and title size; the right-hand column keeps one
   step of the stagger so the field still refuses a ruled top edge. */
@media (max-width: 900px) {
  .sec-product-service .svc__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s16); }
  .sec-product-service .svc__card--lead { grid-column: span 2; gap: var(--s24); }
  .sec-product-service .svc__card--d1 { margin-top: 0; }
  .sec-product-service .svc__card--d2 { margin-top: var(--s24); }
  .sec-product-service .svc__card { padding: var(--s24); }
}

/* ——— Phone ————————————————————————————————————————
 *
 * NOT the desktop field with the tracks taken out. Six full-width plates
 * stacked down a phone is 2,300px of scroll in which the card chrome earns
 * nothing: six identical borders, six identical radii, twelve identical
 * paddings, and no rank left at all once the lead is no longer wider than
 * anything.
 *
 * So the plate is stated ONCE, for the group, and the six become its rows,
 * divided by the same hairline — the product's own list UI, sitting directly
 * under the drawing of the product's own floor screen, with the lead row filled
 * in mist exactly as an occupied table is filled in the plan above it. This is
 * not a new surface: the ground (--raised), the hairline (--hair), the radius
 * (--r-card) and the mist are the .card / .card--paper component's own, and no
 * value is invented here. It costs about a third of the scroll.
 */
@media (max-width: 720px) {
  .sec-product-service .svc__title { max-width: 100%; }
  .sec-product-service .svc__plan { margin-top: var(--s64); }
  .sec-product-service { --svc-window: 940; }

  .sec-product-service .svc__grid {
    margin-top: var(--s48);
    display: block;
    background: var(--raised);
    border: 1px solid var(--hair);
    border-radius: var(--r-card);
    overflow: hidden;
  }
  .sec-product-service .svc__card {
    background: none;
    border: 0;
    border-radius: 0;
    margin: 0;
    padding: var(--s20) var(--s24);
  }
  .sec-product-service .svc__card + .svc__card { border-top: 1px solid var(--hair); }
  .sec-product-service .svc__card--lead {
    display: block;
    background: var(--mist);
  }
  /* One rank inside the list; the fill alone marks the lead, so the title
     steps back down to the size its five siblings carry. */
  .sec-product-service .svc__card--lead .card__title {
    font-size: var(--t-body-lg);
    letter-spacing: -0.01em;
    margin-bottom: var(--s12);
  }
}

/*
 * The small-phone step. Below 480 the 940-unit window puts a table under 35px
 * and a cover figure under 10, so the window closes to 735 — T1, T7, T2 and T8
 * with both their cover counts, at 36px a table on a 390 screen — and the
 * figures take one size up in room units to hold their legibility at that
 * scale. Shrinking the whole room into a 370px column instead would put a table
 * at 17px and a figure at 5, which is the trade this section refuses to make.
 */
@media (max-width: 480px) {
  .sec-product-service { --svc-window: 735; }
  .sec-product-service .svc__cov { font-size: 24px; }
}

/* ===== section:product-grow ===== */
/* ===================================================================
   product-grow — "Six things that fill it"

   TWO DECISIONS THIS FILE MAKES.

   1. THERE IS NO CARD, AND ONE OF THE SIX IS THE LEAD.

   product-counter sits two sections up the same page: six mist cards in a 3x2
   grid under a headline that also opens "Six things". This section used to be
   that same object again — same anatomy, same frame, four hundred pixels apart
   — so the card went, and the six entries hang off two ruled shelves on the
   bare fog with a plate number in the margin.

   That fixed the redundancy and left a worse problem in its place: six cells of
   exactly equal weight in an even 3x2, run twice, with two hairlines whose
   asymmetry was real in the pixels and invisible on the page. Equal cells are
   the named anti-pattern and repeating them does not make a composition.

   So the field is four columns, and entry 01 takes two of them: a drawing at
   about twice the linear size of its siblings, its sentence set as a lede, and
   the header sharing its measure so the headline, the plate number, the drawing
   and the sentence all start on one edge. Row two is indented a whole column,
   which opens a quarter-width void under the lead and steps the sheet right as
   it descends. The shelves say the same thing at a length you can see: A starts
   on the page rail and runs off the right edge of the screen, B starts a column
   in and stops on the rail — about 892px against 1320+, anchored at opposite
   ends rather than fading at both.

   No promotion by chrome: the lead has no card, no tint, no plate and no
   shadow. It is bigger, and that is all. The page's one peach surface is not
   this section's to spend, and a near-empty dark rectangle has been rejected
   here before.

   2. THE SECTION PAINTS ITS OWN GRAIN, AND CANCELS THE PAGE'S.

   The page grain is body::after, `position: fixed`, one viewport tall. Live it
   is right; in a full-page capture Chromium paints a fixed layer once, at the
   top, so the texture stopped dead at y=900 on desktop and y=844 of a 2446px
   mobile section, leaving a hard full-width seam through the row-2 drawings and
   two thirds of the phone view on a flat, lighter ground. `.gr__grain` covers
   the section's real height at every width and at the page layer's own
   strength; `.gr__lift` takes back the ~1.8% the fixed layer darkens whatever
   part of the section is on screen, so the two never disagree. Measured after:
   mean 245.4-246.1, sd 4.6-5.3, top to bottom, on both viewports and on
   product.html as well as on this section's own preview.

   ONE LEFT EDGE PER COLUMN. Plate number, drawing, title and sentence all start
   on their column's rail. The numbers used to hang in a 46px margin while the
   drawings were centred in their cells, which put four competing left edges in
   the section and gave every drawing a different 20-61px inset.

   ONE OPTICAL SIZE, NOT ONE FRAME. The six shared a 160x130 box, which is not
   the same thing: their ink measured 126 to 208px wide and 04 read as the runt.
   Each drawing is now scaled in the markup so that sqrt(bounding area x
   greatest dimension) is identical, left-aligned to x=0 and stood on a common
   ground line at the foot of a shared 132x124 frame. Optical spread 1.24x ->
   1.10x, width spread 1.65x -> 1.31x; what remains is the objects' honest
   difference in proportion — a flat stack of stamp cards IS wider and lower
   than a standing guest record. Stroke weight, caps, joins, fill and contrast
   stay the global `.illo` class's job and this file sets none of them; the
   global `vector-effect: non-scaling-stroke` is why the lead's drawing carries
   the same 1.4px line as the small ones at twice the size.

   NO DRAWING HERE CARRIES A FILL, and that is a change. Five of the six always
   did their hidden-line removal by splitting the occluded line — the beam under
   each ticket clip, the envelope's far edge behind the letter, the table legs
   under the slab — but 04 used an `.illo-mask` silhouette in --illo-ground, and
   a flat patch of --fog painted over a grained ground is 4.5 levels lighter
   than the paper around it: the guest card rendered as a pale cut-out. Its two
   occluded edges are now computed against the card's silhouette and stopped
   there, like every other drawing in the set, so the section needs no
   --illo-ground at all.

   Every rule is scoped under .sec-product-grow.
   =================================================================== */

.sec-product-grow {
  position: relative;
  isolation: isolate;

  /* Page rail to screen edge, for the bleeding shelf. */
  --gr-bleed: max(var(--gutter), (100vw - var(--page)) / 2);

  /* One drawing size for the five siblings. A ceiling, lowered from the site's
     --illo-card to fit a quarter-width column, never a new number
     (_src/ILLUSTRATION.md item 8). */
  --gr-mark-w: clamp(150px, 80%, 220px);

  /* And one for the lead, lowered from --illo-feature the same way. At 1440
     this lands at 430px against the siblings' 220 — the promotion is size and
     nothing else. */
  --gr-lead-w: min(100%, calc(var(--illo-feature) * 0.86));
}

/* ——— The grain ————————————————————————————————————
   The filter is the PAGE's, not home-capability's, and that is deliberate. The
   smooth-noise version (feTurbulence + saturate, no transfer function) measured
   sd 0.85 against the page layer's 4.71 — a haze, not a grain, and it left the
   lower two thirds of the section reading as the flat ground it was meant to
   replace. The discrete transfer function is what makes the tile hard black or
   nothing, and it is what the texture is made of.

   Strength is matched to the page layer's, not halved: sd 4.6 below the fixed
   layer's floor against sd 5.3 above it, where it read 0.00 before. Different
   seed, frequency and tile size from the page's, so the two do not correlate
   into a moire where they overlap.

   Grain alone was not enough, and this is the part that took two attempts to
   see. Restoring the texture leaves the TONE wrong: the fixed layer multiplies
   the band it covers by about 0.982 and nothing below it, so the section still
   read as two grounds ~4.5 levels apart. At 1:1 the grain masks that; the
   moment anyone looks at a scaled-down view the noise averages out and the step
   is the first thing they see, which is exactly how it was caught. `.gr__lift`
   below is what cancels it. */
.sec-product-grow .gr__grain {
  position: absolute;
  inset: 0;
  /* No z-index: a stacking context here would isolate the multiply from the
     section's own background and the grain would have nothing to darken.
     `clip` rather than `hidden` — hidden makes this a scroll container and the
     lift below would never stick to anything. */
  overflow: clip;
  pointer-events: none;
}
.sec-product-grow .gr__grain::before {
  content: '';
  position: absolute;
  inset: 0;
  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='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.78' numOctaves='2' seed='23' 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(%23g)'/%3E%3C/svg%3E");
  background-size: 210px 210px;
}

/*
 * THE LIFT — the half of the fix that makes the seam actually go away.
 *
 * Painting our own grain restores the texture but not the TONE: the page's
 * fixed layer multiplies the first viewport by about 0.982 and nothing below
 * it, so the band it covers stays ~4.5 levels darker than the rest of the
 * section. Grain masks that at 1:1 and stops masking it the moment anyone
 * looks at a scaled-down view, which is exactly how the seam was caught.
 *
 * The lift is a viewport-tall sheet of --raised at 51%, which lightens the
 * ground by the same ~4.5 levels the page layer is about to take away, so the
 * two cancel and the section reads as one tone from top to bottom. It is
 * `sticky`, not `fixed`, because it has to track the viewport the way the page
 * layer does WITHOUT leaking outside this section: a fixed layer cannot be
 * clipped to a band (an ancestor that could clip it would also become its
 * containing block and stop it tracking the viewport at all), while a sticky
 * one is bounded by its containing block for free.
 *
 * `margin-top: -100vh` is the other half, and it is not a nudge. A sticky
 * element that has not been shifted yet sits at its static position, so with a
 * static position at the section's own top the lift painted a viewport-tall
 * light band across any section that had scrolled past — measured on
 * product.html's full-page capture, where the fixed layer is painted once at
 * the document top and never reaches a band 3585px down, the lift compensated
 * for a darkening that was not there and left a 5-level seam of its own. Parked
 * one viewport ABOVE the section, its unshifted position falls outside the clip
 * and it paints nothing; the moment the viewport top enters the section, sticky
 * shifts it down onto the viewport and it lines up with the fixed layer again.
 * The two are then in register at every scroll offset and in both captures.
 */
.sec-product-grow .gr__lift {
  display: block;
  position: sticky;
  top: 0;
  margin-top: -100vh;
  height: 100vh;
  background: var(--raised);
  opacity: 0.51;
}

.sec-product-grow .wrap { position: relative; z-index: 1; }

/* ——— Head ————————————————————————————————————————
   The header holds the LEAD's measure — two of four columns — so the headline
   sits on the same rail as the plate number, the big drawing and the sentence
   below it. It used to be indented onto column 4, where it aligned to nothing:
   not the eyebrow above it, not the text under it, not the centre of its own
   measure. The void it leaves to the right is the composition. */
.sec-product-grow .gr__head {
  max-width: calc((100% - var(--gutter) * 3) / 2 + var(--gutter));
}


/*
 * The break is explicit. Balanced wrapping put "that" with "fill it", which
 * buries the one italic accent mid-line; the accent gets its own line.
 * Tighter than the token default: at this size Source Serif's word spaces open
 * up and "Six things that" set loose at --tr-h (-0.015em).
 */
.sec-product-grow .gr__title {
  letter-spacing: -0.022em;
  text-wrap: wrap;
}

/* ——— The field ————————————————————————————————————
 * One grid holds the shelves and the entries, so a shelf is genuinely the top
 * edge of the row beneath it rather than a rule that happens to sit near it.
 */
.sec-product-grow .gr__field {
  margin-top: var(--s64);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: 0;
}

/*
 * THE TWO SHELVES. Ground, height and colour are the global .rule component's;
 * this file only decides how far each one runs and which end it is anchored to.
 * A bleeds right off the screen from the page rail; B starts a column in and
 * stops dead on the rail. The page clips at the root (10-base.css), so the
 * bleed adds no scrollbar.
 */
.sec-product-grow .gr__shelf {
  grid-column: 1 / -1;
  /*
   * The ONE thing this section changes about .rule, and the reason is the whole
   * design: with the cards gone these two hairlines are the only structure in
   * the section, and --hair (#ececec) on --fog (#fafafb) is a 1.4% step that
   * measured as very nearly invisible in the render. --hair-strong is the token
   * that exists for a hairline that has to carry weight. Nothing else here
   * restates a component's paint.
   */
  background: var(--hair-strong);
}
.sec-product-grow .gr__shelf--a {
  width: calc(100% + var(--gr-bleed));
  justify-self: start;
}
.sec-product-grow .gr__shelf--b {
  grid-column: 2 / -1;
  margin-top: var(--s96);
}

/*
 * AN ENTRY. Number, drawing, title, sentence — one block, one left edge, no
 * margin column. `align-content: start` is not needed because nothing here
 * stretches; the entry is as tall as what is in it, which is the point of
 * losing the card.
 */
.sec-product-grow .gr__item {
  padding-top: var(--s24);
}

.sec-product-grow .gr__num {
  display: block;
  font-size: 13px;
  font-weight: 450;
  line-height: 1;
  color: var(--ash);
  letter-spacing: 0.02em;
  /* Hung ~10px under the shelf, where it reads as that line's label rather than
     as a caption floating above a drawing. */
  margin-top: -14px;
  margin-bottom: var(--s24);
}

/* `.illo` is width:100% by default; here the frame is pinned so the five
   siblings land at one apparent size whatever their column measures, and the
   lead lands at one of its own. The ink inside the frame is flush left, so the
   drawing's leftmost stroke sits on the same rail as the title under it. */
.sec-product-grow .gr__mark { display: block; }
.sec-product-grow .gr__mark .illo {
  width: var(--gr-mark-w);
  max-width: 100%;
}

.sec-product-grow .gr__t { margin-top: var(--s20); }
.sec-product-grow .gr__b { margin-top: var(--s12); max-width: 30ch; }

/* ——— The lead ————————————————————————————————————— */
.sec-product-grow .gr__item--lead { grid-column: span 2; }
.sec-product-grow .gr__item--lead .illo { width: var(--gr-lead-w); }
.sec-product-grow .gr__item--lead .gr__t { margin-top: var(--s28); }
.sec-product-grow .gr__item--lead .gr__b { max-width: 34ch; }

/*
 * TWO placements, and auto-flow does the rest. The lead takes two columns, so
 * 02 and 03 fall into 3 and 4 behind it; 04 is pinned to column 2, which is the
 * indent that opens the void under the lead, and 05 and 06 follow it across.
 * Stated as four `grid-row`s and six `grid-column`s instead, every one of them
 * outranked the phone stylesheet's reset on specificity and the 390px render
 * came out as a squeezed four-column grid with the lead's drawing gone.
 */
.sec-product-grow .gr__item:nth-of-type(4) { grid-column: 2; }

/* ——— Narrow desktop ————————————————————————————————
   --t-h drops to 36px at 1080 and the columns lose ~50px each, so the two
   drawing ceilings come down with them. Nothing else moves. */
@media (max-width: 1080px) {
  .sec-product-grow {
    --gr-mark-w: clamp(140px, 86%, 190px);
    --gr-lead-w: min(100%, calc(var(--illo-feature) * 0.74));
  }
}

/* ——— Tablet ————————————————————————————————————————
   Below ~1000px a quarter column is too narrow to hold a drawing and a
   sentence, so the field goes to two columns. At two columns a shelf is no
   longer the top edge of a row, so the shelves are withdrawn and each entry
   rules its own top instead: the same specimen sheet stated as a ruled table.
   The lead keeps its double width — it now spans the whole field — and keeps
   its larger drawing and its lede, so the hierarchy survives the reflow. */
@media (max-width: 1000px) {
  .sec-product-grow {
    --gr-mark-w: clamp(150px, 62%, 210px);
    --gr-lead-w: min(62%, calc(var(--illo-feature) * 0.8));
  }

  /* A cap, not a measure: `22ch` resolved against the header's own 17px sans
     and came out at 235px — one pixel of slack around a 36px headline, which is
     a bad wrap waiting for a font to load differently. A length that cannot
     collide with the line it holds. */
  .sec-product-grow .gr__head { max-width: 420px; }

  .sec-product-grow .gr__field {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: var(--s64);
    margin-top: var(--s48);
  }
  .sec-product-grow .gr__shelf { display: none; }

  .sec-product-grow .gr__item { border-top: 1px solid var(--hair-strong); }
  .sec-product-grow .gr__item:nth-of-type(4) { grid-column: auto; }
  .sec-product-grow .gr__item--lead { grid-column: 1 / -1; }
  .sec-product-grow .gr__num { margin-top: 0; }
}

/* ——— Phone —————————————————————————————————————————
   One column, same anatomy again: number, drawing, title, sentence, each on the
   one rail. The lead still leads — its drawing runs the full measure where the
   others are held back to ~74% — so the phone gets the same first beat as the
   desktop instead of six identical stacked blocks. */
@media (max-width: 560px) {
  .sec-product-grow {
    /* A phone rail is ~350px at 390. Lowered — never raised — as
       _src/ILLUSTRATION.md item 8 allows. */
    --gr-mark-w: clamp(150px, 74%, 220px);
    --gr-lead-w: 100%;
  }

  .sec-product-grow .gr__head { max-width: none; }

  .sec-product-grow .gr__field {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--s48);
    margin-top: var(--s40);
  }

  .sec-product-grow .gr__item--lead .gr__t { margin-top: var(--s20); }
  .sec-product-grow .gr__b { max-width: 34ch; }
}

/* ===== section:product-guest ===== */
/* ===================================================================
   product-guest — "The guest arrives already known"

   COMPOSITION. Two unequal rails, 0.68 / 1.32, held out of register on
   purpose. The right rail runs eyebrow → headline → four ruled notes. The
   left rail holds one tall record card that begins ABOVE the eyebrow and
   runs OFF the left edge of the screen — its left radius is clipped by the
   viewport, not parked politely inside the text grid. Off-register on both
   axes: the artifact is not a column-mate of the headline, it is an object
   the headline is written beside. Nothing here is a centred column and
   nothing is an even grid — the four notes are a 2x2 whose right pair drops
   half a step, so the section's structure is four hairlines at four
   different heights, not two long bars.

   THE HEADLINE IS THE VOICE, AND IT IS SIZED LIKE IT. It runs at display
   scale (clamp 40 → 66px) — roughly 2x the largest figure inside the card at
   every width above 1045px and never less than 1.6x anywhere. The previous
   build set it at --t-h (44px desktop / 28px mobile) against a 34/28px
   figure: on a 390px screen the two were literally the same size in the same
   face, which is the one thing a section's own voice must never be. Line one
   is also long enough now to reach past the second note column and set that
   column's right-hand edge before the notes do, so the top-right is
   composed void rather than an unfilled grid cell.

   WHY THE CARD IS THE ONLY OBJECT. The two sections either side of this one
   on product.html are both icon-and-card grids (product-grow above,
   product-record's ledger below). Repeating a filled-card treatment a third
   time would flatten the page, so the notes here carry no fill at all — a
   rule, a title, a line of body, and nothing between them for a hole to open
   in. The only surface in the section is the record itself, and the lead note
   earns its rank on rule weight and type size rather than on a slab.

   THE ONE INKED THING is the allergy strip inside the card: ~50px tall, and
   dark because a kitchen must not miss it. Its radius is half the card's, so
   it reads as a stamped bar among hairlines rather than as a third pill
   beside the two real chips.

   HONEST MARKS. Each visit row carries a bar whose width is that visit's
   spend over the largest of the four (HK$755) — 90.1 / 54.6 / 26.0 / 100.
   No pixel floor, no minimum width: the HK$196 bar really is a quarter of
   the HK$755 one. Both money and dates are tabular so the four figures
   align on their digits.

   OPTICAL, NOT METRIC. Nothing on the rail starts at the same number. The
   hairline that opens each note is the datum; the eyebrow's W hangs a pixel
   onto it, the headline hangs 0.032em so the roman T's serif reaches it and
   the italic 'a' below passes it, and the body's flat P stem is left where it
   falls. Five glyph types previously started at exactly one x, which is the
   look of a spec, not of typesetting.

   Peach belongs to another surface on this page, so the focal tone is ink on
   paper — but the record's fill is warmed off pure white, because a neutral
   plate on a warm ground separates by luminance alone and leaves the shadow
   doing all the work.

   Every rule is scoped under .sec-product-guest.
   =================================================================== */

.sec-product-guest {
  /* Inner padding of the record card. */
  --g-pad: var(--s28);
  /* How far the record rises above the header band. */
  --g-lift: var(--s40);
  /* How far past the screen edge the record runs. Larger than --r-elevated so
     the left radius is genuinely cut off rather than kissed. */
  --g-crop: var(--s24);
  /* Page rail → screen edge, plus that overshoot. The house `max(gutter, …)`
     idiom, so it stays correct once the window is narrower than the measure. */
  --g-bleed: calc(max(var(--gutter), (100vw - var(--page)) / 2) + var(--g-crop));
  /* The strip is stamped, not soft: half the card's own radius. */
  --g-r-strip: 8px;
  /* A record is white paper on warm paper — warm, not neutral, so it
     separates by hue as well as by value and the shadow can be lighter. */
  --g-plate: color-mix(in srgb, var(--raised) 90%, var(--wash-2));
}

/* The lift is the CARD's, not the section's. This band used to grow its own top
   padding by exactly --g-lift to pay for it, which made its rhythm 136 against a
   96 token; the card now rises into the shared rhythm instead. */

/* ——— The two rails ————————————————————————————————
   Placement, not source order: the DOM runs eyebrow → title → card → notes
   so the stacked layout below 900px already reads correctly with the
   artifact ahead of the notes. Here the card is lifted into rows 1–3 of the
   left column while the right column keeps its own vertical rhythm.
   The left track is narrow (0.68) because the card reclaims --g-bleed back
   out of the page on top of it; the two together land it near 500px. */
.sec-product-guest .g-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.68fr) minmax(0, 1.32fr);
  column-gap: clamp(var(--s48), 5.4vw, var(--s80));
  align-items: start;
}

.sec-product-guest .g-eyebrow {
  grid-column: 2;
  grid-row: 1;
  /* A capital W has almost no side bearing, so metrically flush it reads a
     pixel inboard of the hairline that opens the first note below. */
  margin-left: -1px;
}

.sec-product-guest .g-title {
  grid-column: 2;
  grid-row: 2;
  /* No size of its own. The 40–66px clamp that used to sit here rendered at
     66px, i.e. two pixels ABOVE product.html's own H1 — a section headline
     outranking the page title. .h governs; the lifetime figure below it is
     31.7px, so the two still separate. */
  line-height: 1.08;
  letter-spacing: -0.018em;
  /* Two lines. Wide enough that line one clears the second note column and
     establishes the right rail's edge, tight enough that the italic always
     falls to a line of its own. */
  max-width: 20ch;
  /* Hung, not flush — by the shared serif correction in 10-base.css, which this
     file used to duplicate at -0.032em. The roman comes out to the rule and the
     italic keeps the extra overhang an italic is supposed to have. */
}

.sec-product-guest .g-facts { grid-column: 2; grid-row: 3; margin-top: var(--s64); }

.sec-product-guest .g-card {
  grid-column: 1;
  grid-row: 1 / span 3;
  align-self: start;
  /* Rises into the section's own top padding, so the card's top edge crosses
     the header's band instead of sitting level with it. */
  margin-top: calc(var(--g-lift) * -1);
  /* …and runs off the left of the screen. Not a nudge into the gutter — the
     previous build stopped 56px short of the viewport edge with no left-rail
     datum to violate, so the off-register move cost a breakpoint and read as
     nothing. html carries `overflow-x: clip`, so this crops without scroll. */
  margin-left: calc(var(--g-bleed) * -1);
  /* Drifts in from the left so it arrives across the rail, not up it. */
  --float-x: -18px;
}

/* ——— The record ————————————————————————————————— */
.sec-product-guest .g-card {
  padding: 0;
  overflow: hidden;
  background: var(--g-plate);
  /* Lighter than the stock three-layer artifact shadow: the plate now
     separates on hue and value of its own, so the shadow only has to seat it. */
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--ink) 9%, transparent),
    0 20px 34px -20px color-mix(in srgb, var(--ink) 20%, transparent),
    0 4px 10px -8px color-mix(in srgb, var(--ink) 12%, transparent);
}

/* The crop is added back as inner padding, so the record's content keeps a
   full --g-pad from the SCREEN edge however far the plate itself runs off it.
   Only the rules and the plate get cut; nothing readable does. */
.sec-product-guest .g-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s16);
  padding: var(--s20) var(--g-pad) var(--s20) calc(var(--g-pad) + var(--g-crop));
  border-bottom: 1px solid var(--hair);
}

.sec-product-guest .g-who {
  font-size: var(--t-body);
  font-weight: 500;
  letter-spacing: -0.01em;
}

.sec-product-guest .g-tag {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: none;
  padding: 4px 12px 4px 10px;
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-pill);
  font-size: 13px;
  line-height: 1.4;
  color: var(--slate);
}
.sec-product-guest .g-tag__dot {
  width: 6px; height: 6px; flex: none;
  border-radius: var(--r-pill);
  background: var(--ink);
}

.sec-product-guest .g-card__body {
  padding: var(--g-pad) var(--g-pad) var(--g-pad) calc(var(--g-pad) + var(--g-crop));
}

/* Micro-labels. Deliberately below caption size — they name a field, they are
   not read as prose. */
.sec-product-guest .g-lbl {
  font-size: 13px;
  line-height: 1.4;
  color: var(--ash);
}
.sec-product-guest .g-lbl--sp { margin-top: var(--s28); }

.sec-product-guest .g-chips {
  list-style: none;
  margin: var(--s12) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s8);
}
.sec-product-guest .g-chip {
  padding: 7px 14px;
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-pill);
  font-size: var(--t-caption);
  line-height: 1.25;
}

.sec-product-guest .g-allergy {
  margin-top: var(--s24);
  display: flex;
  align-items: flex-start;
  gap: var(--s12);
  padding: 14px var(--s16);
  border-radius: var(--g-r-strip);
  background: var(--ink);
  color: var(--paper);
  font-size: var(--t-caption);
  line-height: 1.4;
}
.sec-product-guest .g-allergy svg { flex: none; margin-top: 1px; }

/* ——— Four visits ————————————————————————————————
   Three columns: date, measured bar, money. The money column is tabular and
   right-set so the four figures align on their digits; the date column is
   tabular too, so "12 Aug" and "11 Jul" do not shuffle. The track is inset
   from both neighbours so the bars read as marks, not as leader rules. */
.sec-product-guest .g-visits { list-style: none; margin: var(--s12) 0 0; padding: 0; }

.sec-product-guest .g-visit {
  display: grid;
  grid-template-columns: 54px minmax(24px, 1fr) auto;
  align-items: center;
  gap: var(--s20);
  padding-block: 13px;
  border-top: 1px solid var(--hair);
}

.sec-product-guest .g-visit__d { font-size: 14px; color: var(--slate); }

.sec-product-guest .g-visit__track {
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--hair);
}
.sec-product-guest .g-visit__bar {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--ink);
}

.sec-product-guest .g-visit__m { font-size: var(--t-caption); font-weight: 500; }

.sec-product-guest .g-ltv {
  margin-top: var(--s28);
  padding-top: var(--s24);
  border-top: 1px solid var(--hair-strong);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s16);
}
/* The card's one display-size figure. It is the largest thing INSIDE the
   record — 32px against the 14px visit rows — and never within a step of the
   headline OUTSIDE it: 66/32 at 1440, 59/29 at 1280, 47/23 at 1024, 40/23 at
   390. The floor moved down to 23 and the headline up to 40 precisely because
   at 28/28 on a phone they were the same size in the same face. */
.sec-product-guest .g-ltv__fig {
  font-size: clamp(23px, 2.2vw, 32px);
  line-height: 1;
  letter-spacing: -0.012em;
}

/* ——— The four notes, ranked ——————————————————————
   The previous build set these as a 2x2 of four blocks at one width with one
   title treatment and a half-step stagger. That is an even grid with a wobble
   on it, and it made a claim the copy does not support: that "Guest profiles"
   and "Deposits and no-shows" are worth the same. They are not. One of the
   four carries a named customer and a measured result — NOOT, 63% — and it is
   the only sentence in the section a restaurateur can put a number against.

   So: one lead across the whole rail, three calm supports in a row beneath it.
   The lead is separated by FIVE devices at once, none of them a fill: the full
   span, a 2px ink rule where the others get a hairline, a title one step up
   (26 → 20), a body one step up (20 → 16) and the figure swollen inside the
   sentence. The three below then match each other EXACTLY — same width, same
   padding, same rule, same title — because same rank must look same. The
   ordinals are gone with the 2x2: 01–04 is the notation of an index of
   equals, which is precisely the reading being corrected.
   ————————————————————————————————————————————————— */
.sec-product-guest .g-facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--s32);
  row-gap: var(--s48);
}

.sec-product-guest .g-fact {
  border-top: 1px solid var(--hair-strong);
  padding-top: var(--s16);
}

.sec-product-guest .g-fact__t {
  font-family: var(--sans);
  font-size: var(--t-body-lg);
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -0.012em;
}

.sec-product-guest .g-fact .body { margin-top: var(--s12); }

/* Only one of the three supports opens on a round cap. A G has to overshoot
   the rule above it to look level with the flat-stemmed R and E. */
.sec-product-guest .g-fact--round .g-fact__t { margin-left: -0.075em; }

/* ——— The lead ————————————————————————————————— */
.sec-product-guest .g-fact--lead {
  grid-column: 1 / -1;
  /* Ink, and twice the weight. This is the only rule in the section that is
     not a hairline, and it lands directly under the headline — so it also
     gives the right rail the full-width edge the two-line headline never
     reaches on its own. */
  border-top: 2px solid var(--ink);
  padding-top: var(--s20);
}

.sec-product-guest .g-fact--lead .g-fact__t {
  font-size: var(--t-h-sm);
  line-height: 1.24;
  letter-spacing: -0.016em;
}

.sec-product-guest .g-fact--lead .body {
  font-size: var(--t-body-lg);
  line-height: 1.5;
  /* One line at full width on desktop, two once the rail narrows. Either way
     it sits directly under its own title: there is no gap for a hole to open
     in, because there is nothing below it to push away from. */
  max-width: 56ch;
}

/* The proof figure, set inside its own sentence rather than pulled out beside
   it — repeating "63%" as a stat block would say the number twice. Its
   line-height is deliberately below the paragraph's computed leading (0.9 x
   1.35em = 1.215em against 1.5em) so the swell cannot open the line box and
   push the second line down. */
.sec-product-guest .g-fig {
  font-size: 1.35em;
  line-height: 0.9;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--ink);
}

/* ——— Narrower desktop ——————————————————————————— */
@media (max-width: 1080px) {
  .sec-product-guest { --g-lift: var(--s24); --g-pad: var(--s24); }
  .sec-product-guest .g-grid { grid-template-columns: minmax(0, 0.74fr) minmax(0, 1.26fr); }
  .sec-product-guest .g-title { max-width: 17ch; }
  .sec-product-guest .g-facts { margin-top: var(--s48); column-gap: var(--s24); row-gap: var(--s40); }
}

/* ——— Stacked ————————————————————————————————————
   One column, the record first. The bleed is a two-rail move — with one rail
   there is no text grid left for the card to be out of register with, so it
   returns inside the measure rather than crowding the screen edge. The card
   is capped rather than stretched: a 700px-wide profile card is a different,
   worse object. */
@media (max-width: 900px) {
  .sec-product-guest { --g-crop: 0px; }
  .sec-product-guest .g-grid { grid-template-columns: minmax(0, 1fr); }
  .sec-product-guest .g-eyebrow,
  .sec-product-guest .g-title,
  .sec-product-guest .g-card,
  .sec-product-guest .g-facts { grid-column: 1; grid-row: auto; }
  .sec-product-guest .g-card {
    max-width: 460px;
    margin-top: var(--s48);
    margin-left: 0;
    --float-x: 0px;
  }
  .sec-product-guest .g-title { max-width: 18ch; }
  .sec-product-guest .g-facts { margin-top: var(--s64); column-gap: var(--s32); }
}

@media (max-width: 640px) {
  .sec-product-guest { --g-pad: var(--s20); }
  /* Let the headline use the full measure: capped, it broke "The guest /
     arrives" across two lines for no reason. */
  .sec-product-guest .g-title { max-width: none; }
  .sec-product-guest .g-card { max-width: none; margin-top: var(--s40); }
  /* One column. Three 60-character sentences will not sit side by side on a
     phone, and the lead keeps its rank on rule weight and type size alone. */
  .sec-product-guest .g-facts {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--s32);
    margin-top: var(--s48);
  }
  .sec-product-guest .g-fact--lead { padding-top: var(--s16); }
  .sec-product-guest .g-visit { grid-template-columns: 50px minmax(20px, 1fr) auto; gap: var(--s16); }
}

/* ===== section:product-record ===== */
/* ===================================================================
   product-record — "What the last column means"

   The intellectual centre of the Product page: one headline, one register,
   one drawn line, one large number. No illustration and no second surface,
   because the argument is an argument about a single record.

   THE REVIEW THIS BUILD ANSWERS. The previous version put the whole argument
   inside a white .artifact and then invested nothing in it: five rows of near
   identical typographic weight, no focal point, and the one datum the section
   exists to state — 70.5% margin — buried mid-sentence in row four at body
   size, weighing exactly what "The ticket routes to the kitchen." weighed.
   Measured, the plate's fill sat ONE grey level off the fog it stood on once
   the page grain had multiplied over both. Four decisions follow from that.

   1. NO PLATE. The fill and the shadow are gone. An object whose edge you
      cannot see is not an object, and lifting white off fog is a five-level
      move before grain and a one-level move after it. Hairlines alone define
      the register now — rules that run the full rail, a masthead, a spine —
      and nothing here can dissolve into a ground because nothing here is a
      ground. This is also why the section reads the same above and below the
      fold: there is no surface whose relationship to the page can change.

   2. ONE LARGE FIGURE, AND ONLY ONE. "70.5% margin," is set in the headline's
      serif at --rec-fig — roughly 2.6x the ledger's speaking voice and the
      only thing in the section that competes with the h2 — hung in the right
      half of its row, which is empty in the three rows above it. The sentence
      is SET, not typed: its opening clause stays on the left rail in the
      ledger's voice, its em dash points into the void, and its tail sits under
      the figure. Every word of the signed-off line survives, in order, once.

   3. ONE RIGHT EDGE. The register used to stop two columns short at 1115 while
      the lede ran to 1311 and the headline hung at 121 — three unrelated
      edges and 324px of unexplained margin. The register now runs the whole
      rail, so the section is two aligned columns and the asymmetry lives
      INSIDE the object: quiet left-set lines, then a figure dropped into the
      right half, then a full stop that breaks the frame.

   4. THE CLOSE IS A RESOLUTION, NOT A ROW. "Posted once." has no cell, no tag
      and no rule of its own. It sits below the register's closing rule and
      knocks a hole through it; the spine runs past the last row and terminates
      in the section's one solid mark, centred on that rule. Flat fog ground,
      so the knockout is invisible and only the break reads.

   TWO geometry notes worth keeping, both learned by measurement:
     - An absolutely positioned SVG must never be sized by `top` + `bottom`
       with `height: auto`. It is a replaced element — `height: auto` resolves
       to the INTRINSIC height of the viewBox and `bottom` is ignored, so a
       spine sized that way shipped as a 100px stub. It is sized here by an
       explicit `height: calc()` against its flow wrapper.
     - Row padding is a variable, not a constant, because the figure row is
       taller than its siblings; the node offset is computed from that same
       variable so the bead cannot drift off the stroke in the one row where
       the padding differs.

   Peach is rationed to one surface per page and this section does not own it.
   The whole section is ink, fog and hairline.

   Every rule is scoped under .sec-product-record.
   =================================================================== */

.sec-product-record {
  /* The page's centrepiece gets the loose rhythm — a stage, not a band. */
  padding-block: var(--section-y-loose);

  /* Geometry of the register. Every mark on it comes from these.
     Three x positions and nothing else:
       0                       the rules, the masthead — and the page rail,
                               so eyebrow, headline and register share one edge
       --rec-pad-x             the spine and its nodes
       + --rec-rail            the sentences and the closing line */
  --rec-pad-x: var(--s24);
  --rec-rail: var(--s40);
  /* The consequence rows are deliberately TIGHTER than the figure row — 95px
     against 227px. A register whose rows are all one height is a table; the
     contrast is what makes the payoff read as a stage the ledger opens onto. */
  --rec-pad-y: var(--s28);     /* block padding on a consequence row     */
  --rec-pad-fig: var(--s48);   /* block padding on the figure row        */
  --rec-node: 9px;

  --rec-what: 28px;            /* the ledger's speaking voice            */
  --rec-ident: 26px;           /* the write — a rank above its consequences */
  --rec-fig: 64px;             /* the section's one large figure         */
  --rec-fig-u: 21px;           /* its unit, on the figure's baseline     */

  /* WHERE THE FIRST NODE SITS, and therefore where the stroke starts: half the
     first line's height below the first row's padding edge, so a sentence that
     wraps still hangs from its own first line. Only the spine uses this one —
     the per-row offset is declared on .rec__row, and it has to be, because a
     custom property is substituted against the element it is DECLARED on. A
     --rec-node-top written here would bake in --rec-pad-y and the figure row's
     deeper padding would never reach it; its bead shipped 60px above its own
     sentence exactly once, for exactly that reason. */
  --rec-row-pad: var(--rec-pad-y);
  --rec-node-a: calc(var(--rec-pad-y) + var(--rec-what) * 0.7);

  /* The one drawn stroke. --hair-strong at 12% ink measures invisible at this
     length, and a flow line that cannot be seen is not a flow line. */
  --rec-line: color-mix(in srgb, var(--ink) 30%, transparent);
}

/* ——— The frame ————————————————————————————————————— */
.sec-product-record .rec {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gutter);
  align-items: start;
}

.sec-product-record .rec__eyebrow { grid-column: 1 / span 8; grid-row: 1; }
.sec-product-record .rec__title   { grid-column: 1 / span 7; grid-row: 2; }

/* Bottom-aligned to the headline, one empty column clear of it. The measure is
   deliberately short — four columns — so it reads as a margin note against a
   display line, not as a second headline. The optical padding drops its last
   baseline onto the headline's. */
.sec-product-record .rec__sub {
  grid-column: 9 / -1;
  grid-row: 2;
  align-self: end;
  padding-bottom: 0.38em;
}

/* ——— The register ——————————————————————————————————
   Full rail. Its top edge is a rule, so the document has a top without needing
   a fill to have one. */
.sec-product-record .rec__doc {
  grid-column: 1 / -1;
  grid-row: 3;
  margin-top: var(--s96);
  border-top: 1px solid var(--hair-strong);
}

/* ——— The masthead ————————————————————————————————
   The write itself, flush on the page rail with the headline above it. One size
   throughout: the words in ash, the figures in tabular ink, so the string reads
   as written and the numerals still carry the weight. Its right side stays
   empty — a write is not a system, so it reports into no tag column. */
.sec-product-record .rec__ident {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 0.44em;
  padding-block: var(--s32) var(--s28);
  font-size: var(--rec-ident);
  line-height: 1;
  letter-spacing: -0.012em;
}
.sec-product-record .rec__ident-w { font-weight: 400; color: var(--slate); }
.sec-product-record .rec__ident-n { font-weight: 450; color: var(--ink); }
/* --hair-strong is 12% ink: as a separator between two 26px words it measured
   invisible, so the string read as four words with an accidental gap. --smoke
   is the large-type grey, and this is large type. */
.sec-product-record .rec__ident-x { color: var(--smoke); margin-inline: 0.18em; }

/* ——— The flow ——————————————————————————————————— */
.sec-product-record .rec__flow { position: relative; }

/* The stroke's top is the first node; its bottom is the register's closing
   rule, where the solid terminal sits. Percentages on an absolutely positioned
   child resolve against the flow's padding box, which ends exactly at that
   rule — so the two cannot drift.

   viewBox width 2 in a 2px box means the x scale is exactly 1, so
   `stroke-width: 1` renders as one true pixel however tall the register grows;
   only y stretches, and a vertical line has no horizontal extent to distort.
   getTotalLength() reports 100 user units, which is what the shared [data-draw]
   observer animates. Under the rows, so every node covers it. */
.sec-product-record .rec__line {
  position: absolute;
  z-index: 0;
  left: calc(var(--rec-pad-x) - 1px);
  top: calc(1px + var(--rec-node-a));
  width: 2px;
  height: calc(100% - 1px - var(--rec-node-a));
  color: var(--rec-line);
  overflow: visible;
}

/* ——— Rows ————————————————————————————————————————
   Full-rail rules: they run edge to edge, which is what a register does and
   what a table of inset rules never does. The row carries the type size so the
   node can be placed against it and follow it at every breakpoint. */
.sec-product-record .rec__rows {
  position: relative;
  z-index: 1;
  border-bottom: 1px solid var(--hair-strong);   /* the closing rule */
}

.sec-product-record .rec__row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--s64);
  align-items: baseline;
  padding: var(--rec-row-pad) 0 var(--rec-row-pad)
           calc(var(--rec-pad-x) + var(--rec-rail));
  border-top: 1px solid var(--hair);
  font-size: var(--rec-what);
  /* Declared HERE so it substitutes against this row's own --rec-row-pad; the
     figure row raises that and its bead follows without a second number. */
  --rec-node-top: calc(var(--rec-row-pad) + var(--rec-what) * 0.7);
}
/* The seam that means something — under the write — takes the stronger rule.
   The register therefore reads as head + list + foot, three weights, rather
   than as six identical bands. */
.sec-product-record .rec__row:first-child { border-top-color: var(--hair-strong); }

/* The node. A ring threaded on the stroke, centred on the row's FIRST line. */
.sec-product-record .rec__row::before {
  content: '';
  position: absolute;
  left: calc(var(--rec-pad-x) - var(--rec-node) / 2);
  top: calc(var(--rec-node-top) - var(--rec-node) / 2);
  width: var(--rec-node);
  height: var(--rec-node);
  border: 1px solid var(--ink);
  border-radius: var(--r-pill);
  background: var(--fog);
}

.sec-product-record .rec__what {
  grid-column: 1;
  grid-row: 1;
  font-size: 1em;
  line-height: 1.4;
  letter-spacing: -0.012em;
  color: var(--ink);
  text-wrap: pretty;
}

/* Four small ash words, right-set on the rail: the tag column the brief asked
   for, and the only thing in it. */
.sec-product-record .rec__tag {
  grid-column: 2;
  grid-row: 1;
  font-size: 13px;
  font-weight: 450;
  line-height: 1;
  letter-spacing: 0.09em;
  color: var(--ash);
  text-align: right;
  white-space: nowrap;
}

/* ——— The figure row ————————————————————————————————
   The stage. Taller than its siblings, and the only row that uses its second
   grid row: the sentence's opening clause on the left rail, the figure dropped
   into the right half beneath the tag that has been marking that column for
   three rows. That vertical offset is deliberate — figure and clause on one
   baseline would read as a caption; offset, the row reads as a diagonal. */
.sec-product-record .rec__row--fig {
  --rec-row-pad: var(--rec-pad-fig);
  row-gap: var(--s12);
}
.sec-product-record .rec__fig {
  grid-column: 2;
  grid-row: 2;
  justify-self: end;
  text-align: right;
}

.sec-product-record .rec__figv {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  /* Sized in the UNIT's em, not the row's, so the space between the figure and
     the word it qualifies holds its proportion at every breakpoint. Stated
     against the row it measured 4px at 28px type and the two words fused. */
  font-size: var(--rec-fig-u);
  gap: 0.62em;
  line-height: 1;
}
/* Tabular AND lining: the percentage has to set on the same widths as the two
   money amounts in the clause beside it, and Source Serif's default figures
   must not be allowed to drop below the baseline at this size. */
.sec-product-record .rec__figv-n {
  font-family: var(--serif);
  font-weight: 400;
  font-optical-sizing: auto;
  font-size: var(--rec-fig);
  line-height: 1;
  letter-spacing: -0.022em;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--ink);
}
.sec-product-record .rec__figv-u {
  font-size: 1em;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--slate);
}
.sec-product-record .rec__figt {
  margin-top: var(--s12);
  font-size: var(--t-body);
  line-height: 1.45;
  letter-spacing: -0.005em;
  color: var(--slate);
}

/* ——— The closing statement ——————————————————————————
   Outside the register, straddling its closing rule. The line is pulled up by
   most of its own leading so the rule crosses it at cap height, and it paints
   itself in the section ground with a little air either side — on a flat fog
   band the knockout is invisible and only the BREAK in the rule reads.

   The terminal is the one solid mark in the section: a filled disc on the
   spine's x, centred on that same rule, so the stroke visibly lands rather than
   stopping. It shipped once as a fifth identical ring inside a table cell,
   which is why the close read as a leftover headline dropped into a row. */
.sec-product-record .rec__close {
  position: relative;
  padding-left: calc(var(--rec-pad-x) + var(--rec-rail));
}
.sec-product-record .rec__close::before {
  content: '';
  position: absolute;
  left: calc(var(--rec-pad-x) - var(--rec-node) / 2);
  top: calc(var(--rec-node) / -2);
  width: var(--rec-node);
  height: var(--rec-node);
  background: var(--ink);
  border-radius: var(--r-pill);
}
.sec-product-record .rec__end {
  display: inline-block;
  margin-top: calc(var(--t-h) * -0.5);
  /* -0.026em is the shared serif sidebearing correction from 10-base.css; the
     rest pulls the knockout's left padding back off the rail so the glyph stays
     on it. Written as one value because two margin-lefts cannot both win. */
  margin-left: calc(-0.026em - var(--s20));
  padding-inline: var(--s20) var(--s28);
  background: var(--fog);
  color: var(--ink);
}

/* ——— Motion ————————————————————————————————————————
   The rows keep a short 8px rise — a bead on a continuous line is still on it,
   and the stroke itself must not move while they stagger. The close reveals
   after the four rows have finished (4 x 110ms), because it is their
   conclusion; it sits outside the reveal group, so its delay is stated here. */
.sec-product-record .rec__row[data-reveal] { transform: translateY(8px); }
.sec-product-record .rec__row[data-reveal].is-in { transform: none; }
.sec-product-record .rec__close[data-reveal] { --reveal-delay: 520ms; transform: none; }

/* ——— Narrow desktop ————————————————————————————————
   --t-h-lg drops to 48px at 1080, so the head's spread tightens by a column and
   the whole register steps down with it. The figure keeps its ratio to the
   ledger voice rather than being re-guessed. */
@media (max-width: 1180px) {
  .sec-product-record {
    --rec-pad-x: var(--s20);
    --rec-rail: var(--s32);
    --rec-what: 24px;
    --rec-ident: 23px;
    --rec-fig: 52px;
    --rec-fig-u: 18px;
  }
  .sec-product-record .rec__title { grid-column: 1 / span 8; }
  .sec-product-record .rec__row { column-gap: var(--s40); }
  /* --smoke is the LARGE-type grey and is not licensed below 24px; the masthead
     steps under that here, so its separator moves to the small-text grey. */
  .sec-product-record .rec__ident-x { color: var(--ash); }
}

@media (max-width: 940px) {
  .sec-product-record .rec { display: block; }
  /* Hung to the RIGHT rather than indented: a left indent with this measure
     lands the lede almost exactly centred under the headline, which reads as an
     accident. Right-set, it keeps the desktop spread's asymmetry after the grid
     stops being a row. */
  .sec-product-record .rec__sub {
    margin-top: var(--s32);
    margin-left: auto;
    width: min(520px, 64%);
    padding-bottom: 0;
  }
  .sec-product-record .rec__doc { margin-top: var(--s80); }
}

/* ——— Phone ————————————————————————————————————————
   Genuinely re-laid out, not squashed.

   THE TAG MOVES. At ~330px of measure there is no right-hand column, and the
   shipped build let each tag orphan onto its own right-aligned line under its
   sentence — four grey words floating in empty space, one-line rows becoming
   two and the figure row becoming four. The tag now leads its row as a small
   eyebrow on the row's own left edge, so the tag column becomes a vertical
   rhythm down the left rather than four unanchored scraps down the right, and
   every row is back to one block of text with a label.

   The node arithmetic follows it: the row's first line is now the tag, so the
   bead is placed against the TAG's height on phones instead of the sentence's.

   The stroke, the five marks, the full-rail rules, the figure and the closing
   break all survive: they are the section's whole argument. */
@media (max-width: 720px) {
  .sec-product-record {
    --rec-pad-x: 5px;
    --rec-rail: var(--s20);
    --rec-pad-y: var(--s24);
    --rec-pad-fig: var(--s48);
    --rec-node: 8px;
    --rec-what: 18px;
    --rec-ident: 20px;
    --rec-fig: 40px;
    --rec-fig-u: 15px;
    --rec-node-a: calc(var(--rec-pad-y) + 6px);
  }
  /* The row's first line on phones is the TAG, not the sentence, so the bead is
     placed against the tag's 12px box instead of the ledger voice's. */
  .sec-product-record .rec__row { --rec-node-top: calc(var(--rec-row-pad) + 6px); }

  .sec-product-record .rec__sub { margin-left: 0; width: auto; margin-top: var(--s24); }
  .sec-product-record .rec__doc { margin-top: var(--s64); }
  .sec-product-record .rec__ident { padding-block: var(--s24) var(--s20); }

  .sec-product-record .rec__row {
    grid-template-columns: minmax(0, 1fr);
    column-gap: 0;
    row-gap: 0;
  }
  .sec-product-record .rec__tag,
  .sec-product-record .rec__what,
  .sec-product-record .rec__fig {
    grid-column: 1;
    grid-row: auto;
  }
  .sec-product-record .rec__tag {
    font-size: 12px;
    text-align: left;
    margin-bottom: var(--s12);
  }
  .sec-product-record .rec__row--fig { row-gap: 0; }
  .sec-product-record .rec__fig {
    justify-self: start;
    text-align: left;
    margin-top: var(--s20);
  }
  .sec-product-record .rec__figv { justify-content: flex-start; }
  .sec-product-record .rec__figt { font-size: 15px; }

  .sec-product-record .rec__end {
    margin-top: calc(var(--t-h) * -0.48);
    padding-inline: var(--s12) var(--s16);
    margin-left: calc(-0.026em - var(--s12));
  }
}

/* ===== section:product-trio ===== */
/* ===================================================================
   product-trio — three closing assurances

   The exhale before the CTA band, so it holds exactly four things: three
   short paragraphs and one hairline. Everything else was removed.

   WHAT CHANGED, AND WHY

   1. THERE IS A FOCAL POINT NOW, AND IT IS MADE OF SCALE.
      Three titles at one size, one weight, one colour gave the section no
      entry point: the first unit only appeared to dominate because its
      sentence was the longest, which is accidental mass, not designed mass.
      The first assurance — the one a shop owner is actually afraid of — is
      set at true display size (up to 72px serif, tracking -0.02em, the
      section's only italic riding the last word). The other two drop to
      .h-sm-serif. Display-to-body is now 72/16, a display ratio; it was
      32/16, which is a subhead ratio and no typographic event at all.

   2. ONE TYPE-TO-RULE RULE, OBEYED THREE TIMES.
      Previously the first title sat on the page rail while its rule came in
      from the screen edge, and the other two sat flush on their risers —
      two different relationships in one device. Now EVERY tread's straight
      run begins exactly where its own title begins. That is why the stroke
      no longer arrives from off the left edge: a run with no beginning
      cannot be flush with anything.

   3. THE EMPHATIC GESTURE MOVED OFF THE LIGHTEST CONTENT.
      "For groups" is two words and used to carry the boldest move in the
      section — a tread bleeding off the right edge. The treads now
      DIMINISH (534 / 307 / 295px at the page measure) so the longest run
      sits under the biggest type, and the third unit's job is only to hand
      the stroke to the last turn.

   4. THE STROKE STILL LEAVES THE FRAME, DOWNWARD.
      After the third tread it turns one last time and drops through the
      bottom of the section, where the fog CTA band cuts it. A closing
      section whose line points into the next one is saying what the copy
      says. It is also the only boundary this section crosses, which is the
      right number for an exhale.

   THE OBJECT

     ┌───────────────────────────────┐              tread a  (flush with title a)
     │  It works when                └──┐
     │  the internet                    └──────────┐   tread b
     │  doesn't                                    └────────────┐  tread c
     │                                                          │  tail, cut by
     │                                                          ┆  the section edge

   Every turn is an arc at --r-small — the system's own small radius — so
   the stroke turns instead of cornering. Three 90-degree hairline elbows
   read as the outline of open rectangles, i.e. as table borders.

   Peach belongs to another surface on this page and the CTA band that
   follows is the fog one. This section is paper, and stays paper.

   Every rule is scoped under .sec-product-trio.
   =================================================================== */

.sec-product-trio {
  --tri-gap: var(--s64);
  /* The drop per step. It has to stay well clear of twice the turn radius or
     the joint becomes all arc and no riser, and the stair starts reading as
     the corner of an enormous rounded card rather than as a line turning.
     At 96 the straight part of each riser is 64px against 32px of arc. */
  --tri-step: var(--s96);
  --tri-line: var(--hair-strong);
  /* The turn radius. --r-small, not a bespoke number: the one place this
     section has a curve, it uses the system's curve. */
  --tri-r: var(--r-small);
  /* The stacked layout's step. Zero until the mobile breakpoint sets it. */
  --tri-in: 0px;

  /* More air above than a normal section — this is the page breathing out,
     and it also carries the first tread well clear of the fog seam above so
     it can never be misread as that band's bottom border. Below, the CTA
     brings its own generous top padding, so the close stays tighter than the
     open. The tail's geometry is measured off these two values, so if either
     changes the tail follows. */
  /* A stage: three columns of drawing hanging off one tread. The loose rhythm,
     symmetrically — this used to be section-y plus 80 above and plus 48 below. */
  padding-block: var(--section-y-loose);
}

/* ——— The three columns ————————————————————————————
   Unequal on purpose: an even 3-up is the failure mode for a section like
   this. Column one is wide because it carries display type; columns two and
   three are near-equal and small. min-width: 0 so a long title can never
   push a column past its share. */
.sec-product-trio .tri-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 0.82fr) minmax(0, 0.88fr);
  column-gap: var(--tri-gap);
  align-items: start;
}

/* Each column is the positioning frame for its own piece of the stroke, so
   every offset below is measured from a real grid edge, never from a guess. */
.sec-product-trio .tri { position: relative; }

/* The stagger. This is what turns three rules into one descending line. */
.sec-product-trio .tri--b { margin-top: var(--tri-step); }
.sec-product-trio .tri--c { margin-top: calc(var(--tri-step) * 2); }

/* ——— The treads ————————————————————————————————————
   THE ONE RULE: a tread's straight run starts on its own column's left rail,
   which is exactly where that column's title starts. (-1px, because that is
   where the arc handing the stroke over finishes; at hairline weight the two
   have to share a pixel or the joint shows a step.)

   Treads a and b run one radius PAST the far side of the gutter, to the
   x where the next column's arc picks the stroke up. Tread c stops one
   radius short of the page rail, where the tail's arc takes it down. */
.sec-product-trio .tri__rule {
  position: absolute;
  top: 0;
  left: -1px;
  height: 1px;
  background: var(--tri-line);
}
.sec-product-trio .tri--a .tri__rule,
.sec-product-trio .tri--b .tri__rule { right: calc((var(--tri-gap) - var(--tri-r) * 2) * -1); }
.sec-product-trio .tri--c .tri__rule { right: var(--tri-r); }

/* ——— The joints ————————————————————————————————————
   Box width equals the radius, so `border-top` is entirely the arc turning
   down out of the previous tread and `border-right` is the riser. The box
   sits two radii left of the column rail, which puts the riser one radius
   out — leaving room for the lower arc to land the stroke ON the rail
   rather than a radius inside it. Height is one step MINUS one radius, so
   the riser stops exactly where the lower arc begins. */
.sec-product-trio .tri__joint {
  position: absolute;
  left: calc(var(--tri-r) * -2);
  top: calc(var(--tri-step) * -1);
  width: var(--tri-r);
  height: calc(var(--tri-step) - var(--tri-r));
  border-top: 1px solid var(--tri-line);
  border-right: 1px solid var(--tri-line);
  border-top-right-radius: var(--tri-r);
}

/* The arc out. Offset by the radius (less the 1px the riser's border already
   occupies) so its left edge sits ON the riser, and one pixel taller than the
   radius so its bottom border lands on this column's own tread rather than a
   pixel above it. */
.sec-product-trio .tri__joint::after {
  content: '';
  position: absolute;
  left: calc(var(--tri-r) - 1px);
  top: 100%;
  width: var(--tri-r);
  height: calc(var(--tri-r) + 1px);
  border-left: 1px solid var(--tri-line);
  border-bottom: 1px solid var(--tri-line);
  border-bottom-left-radius: var(--tri-r);
}

/* ——— The last turn, and the tail ——————————————————
   One box does both: `border-top` is the arc off the end of tread c and
   `border-right` is the drop. `bottom: 0` lands it on the section's own
   border-box edge — so it is CUT by the boundary rather than stopping in
   mid-air, and it cannot overflow the document.

   It lives outside the grid and is measured off the section, so its right
   edge is the page rail (`--page` when there is room, the gutter when there
   is not) at every width — the same rail column three ends on. */
.sec-product-trio .tri__tail {
  position: absolute;
  top: calc(var(--section-y-loose) + var(--tri-step) * 2);
  bottom: 0;
  right: calc((100% - min(var(--page), 100% - var(--gutter) * 2)) / 2);
  width: var(--tri-r);
  border-top: 1px solid var(--tri-line);
  border-right: 1px solid var(--tri-line);
  border-top-right-radius: var(--tri-r);
}

/* ——— Drawing the stroke ————————————————————————————
   `data-bar` is the shared primitive: it declares only `transition: transform`
   plus a scaleX(0) → scaleX(1) on `.is-in`. The treads take it as written and
   sweep left to right. The joints and the tail need the same timing on the
   other axis, so the transform (not the transition) is restated here at
   higher specificity — the motion is still entirely the primitive's,
   including its collapse to nothing under prefers-reduced-motion, which
   lands `!important`. */
.sec-product-trio .tri__joint,
.sec-product-trio .tri__tail { transform: scaleY(0); transform-origin: top; }
.sec-product-trio .tri__joint.is-in,
.sec-product-trio .tri__tail.is-in { transform: scaleY(1); }

/* One stroke, drawn in six beats: run, turn, run, turn, run, fall. The type
   follows on the group's own stagger, a beat behind its rule. */
.sec-product-trio .tri--a .tri__rule  { transition-delay: 0ms; }
.sec-product-trio .tri--b .tri__joint { transition-delay: 240ms; }
.sec-product-trio .tri--b .tri__rule  { transition-delay: 400ms; }
.sec-product-trio .tri--c .tri__joint { transition-delay: 640ms; }
.sec-product-trio .tri--c .tri__rule  { transition-delay: 800ms; }
.sec-product-trio .tri__tail          { transition-delay: 1040ms; }

/* ——— Type ——————————————————————————————————————— */
.sec-product-trio .tri__txt { padding-top: var(--s24); }
/* Display type carries far more half-leading above its cap than 26px does —
   at 72px the line box eats 6px above the ascender — so the lead needs LESS
   padding to hang the same distance below its tread. Measured, both land
   their cap-height ~32px under the hairline. */
.sec-product-trio .tri--a .tri__txt { padding-top: var(--s20); }
/* Column three has to keep clear of the tail dropping down the page rail. */
.sec-product-trio .tri--c .tri__txt { padding-right: var(--s32); }

/* Optical alignment — the shared -0.026em on every serif headline in
   10-base.css, which scales with the type, so both the lead title and the 26px
   ones land their glyph on the tread. This file used to hold a -0.03em copy. */

/* THE EVENT is the RANK, not a size of its own. The lead title is the section's
   headline, so it sits on .h like every other section headline on the site; the
   other two sit a step below it on .h-sm-serif. It used to restate a 44–72px
   clamp on top of .h-lg, which made a column head on an inner page larger than
   that page's own H1. Nothing is restated now — the class governs. */

.sec-product-trio .tri__b {
  margin-top: var(--s16);
  /* ONE measure for all three bodies, narrower than any column, so the type
     reads as one system while the titles carry the whole hierarchy. The air
     it leaves inside column one is intended: that column is wide because its
     TITLE is big, not because its prose is long. */
  max-width: 34ch;
  /* `balance`, not the `.body` default of `pretty`. These paragraphs set to
     two or three lines, and `pretty` only tidies the LAST line — it left
     every one of them breaking on a weak word: "and it / keeps trading",
     "including the day / you leave", "You see / all of them". Balancing
     lands each break where the sentence already pauses. */
  text-wrap: balance;
}
.sec-product-trio .tri--a .tri__b { margin-top: var(--s24); }

/* ——— Narrow ——————————————————————————————————————
   The columns tighten before the type does; the lead is on vw and follows
   by itself. */
@media (max-width: 1080px) {
  .sec-product-trio { --tri-gap: var(--s48); --tri-step: var(--s80); }
}

/* ——— Stacked ————————————————————————————————————
   NOT the desktop unstacked. The stair cannot survive as a stair — the
   risers would become a full-height bracket down the left edge, a different
   and much louder drawing — so the descent is carried over as an INDENT:
   each unit steps 24px further in, and its tread starts at that indent.
   All three end on the same right edge, so the steps read as a designed
   progression rather than as three rules that failed to line up.

   (The previous build leaked desktop bleeds in here and rendered three
   rules at three origins AND three lengths — 0→739, 40→739, 40→779 at
   812px. Both bleeds are gone from the section entirely, so there is
   nothing left to leak.) */
@media (max-width: 860px) {
  .sec-product-trio .tri-grid {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--s64);
  }
  .sec-product-trio .tri--b,
  .sec-product-trio .tri--c { margin-top: 0; }

  /* The stair's joints and its tail are desktop geometry. They go. */
  .sec-product-trio .tri__joint,
  .sec-product-trio .tri__tail { display: none; }

  .sec-product-trio .tri--b { --tri-in: 24px; }
  .sec-product-trio .tri--c { --tri-in: 48px; }

  /* `right` is restated for ALL THREE, not just the two that carry a desktop
     value: column three holds a 16px inset for the tail's arc, and left
     unreset it rendered the third rule 16px short of the other two — the
     exact class of leak this breakpoint exists to stop. Three origins by
     design (0 / 24 / 48), ONE right edge. */
  .sec-product-trio .tri--a .tri__rule,
  .sec-product-trio .tri--b .tri__rule,
  .sec-product-trio .tri--c .tri__rule { left: var(--tri-in); right: 0; }
  .sec-product-trio .tri__txt,
  .sec-product-trio .tri--a .tri__txt { padding-top: var(--s20); padding-left: var(--tri-in); }
  .sec-product-trio .tri--c .tri__txt { padding-right: 0; }

  .sec-product-trio .tri__b { max-width: 36ch; }
}

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