/* ===== 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:pricing-hero ===== */
/* ===========================================================================
   pricing-hero — the Pricing page opening, and the boldest claim on the site.

   THE BRIEF: one headline, the two bounds, one line of terms, no buttons — the
   calculator immediately below is the interaction, so anything clickable here
   would compete with it.

   THE COMPOSITION IS TWO BLOCKS ACROSS THE PAGE RAIL, TIED BY TWO BASELINES —
   not a stack down its left edge:

       Pricing

       A quarter of a percent of
       what you sell. ················ Never below   HK$1,280.

                                       Never above   HK$2,880.
       Everything included, ·································

   The italic and the floor figure share one baseline; the terms and the ceiling
   figure share another. Nothing is drawn between them — the dotted rules above
   are the relationship, not the render — and that is the whole point: 300px of
   empty paper reads as HELD rather than as leftover, because both ends of it
   are pinned. The arithmetic that makes the two ties exact at every width is on
   the two-column block near the bottom of this file.

   The void is what is left over: an L that opens between the end of the
   headline and the start of the bounds. It has a shape because two things bound
   it.

   WHAT THIS REPLACES, and why. The previous setting ran everything down the
   left rail and stopped at x=780 of a 1440 frame. Two measured problems:
     1. NO SECOND CORNER. The right 45% of the section carried no ink and no
        edge, while its two siblings — product-hero's artifact, solutions-hero's
        flush-right italic — both use that half. Void is the aesthetic; void
        with nothing across it is a paragraph that ended.
     2. IT READ AS THE CALCULATOR'S CAPTION. pricing-calc opens 96px below with
        HK$1,280 and HK$2,880 in the same serif. Set one under the other on one
        rail, the hero and the instrument merged into a single left-aligned
        column and the hero lost its own frame.

   HOW IT DIFFERS FROM THE OTHER PAGE OPENINGS, so the set reads as one hand
   rather than one template:
     · product-hero   — a wedge: wide headline, short lede, one floating
                        artifact on the right, a rule bled in from the left.
     · solutions-hero — a step: line one on the rail, line two flush right, a
                        six-mark index rule running off the frame.
     · here           — a span: type only, two blocks at the two ends of the
                        rail, and the only two alignments in it are never drawn.
   No artifact, no card, no shadow, no rule, no illustration, no peach: six
   things this section could have had and is stronger without.

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

.sec-pricing-hero {
  /*
   * THE STAGE IS THE SHARED ONE. --hero-y above (the nav band is inside that
   * figure, so the visible air matches every other page) and the loose section
   * rhythm below, both from the hero rule in 10-base.css. This section used to
   * carry a private 252 / 208 pair, which made it the tallest opening on a site
   * whose other seven heroes run off the same 76px nav.
   */

  /* CLIP X ONLY. `.has-wash > .wash` is inset -5% on both flanks and counts
     toward document scrollWidth even under the root's overflow-x: clip; an
     unclipped hero measured a horizontal scrollbar into the capture. Y stays
     visible so nothing is guillotined at the seam with pricing-calc. */
  overflow-x: clip;
  overflow-y: visible;

  /*
   * THE TYPE LADDER, AND THE ONE STEP THIS SECTION SETS ITSELF.
   *
   * 64 headline / 38 figure / 20 terms / 15 label — 1.7x, 1.9x, 1.3x. The
   * headline takes --t-h-lg from the class it wears, the same 64px every other
   * page title takes; the figures are the only rank written here, because they
   * are the one element on the site that is neither a heading nor body copy.
   *
   * They are NOT a second headline. An earlier pass ran them at 54px against a
   * 64px title — 1.19x apart, which is not a ladder, it is two headlines — and
   * on the assembled page the hero's figures then sat within a few px of the
   * calculator's own answer 96px below. 38 puts them a clear step under the
   * title and a clear step over the terms.
   *
   * The divisor is written against the gutter variable, not as a raw vw, so the
   * ramp needs no new arithmetic when the global ramp drops --gutter at 1080.
   */
  --prh-fig: clamp(26px, calc((100vw - var(--gutter) * 2) / 34), 38px);
  --prh-lab: 15px;

  /* The gap the bounds hold from the headline column. Wide enough that the two
     blocks are two objects; not so wide that the headline has to wrap early. */
  --prh-gap: var(--s80);
}

/* — THE GROUND ————————————————————————————————————————
 *
 * The shared four-bloom wash is a page-level texture and knows nothing about
 * where this section puts its ink. Dropped straight onto the hero it lit the
 * frame evenly and resolved into two visible blobs; measured on an earlier
 * pass, mean warmth (r−b) came out 41.0 behind the sub copy against 29.8 behind
 * the italic — the wash peaked on the lowest-contrast text in the frame and
 * left the one crafted moment on the coolest ground in it.
 *
 * Two fields now, and both of them are CUT BY AN EDGE rather than fading to
 * nothing inside the frame. That is the difference between a plane of light the
 * page happens to show a window onto, and a cloud floating in the middle of a
 * composition.
 *
 *   1. THE CORE, IN THE ROOM RATHER THAN ON THE TYPE. `at 46% 52%` sits in the
 *      void the two baseline ties bracket — between the end of the headline and
 *      the start of the bounds — so neither the italic accent nor the figures is
 *      picked out by a glow. SAMPLED OFF THE 1440 RENDER, mean warmth (r−b) over
 *      ground pixels: 27.4 in the centre void, 27.0 behind the italic, 18.8
 *      behind the figures, 18.5 under the terms. The peak is the room the two
 *      blocks stand in, not either block.
 *
 *      IT IS WIDE ENOUGH TO LEAVE THE FRAME. 92% x 56% still carries 17.1 on the
 *      left edge strip and 10.7 on the right, against 3.0 in the faded bottom
 *      band — so the field is CUT by the viewport rather than resolving into an
 *      ellipse inside it.
 *
 *      FIVE STOPS, AND THE PEAK IS NOT THE TOKEN. --wash-1 used neat at 0% put
 *      an opaque tint at the core and rendered as a discrete orange oval: a glow
 *      on the type, which is the one thing that makes a warm ground look cheap.
 *      The other end of that mistake is a peak so wide and strong that the paper
 *      never comes back and the section reads as a coloured panel. 0.56 with
 *      three shoulder stops is the setting where the falloff has no isoline you
 *      can point at and the bottom band still returns to paper at 3.0.
 *   2. THE LEFT PLANE, ENTERING FROM OFF-FRAME. `at -8% 40%` puts its core
 *      outside the left edge of the wash box, i.e. genuinely off the page: what
 *      the section shows is the right-hand shoulder of a field whose centre the
 *      viewport never contains. It is tall so it runs the full height of the
 *      flank rather than sitting as an oval behind the nav, and it is the
 *      palest of the tints, so it warms the corner under the logo (18.9 in the
 *      top-left 80x60, against 3.0 in the bottom-right) without competing for
 *      focus. That asymmetry is deliberate: the page's own logo sits in that
 *      corner and the bottom-right corner is where the calculator's plate
 *      arrives, which wants paper under it.
 *
 * The two cool tints are dropped. They earn their place behind a hero with
 * product artifacts to sit against; on pure type they are two more candidates
 * for a focus that is already spoken for.
 *
 * Zero-alpha stops are spelled as rgba() of the same tint rather than
 * `transparent` — `transparent` is rgba(0,0,0,0), so interpolating toward it
 * drags a warm ramp through grey and leaves a dirty edge.
 */
.sec-pricing-hero > .wash {
  background:
    radial-gradient(92% 56% at 46% 52%,
      rgba(255, 217, 194, 0.56) 0%, rgba(255, 217, 194, 0.38) 30%,
      rgba(255, 217, 194, 0.15) 62%, rgba(255, 217, 194, 0.02) 88%,
      rgba(255, 217, 194, 0) 100%),
    radial-gradient(46% 60% at -8% 40%,
      rgba(255, 233, 214, 0.76) 0%, rgba(255, 233, 214, 0.22) 34%,
      rgba(255, 233, 214, 0) 86%);
}

/* The stock seam fade is 200px, which on this section erased the ground for the
   whole of the lower band — the exact band the calculator's housing sits in,
   and the one place its hairline has to fall on warm paper rather than on
   white. 140 keeps the plane alive to within a caption's height of the seam and
   still lands on paper at the boundary, so there is no hard edge. */
.sec-pricing-hero > .wash::after { height: 140px; }

/* ———————————————————————————————————————————————————————————————————
   NARROW FIRST. Below the two-column breakpoint the four marks simply run down
   the rail in DOM order — label, headline, bounds, terms — because at that
   width there is no second corner to hang anything on, and a two-column grid
   squeezed into 900px is the squashed-desktop failure rather than a design.
   ——————————————————————————————————————————————————————————————————— */

/* — The headline ————————————————————————————————————— */
.sec-pricing-hero .prh-title {
  /*
   * 1.02, TIGHTER THAN THE 1.1 THE RAMP HANDS OUT. Two lines of display serif
   * is the one case where the stock leading reads loose: it closes the pair
   * into a single mass instead of two floating lines. Descender clearance is
   * bought back below rather than with leading, so this is safe to tighten.
   */
  line-height: 1.02;

  /* `pretty`, not the `balance` the scale classes inherit: the breaks are
     authored, and balance re-negotiating the phone stack inside line one's mask
     is the one way those breaks could stop being authored. */
  text-wrap: pretty;
}

/*
 * DESCENDER CLEARANCE, WHICH IS ARITHMETIC AND WOULD OTHERWISE SHAVE THE TYPE.
 *
 * The reveal masks clip to the line box, and both lines carry descenders: two
 * q/p pairs in "A quarter of a percent of", the y in "what you sell". Source
 * Serif 4 puts the baseline 1.036em below the content-box top and its content
 * box is 1.371em tall; against a 1.02em line box the half-leading is
 * (1.02 - 1.371)/2 = -0.1755em, so the baseline lands 0.8605em down and there
 * are only 0.1595em of box left underneath it. The roman p/q descend 0.205em
 * and the italic y further still — every one of them overflows the clip.
 *
 * Fixed the way home-hero fixes it, and for the same reason: padding on the
 * INNER span extends the clip box, and an equal negative margin on the mask
 * gives the leading back, so the 1.02 line height is preserved exactly.
 *
 * The 105% translate in the hidden state is a percentage of the SPAN's own
 * height, which now includes this padding — so the line still clears its mask
 * completely before the reveal fires. (Padding on the MASK would have broken
 * that; it goes on the span.)
 */
.sec-pricing-hero .prh-title .line-mask {
  margin-block: -0.09em;
  margin-inline: -0.08em;
}
.sec-pricing-hero .prh-title .line-mask > span {
  padding-block: 0.09em;
  padding-inline: 0.08em;
}

.sec-pricing-hero .prh-title em {
  /* Source Serif's italic sets marginally tighter than its roman at the same
     nominal size; a hair of extra tracking keeps the accent from reading as
     compressed under a roman line nearly twice its length. It is the one italic
     in the section. */
  letter-spacing: -0.014em;
}

/* The narrow-column break is inert until the phone block turns it on. */
.sec-pricing-hero .prh-br-m { display: none; }

/* ———————————————————————————————————————————————————————————————————
   THE BOUNDS — a two-column specification, not a sentence.

   Four cells on two rows: label, figure, label, figure. `align-items: baseline`
   puts each 15px label on its own 38px figure's baseline, and the labels are
   pushed to the END of their column, so the block has a vertical axis running
   through it that is never drawn — the third undrawn alignment in the section,
   and the only one that survives down to 320px.

   The figures carry .tnum, which is doing structural work rather than
   decorative: HK$1,280 and HK$2,880 then set to exactly the same width, so the
   two rows square off on both edges. With proportional digits the two 8s and
   the two commas land at different offsets and the pair reads as two unrelated
   prices instead of two ends of one scale.
   ——————————————————————————————————————————————————————————————————— */
.sec-pricing-hero .prh-range {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: start;
  align-items: baseline;
  column-gap: var(--s16);

  /* THE ROW GAP IS THE RANGE, AND IT IS WRITTEN AGAINST THE FIGURE.
     What sits between the floor and the ceiling is the interval the sentence
     names; at a table's 12px the two rows are a list, and given room they are
     two ends. This is the STACKED value — the two-column band below derives its
     own, because there the gap has a second job.

     `em` here would have been a bug: this grid inherits the 17px body size, not
     the figure's, so the first cut of this rule set a 10px gap. */
  row-gap: calc(var(--prh-fig) * 1);
  margin: var(--s48) 0 0;
}

/* The connective words. They are not the claim — they are what joins the claim
   to the sentence it came out of, so they take the site's quietest grey at the
   smallest step above caption. Sentence case, no tracking, no rule, no badge:
   an eyebrow stacked over each figure would be the calculator's exact pattern
   one section below, and this has to be a different object. */
.sec-pricing-hero .prh-lab {
  justify-self: end;
  font-family: var(--sans);
  font-size: var(--prh-lab);
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1;
  color: var(--ash);
}

/* The claim. Display serif at 400 — the site sets serif at one weight — with
   tracking pulled toward the display value, because at 38px the stock body
   tracking reads loose under the headline. */
.sec-pricing-hero .prh-fig {
  font-family: var(--serif);
  font-weight: 400;
  font-size: var(--prh-fig);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-optical-sizing: auto;
  white-space: nowrap;
}

/* The full stops belong to the FIGURES and are set at the figures' own size —
   a period shrunk to 0.6em after a big number does not read as punctuation, it
   reads as a speck of dirt on the paper, and dropping it is not available: the
   copy is signed off to the character. Grey, so the digits stay the ink. */
.sec-pricing-hero .prh-stop { color: var(--ash); }

/* THE TERMS. The lede rank the brief asks for, closing the left column. */
.sec-pricing-hero .prh-tail {
  margin-top: var(--s48);
  max-width: 30em;
  text-wrap: pretty;
}

/* ===========================================================================
   THE TWO-COLUMN COMPOSITION — 1000px and up.

   Explicit placement, so the DOM stays in reading order (label → headline →
   bounds → terms) while the grid puts the bounds in the right column. Column
   two is `auto`, so the block is exactly as wide as its own type and its right
   edge lands on the page rail — the same rail the nav's button and the
   calculator plate below both close on.

   TWO BASELINES CROSS THE VOID, AND THEY ARE THE WHOLE COMPOSITION.

       A quarter of a percent of
       what you sell. ————————————————— Never below   HK$1,280.

                                        Never above   HK$2,880.
       Everything included, ———————————————————————————————————

   The floor figure sits on the ITALIC's baseline; the ceiling figure sits on
   the TERMS' baseline. Nothing is drawn between them — the two rules above are
   the relationship, not the render — but the eye reads 300px of empty paper as
   held rather than as leftover, because both of its ends are pinned.

   BOTH TIES ARE DERIVED, NOT NUDGED. Bottom-edge alignment is not baseline
   alignment: a 38px figure on a 1.0 line box and a 20px lede on a 1.45 one
   leave different amounts of box under their baselines, and a hand-tuned pixel
   would only be right at one width and one font size. Two constants do the
   work, both taken from the faces themselves:

     · SOURCE SERIF 4 — content box 1.371em, baseline 1.036em from its top, so
       0.335em of descent below the baseline. On a 1.0 line box the half-leading
       is (1 − 1.371)/2, and the box closes 0.1495em under the baseline.
     · INTER — content box 1.211em, ascent 0.9688em. On the lede's 1.45 line box
       the baseline sits 1.0883em under the box top and the box closes 0.3615em
       under the baseline.

   From those: the terms' first baseline is 0.1595·H (the headline's last line
   box below its own baseline) + --s48 + 1.0883·T under the italic's baseline,
   and two figure baselines are one figure height plus one row gap apart. Set
   them equal and the gap falls out. Verified on the render at 1000, 1024, 1080,
   1200, 1280 and 1440: worst case 0.6px.

   A SIDE EFFECT WORTH THE NAME: because the gap absorbs the headline's own
   size, the bounds block comes out within 6px of the headline's height at every
   width in this band. The right column is a column, not a footnote in a corner.
   =========================================================================== */
@media (min-width: 1000px) {
  .sec-pricing-hero .prh-wrap {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: var(--prh-gap);
    align-items: start;
  }

  .sec-pricing-hero .prh-eyebrow { grid-area: 1 / 1; }
  .sec-pricing-hero .prh-title   { grid-area: 2 / 1; }
  .sec-pricing-hero .prh-tail    { grid-area: 3 / 1; }

  .sec-pricing-hero .prh-range {
    grid-area: 2 / 2 / 4 / 3;
    align-self: end;
    margin: 0;

    /* TIE 2 — the ceiling figure onto the terms' baseline. The block is bottom
       anchored, so this is the difference between the two faces' descent boxes.
       It comes out around 1.5px at the reference width: small, and the reason
       the pair looked 3px out of true before it existed. */
    padding-bottom: calc(0.3615 * var(--t-body-lg) - 0.1495 * var(--prh-fig));

    /* TIE 1 — the floor figure onto the italic's baseline, by making the two
       figure baselines exactly as far apart as the italic's baseline is from
       the terms'. Every term is a token or a face constant, so this re-derives
       itself at 48px headlines and at every step of the figure ramp. */
    row-gap: calc(0.1595 * var(--t-h-lg) + var(--s48)
                  + 1.0883 * var(--t-body-lg) - var(--prh-fig));
  }
}

/* ———————————————————————————————————————————————————————————————————
   TABLET AND BELOW. Nothing re-lays out: the token ramp steps the headline and
   the figures follow the measure down continuously, so the only thing these
   bands owe is proportional vertical air and a proportional label.
   ——————————————————————————————————————————————————————————————————— */
@media (max-width: 1080px) {
  .sec-pricing-hero { --prh-gap: var(--s64); }
}

@media (max-width: 999px) {
  .sec-pricing-hero .prh-range { margin-top: var(--s40); }
  .sec-pricing-hero .prh-tail  { margin-top: var(--s32); }
}

/* ===========================================================================
   THE NARROW BAND — everything at or below 720.

   THE BOUNDS KEEP THEIR TWO COLUMNS. Each row is label + figure and the pair
   measures about 250px at the phone's 26px figures, so both rows clear a 350px
   column: the block stays a specification rather than collapsing into four
   stacked lines, and the two figures still square off on the same two edges.
   =========================================================================== */
@media (max-width: 720px) {
  .sec-pricing-hero { --prh-lab: 14px; }
  /* The step off the headline is bigger than the step off the bounds, so the
     three blocks are a hierarchy rather than an evenly spaced list — the one
     thing a phone stack has instead of a composition. */
  .sec-pricing-hero .prh-range { margin-top: var(--s40); row-gap: calc(var(--prh-fig) * 0.9); }
  /* No narrow-band measure on the terms. Capping it at 22em here forced two
     lines at 720 while 721 held one, so the 720/721 seam visibly jumped a line;
     the 30em cap plus the column is enough to wrap it where it actually needs
     to wrap, and the two no-break spaces in the markup decide where. */
  .sec-pricing-hero .prh-tail  { margin-top: var(--s28); }
}

/* ===========================================================================
   SUB-BAND, 640 AND DOWN — the three-line headline stack.

   Only the BREAK changes here, not the size: --t-h-lg is 34px through this band
   and the authored narrow-column <br> splits line one so the stack sets
   "A quarter of / a percent of / what you sell." Every line clears a 350px
   column with room to spare in Source Serif and in the Georgia fallback alike,
   so there is no per-section divisor left to maintain — the global ramp is the
   only thing sizing this headline at any width.
   =========================================================================== */
@media (max-width: 640px) {
  .sec-pricing-hero .prh-br-m { display: block; }

  /*
   * THE GROUND, RE-DERIVED FOR THE PHONE — same idea, different arithmetic.
   * The desktop percentages are against a 1584 x 979 box; at 390 the box is
   * 429 x 927 and its top sits 84px above the section, so every number moves.
   * The core has to follow the ink, which on a phone is one narrow column, so
   * the field goes wide and shallow — a narrow tall column wants a wide shallow
   * field, or the wash stops marking a place and just fills the frame. At that
   * radius it still runs off both flanks, which is the point.
   */
  .sec-pricing-hero > .wash {
    background:
      radial-gradient(98% 34% at 56% 50%,
        rgba(255, 217, 194, 0.56) 0%, rgba(255, 217, 194, 0.38) 30%,
        rgba(255, 217, 194, 0.15) 62%, rgba(255, 217, 194, 0.02) 88%,
        rgba(255, 217, 194, 0) 100%),
      radial-gradient(74% 30% at -6% 28%,
        rgba(255, 233, 214, 0.72) 0%, rgba(255, 233, 214, 0.22) 34%,
        rgba(255, 233, 214, 0) 86%);
  }
}

/* ===== section:pricing-calc ===== */
/* ============================================================================
   pricing-calc — the live fee calculator.

   Five decisions, and one deletion that is bigger than any of them.

   0. THE DELETION. This section used to draw a fee-against-sales step curve
      with a plateau leaving the page. pricing-ceiling — the very next section
      — is a fee-against-sales chart whose line leaves its plate, drawn to the
      same datum (HK$1,152,000). Two consecutive sections were making the same
      argument with the same device, and the calculator was the one carrying it
      as decoration: it also owned a horizontal control with a mark on it, so
      the pane held two horizontal measured instruments stacked 20px apart, and
      the level labels floated in an 860x162 letterbox with nothing to attach
      to. The chart is now pricing-ceiling's alone. What replaced it here is
      the ONE small mark the brief asks for — the fee's position between its
      floor and its ceiling — which the chart never actually showed.

   1. OFF AXIS, AND CROPPED — AND THE CROP CUTS AT THE INSTRUMENT. The plate's
      LEFT edge is locked to the page measure and its right edge runs past the
      viewport, which cuts it. No right border, no right corner radius — not a
      card in a frame, a plate the frame happens to end on. The clearance
      between the last mark and that cut used to be 90px of empty ground, which
      does not read as a bleed, it reads as a border someone forgot: the track,
      its thumb at maximum and its axis all stopped well short. --cx-safe is now
      12px, so the control runs out to within a dozen pixels of the frame and
      the ground leaves immediately after it. Nothing is clipped mid-figure —
      the thumb has to stay grabbable at both ends, and a half-cut axis number
      is a datum you cannot read.

   1b. THE ANSWER IS THE HERO, BY A FACTOR OF FIVE. The fee and the sales figure
      used to be set at 64 and 52 and measured within 3% of each other in
      numeral area — the input was actually the WIDER object — so a calculator
      whose whole job is to land a verdict had two focal points and therefore
      none. The fee is now ~92px and the sales figure ~34px, the same size as
      the rate: one display figure, two small readouts, and the two small ones
      sit either side of the seam so they read as a pair. The sales figure is an
      echo of a slider the reader already has a thumb on. It does not need to be
      a display figure at all.

   2. NO ELEVATION. A hairline and a ground WARMER and LIGHTER than the paper
      do the separating, which is how card stock separates from a sheet; a cool
      grey panel with a soft shadow is a screen UI. The old plate measured
      249,249,249 on a 251,250,248 paper — cooler AND darker than the sheet it
      claimed to lie on.

   3. TWO PANES, ONE INSTRUMENT EACH, EYEBROWS DEAD LEVEL, AND THE TWO
      INSTRUMENTS ON ONE RAIL. Left: the fee, the rate, the fee's scale, the
      note. Right: the sales figure and the control it is set with.

      THE STAGGER IS DELETED. It was tried at 18px, where this file's own
      comment called it "a failed alignment rather than a decision", and then at
      55px across a 1440px plate, which is the same zone: a diagonal you have to
      be told about is not a diagonal. Either it goes past 160px or it goes. It
      goes — because with the fee now five times the sales figure's numeral area
      the asymmetry is carried by MASS, and a 55px drop was only ever a
      substitute for a hierarchy that was not there.

      What replaces it is a real alignment instead of a near one: --cx-rail puts
      the gauge's hairline and the control's track on exactly the same y, so the
      reading and the thing that sets it are one line of one instrument running
      across the seam. The offset is derived from the two figure sizes and the
      band's own margin, so it holds at every width the side-by-side layout
      takes rather than at one.

   4. THE THRESHOLDS ARE MARKED ON THE CONTROL ITSELF. Not on a separate chart:
      two hairline ticks under the track at HK$512,000 and HK$1,152,000, at the
      exact places the thumb passes them, each with its number under it. The
      regime changes are annotations on the dial, not a second drawing. Where
      the numbers cannot fit, the ticks leave with them — a threshold tick with
      its label taken away is an unexplained hairline.

   5. THE VERDICT IS THE SCALE'S CAPTION, AND IT IS SILENT IN THE MIDDLE.
      "You are at the floor." sits directly under the mark that is sitting on
      the floor cap. It is the section's one italic. In the 0.25% band it now
      prints nothing at all: "Exactly 0.25%." was a caption restating the rate
      readout 100px above it AND the note two lines below it, in the default
      state — the state the section is seen in. One italic, spent on the two
      readings that are not restatements. Its line is reserved either way so
      crossing a threshold never jogs the note.

   6. THE FEE'S SCALE IS HELD TO THE FEE. It used to span both readout columns:
      336px of scale datumed HK$1,280/HK$2,880 overhanging the fee column by
      137px and running on under the rate, which it does not measure. It is now
      grid column 1 — an instrument the width of its own subject.
   ========================================================================== */

.sec-pricing-calc {
  position: relative;
  z-index: 2;                       /* the plate sits above the hero's wash */
  /* THE CROP IS MADE HERE. The plate runs past the right of the screen;
     without a local clip the DOCUMENT grows to its width — the page measured
     1560px against a 1440px viewport, which means the crop never happened, it
     just widened the page. `clip` on x with y left visible cuts at the
     section's own box (== the viewport) without creating a scroll container,
     so the sticky nav is untouched. */
  overflow-x: clip;
  /* NO TOP PADDING AND NO LIFT. The hero above closes on the loose section
     rhythm and this plate sits directly in that air, so the gap to the housing
     is one number owned by one rule instead of a private lift measured against
     a private amount of hero ground. */
  padding-top: 0;
  padding-bottom: var(--section-y);

  --cx-pad: clamp(28px, 3.4vw, 56px);   /* plate padding, left                 */
  --cx-pady: clamp(34px, 4.6vw, 68px);  /* plate padding, top and bottom       */
  --cx-crop: 140px;                     /* how far the plate runs past the eye */
  /* CLEARANCE FROM THE LAST MARK TO THE CUT. This was 72px, and with the axis
     inset on top of it the instrument stopped 90px short of the frame — the
     crop then cut nothing but ground, which reads as a missing border rather
     than as a bleed. 12px is optical margin: the track, the thumb at maximum
     and the axis all run out to the edge and the plate leaves right after
     them. It cannot go to zero — the thumb is 24px wide and has to stay
     grabbable at max, and its focus ring needs 5px beyond that. */
  --cx-safe: 12px;
  --cx-col: clamp(340px, 38vw, 520px);  /* the answer pane                     */
  --cx-gap: clamp(44px, 5vw, 80px);

  /* THE THREE FIGURE SIZES, DECLARED ONCE, BECAUSE THE RAIL IS DERIVED FROM
     THEM. The fee is the only display figure on the plate; the sales figure and
     the rate are the same small size on either side of the seam. */
  --cx-fee-size:   clamp(48px, 6.4vw, 92px);
  --cx-sales-size: clamp(25px, 2.4vw, 34px);
  --cx-rate-size:  clamp(23px, 2.6vw, 34px);
  --cx-band-mt:    clamp(26px, 2.6vw, 36px);

  /* THE RAIL. The drop that puts the control's track on the same y as the
     gauge's hairline, side by side. Both panes open with the same eyebrow and
     the same --eyebrow-gap, so those cancel; what is left is the difference
     between the two figures (both line-height:1), plus the band's margin, plus
     6.5px to the gauge line's centre, minus 22px to the track's centre inside
     its 44px hit area. Derived, not measured, so it holds at every width. */
  --cx-rail: calc(var(--cx-fee-size) - var(--cx-sales-size) + var(--cx-band-mt) - 15.5px);

  /* The control's axis inset. 12px is half the thumb; --cx-edge is extra
     clearance so neither the thumb nor its focus ring collides with anything
     at the extremes. It is applied as PADDING on the input, which insets the
     runnable track, so the thumb's travel and the ticks' x-range are the same
     interval — that identity is what puts a tick under the thumb that passes
     it. Verified: thumb centre and tick agree to 0.1px at 390, 720, 1120 and
     1440. */
  --cx-edge: 6px;
  --cx-half: calc(12px + var(--cx-edge));

  --cx-t: 0.28421;                  /* 640,000 of 100,000–2,000,000           */
  --cx-g: 0.2;                      /* fee 1,600 of the 1,280–2,880 interval  */

  /* Declared once: the plate's ground, so every mark that has to break a line
     it crosses paints itself in the surface it is standing on. */
  --cx-ground: color-mix(in srgb, var(--wash-2) 24%, var(--raised));
}

/* Isolated preview (and any page where this leads): nothing above to overlap. */
main > .sec-pricing-calc:first-child { padding-top: var(--section-y); }

/* ── The plate ───────────────────────────────────────────────────────────
   margin-right: 50% - 50vw takes the right edge exactly to the viewport; the
   extra --cx-crop takes it past, so there is no corner, no border and no gap
   on that side at any width. */
.sec-pricing-calc .cx {
  position: relative;
  margin-right: calc(50% - 50vw - var(--cx-crop));
  padding: var(--cx-pady) calc(var(--cx-crop) + var(--cx-safe)) var(--cx-pady) var(--cx-pad);

  background: var(--cx-ground);
  border: 1px solid var(--hair-strong);
  border-right: 0;
  border-radius: var(--r-card) 0 0 var(--r-card);

  display: grid;
  grid-template-columns: var(--cx-col) minmax(0, 1fr);
  column-gap: var(--cx-gap);
  align-items: stretch;
}

/* Both panes are flex columns so each can pin or float its own lower block. */
.sec-pricing-calc .cx__answer,
.sec-pricing-calc .cx__dial { display: flex; flex-direction: column; }
.sec-pricing-calc .cx__answer { grid-area: 1 / 1; }
/* DEAD LEVEL. The stagger is gone — see note 3. Both eyebrows open on the same
   line and the seam carries the split; the asymmetry is the 5:1 difference in
   numeral mass across it, which does not need a 55px nudge to be legible. */
.sec-pricing-calc .cx__dial   { grid-area: 1 / 2; padding-top: 0; }
/* The note is pinned to the plate's floor; the control is NOT. An earlier
   pass bottom-pinned both, which put the two lower blocks on one rail and left
   a 300px hole in the middle of the dial pane where the deleted chart used to
   be — a void with no shape, directly between the figure and the thing that
   sets it. The control now follows its own figure at a fixed interval and the
   air falls at the BOTTOM of the pane, where it reads as a margin. */
.sec-pricing-calc .cx__note { margin-top: auto; padding-top: clamp(28px, 3.4vw, 52px); }
/* The control drops to the rail — see --cx-rail. */
.sec-pricing-calc .cx__rig  { padding-top: var(--cx-rail); }

/* The seam. Absolutely placed, so it is out of the grid flow and spans the
   plate's full height including its padding. */
.sec-pricing-calc .cx__seam {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--cx-pad) + var(--cx-col) + var(--cx-gap) / 2);
  width: 1px;
  background: var(--hair);
}

/* ── Figures ─────────────────────────────────────────────────────────────
   Serif, 400, tabular, and TIGHT: at these sizes default tracking leaves the
   comma and the zeros swimming. The currency code is set at 0.55em on a raised
   baseline so the amount is the hero — at full size H, K and the mark together
   occupied nearly as much width as the number they qualify. */
.sec-pricing-calc .cx__sales,
.sec-pricing-calc .cx__fee,
.sec-pricing-calc .cx__rate {
  font-family: var(--serif);
  font-weight: 400;
  font-optical-sizing: auto;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.sec-pricing-calc .cx__cur {
  font-size: 0.55em;
  vertical-align: 0.38em;
  letter-spacing: 0;
  margin-right: 0.07em;
  color: var(--slate);
}

/* ── The answer ───────────────────────────────────────────────────────────
   The fee column is max-content — the digits are tabular, so its width never
   changes as the value does and the row cannot shuffle while you drag; the
   rate column takes the rest. The 236px floor exists because the fee's scale
   now lives in this column and has to hold two 12px datum labels side by side;
   at the narrow end of the side-by-side range max-content alone left them 8px
   of slack. Below 1121 the column reverts to plain max-content and the scale
   spans both. */
.sec-pricing-calc .cx__out {
  display: grid;
  grid-template-columns: minmax(236px, max-content) minmax(0, 1fr);
  column-gap: clamp(24px, 3vw, 48px);
  align-items: baseline;
}
/* Every eyebrow here is a grid or flex participant whose gap its container
   owns, so all three take the one legal override — margin-bottom: 0 — and the
   gap under them is the system's --eyebrow-gap, stated on the figure. Three
   eyebrows on one plate must not carry three different gaps. */
.sec-pricing-calc .cx__ey { margin-bottom: 0; }
.sec-pricing-calc .cx__ey--fee  { grid-area: 1 / 1; }
.sec-pricing-calc .cx__ey--rate { grid-area: 1 / 2; }
.sec-pricing-calc .cx__fee      { grid-area: 2 / 1; margin-top: var(--eyebrow-gap); }
.sec-pricing-calc .cx__rate     { grid-area: 2 / 2; margin-top: var(--eyebrow-gap); color: var(--slate); }
/* COLUMN 1 ONLY. A scale datumed to the fee stops where the fee stops. */
.sec-pricing-calc .cx__band     { grid-area: 3 / 1; margin-top: var(--cx-band-mt); }
/* The verdict is the caption of that mark, but it is a sentence, so it takes
   the whole measure rather than being squeezed into the scale's width. */
.sec-pricing-calc .cx__state    { grid-area: 4 / 1 / 5 / 3; }

.sec-pricing-calc .cx__fee  { font-size: var(--cx-fee-size); }
.sec-pricing-calc .cx__rate { font-size: var(--cx-rate-size); }

/* ── The fee's scale ──────────────────────────────────────────────────────
   A hairline between two caps. The caps ARE the datums: the left one is
   HK$1,280 and the right one HK$2,880, and the dot's centre is placed at
   (fee - 1280) / 1600 of the interval between their centres — the 8px inset
   on both the caps and the dot's travel is what makes those two intervals the
   same one. Nothing here is nudged to a pixel floor: at the floor and at the
   ceiling the dot sits exactly on a cap and stays there while you drag, which
   is the pricing rule demonstrated. */
.sec-pricing-calc .cx__gauge {
  position: relative;
  /* The column now sets the measure — see note 6. */
  width: 100%;
  height: 13px;
}
.sec-pricing-calc .cx__gline {
  position: absolute;
  left: 8px; right: 8px; top: 6px;
  height: 1px;
  background: var(--hair-strong);
}
.sec-pricing-calc .cx__gcap {
  position: absolute;
  top: 2px;
  width: 1px;
  height: 9px;
  background: var(--hair-strong);
}
/* Centred ON the ends of the dot's travel, not butted against them: a 1px cap
   placed at left:8 has its CENTRE at 8.5, which put every reading half a pixel
   out and the two datum marks half a pixel apart from the thing they datum. */
.sec-pricing-calc .cx__gcap--lo { left: 8px; transform: translateX(-50%); }
.sec-pricing-calc .cx__gcap--hi { right: 8px; transform: translateX(50%); }
/* The mark. The ring is the hidden-line removal: the hairline BREAKS where the
   dot crosses it rather than drawing through. */
.sec-pricing-calc .cx__gdot {
  position: absolute;
  left: calc(8px + var(--cx-g) * (100% - 16px));
  top: 6px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 0 0 3px var(--cx-ground);
  transform: translate(-50%, -50%);
  transition: left 90ms linear;
}

.sec-pricing-calc .cx__gscale {
  display: flex;
  justify-content: space-between;
  gap: var(--s12);
  width: 100%;
  padding-inline: 8px;
  margin-top: 9px;
  font-size: 12px;
  line-height: 1;
  color: var(--ash);
}

/* The section's ONE italic, the caption of the mark above it — and EMPTY in the
   0.25% band, where its sentence only restated the rate readout above it and
   the note below it. See note 5. */
.sec-pricing-calc .cx__state {
  font-family: var(--serif);
  font-weight: 400;
  font-style: italic;
  font-optical-sizing: auto;
  font-size: clamp(19px, 1.8vw, 23px);
  line-height: 1.3;
  letter-spacing: -0.012em;
  color: var(--slate);
  margin-top: clamp(18px, 1.8vw, 24px);
  /* One line is reserved whether or not there is a verdict to print, so
     dragging across a threshold never jogs the note under it. Every verdict
     fits one line at every width this row takes. */
  min-height: 1.3em;
  max-width: 34ch;
}

.sec-pricing-calc .cx__note { max-width: 42ch; text-wrap: pretty; }

/* ── The dial ────────────────────────────────────────────────────────────── */
.sec-pricing-calc .cx__label { margin-bottom: 0; }
.sec-pricing-calc .cx__sales { margin-top: var(--eyebrow-gap); font-size: var(--cx-sales-size); }

/* ── The control ─────────────────────────────────────────────────────────
   A 2px hairline track and a 24px ink thumb inside a full 44px hit area. An
   ink-filled portion behind the thumb was tried and cut: the thumb already
   says where the value is, and a filled bar next to two threshold ticks reads
   as progress towards something. */
.sec-pricing-calc .cx__control {
  position: relative;
  height: 44px;
}
.sec-pricing-calc .cx__track {
  position: absolute;
  top: 21px;
  left: var(--cx-half);
  right: var(--cx-half);
  height: 2px;
  border-radius: 2px;
  pointer-events: none;
  /* Deliberately darker than the ticks: this is the line you can grab. */
  background: color-mix(in srgb, var(--ink) 26%, transparent);
}
/* The two regime changes, marked where the thumb passes them. Below the track,
   pointing at their own numbers. */
.sec-pricing-calc .cx__tick {
  position: absolute;
  top: 23px;
  width: 1px;
  height: 11px;
  background: var(--hair-strong);
  pointer-events: none;
  left: calc(var(--cx-half) + var(--at) * (100% - var(--cx-half) * 2));
  /* Centred ON the datum, like the gauge's caps: a 1px mark placed BY its left
     edge has its centre half a pixel to the right of the thumb centre it is
     supposed to sit under. */
  transform: translateX(-50%);
}

.sec-pricing-calc .cx__range {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 44px;
  margin: 0;
  background: transparent;
  padding-inline: var(--cx-edge);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.sec-pricing-calc .cx__range::-webkit-slider-runnable-track {
  height: 44px; background: transparent; border: 0;
}
.sec-pricing-calc .cx__range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 24px;
  height: 24px;
  margin-top: 10px;                 /* centre a 24px thumb on a 44px track */
  border: 0;
  border-radius: 50%;
  background: var(--ink);
  transition: box-shadow var(--dur-fast) var(--ease-out);
}
.sec-pricing-calc .cx__range::-moz-range-track {
  height: 44px; background: transparent; border: 0;
}
.sec-pricing-calc .cx__range::-moz-range-thumb {
  width: 24px; height: 24px; border: 0; border-radius: 50%;
  background: var(--ink);
  transition: box-shadow var(--dur-fast) var(--ease-out);
}
/* Focus rings the thumb rather than boxing the full-measure bar. */
.sec-pricing-calc .cx__range:focus { outline: none; }
.sec-pricing-calc .cx__range:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 3px var(--cx-ground), 0 0 0 5px var(--ink);
}
.sec-pricing-calc .cx__range:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 3px var(--cx-ground), 0 0 0 5px var(--ink);
}
.sec-pricing-calc .cx__range:hover::-webkit-slider-thumb { box-shadow: 0 0 0 6px var(--hair); }
.sec-pricing-calc .cx__range:hover::-moz-range-thumb     { box-shadow: 0 0 0 6px var(--hair); }

/* ── The axis ─────────────────────────────────────────────────────────────
   Same measure, same inset as the track. Both ENDPOINTS always show — a range
   has to have two ends — and the two interior labels stand under the ticks. */
.sec-pricing-calc .cx__axis {
  position: relative;
  height: 15px;
  margin-top: var(--s12);
}
.sec-pricing-calc .cx__ax {
  position: absolute;
  top: 0;
  font-size: 12px;
  line-height: 1;
  color: var(--ash);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.sec-pricing-calc .cx__ax--min { left: var(--cx-half); }
.sec-pricing-calc .cx__ax--max { right: var(--cx-half); }
.sec-pricing-calc .cx__ax--thr {
  left: calc(var(--cx-half) + var(--at) * (100% - var(--cx-half) * 2));
  transform: translateX(-50%);
  color: var(--slate);
}

/* ── Stacked ─────────────────────────────────────────────────────────────
   One column, dial FIRST: you set the value, then you read the answer. The
   seam leaves the grid's absolute placement and becomes a full-bleed rule that
   runs off the crop with the plate. */
@media (max-width: 1120px) {
  .sec-pricing-calc { --cx-crop: 80px; --cx-safe: 44px; }
  .sec-pricing-calc .cx {
    grid-template-columns: minmax(0, 1fr);
  }
  .sec-pricing-calc .cx__dial   { grid-area: 1 / 1; }
  .sec-pricing-calc .cx__answer { grid-area: 3 / 1; }
  /* No rail here: the two instruments are one above the other, not side by
     side, so the derived drop has nothing to line up with. */
  .sec-pricing-calc .cx__rig    { padding-top: clamp(34px, 4vw, 52px); }

  /* Stacked, the readout is no longer holding the scale up: column 1 shrinks
     with the smaller fee and two 12px datum labels will not fit inside it, so
     the scale spans the readout again and takes its own cap. */
  .sec-pricing-calc .cx__out   { grid-template-columns: max-content minmax(0, 1fr); }
  .sec-pricing-calc .cx__band  { grid-column: 1 / 3; }
  .sec-pricing-calc .cx__gauge,
  .sec-pricing-calc .cx__gscale { width: min(100%, 420px); }
  .sec-pricing-calc .cx__seam {
    grid-area: 2 / 1;
    position: static;
    width: auto;
    height: 1px;
    margin: clamp(32px, 4vw, 46px) calc((var(--cx-crop) + var(--cx-safe)) * -1)
            clamp(30px, 3.6vw, 42px) calc(var(--cx-pad) * -1);
  }
  .sec-pricing-calc .cx__state { max-width: 30ch; }
  .sec-pricing-calc .cx__note  { max-width: 58ch; }
}

/* THE ANSWER SPLITS SIDEWAYS WHEN THE PANES STOP SPLITTING — BUT ONLY WHILE
   THERE IS WIDTH FOR IT. Stacked, the answer inherits the whole plate and uses
   about half of it: the scale marooned under the fee and the note stranded
   below a 140px gap with an empty right half beside it. Side by side, the
   block fills the measure it has been given and loses a band of height.

   THE LOWER BOUND IS LOAD-BEARING. Applied across the whole stacked range this
   also fired at 768, where 38ch for the note left the readout 328px and the
   rate column 154px, and "Your effective rate" broke over THREE lines while
   its sibling eyebrow sat on one — the shared row-1 baseline the readout is
   built on, gone. Below 900 the answer is one column again. */
@media (min-width: 900px) and (max-width: 1120px) {
  .sec-pricing-calc .cx__answer {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 38ch);
    column-gap: clamp(40px, 5vw, 80px);
    align-items: start;
  }
  .sec-pricing-calc .cx__out  { grid-column: 1; }
  .sec-pricing-calc .cx__note { grid-column: 2; margin-top: 0; padding-top: 6px; }
}

/* ── Narrow ───────────────────────────────────────────────────────────────
   The readout stays a REAL two-column row — fee large, rate small — rather
   than collapsing into a left-aligned dump. The fee's scale spans both
   columns, because at this width the fee column alone is 165px and a scale
   that short cannot show a dot moving. */
@media (max-width: 720px) {
  .sec-pricing-calc {
    /* A bigger crop, not a smaller one. At 28px the plate cleared the viewport
       by 8px, which reads as a card that not quite fits rather than as a plate
       the frame cuts. */
    --cx-crop: 64px;
    --cx-safe: 14px;
    --cx-pad: 22px;
    --cx-pady: 30px;
    --cx-edge: 10px;
  }
  /* The three figure sizes are already at their clamp floors here — 48 / 25 /
     23 — and the floors were set so the fee still carries better than twice the
     sales figure's numeral area on a 390px screen. Nothing to override. */
  .sec-pricing-calc .cx__note   { padding-top: clamp(26px, 5vw, 34px); }
  /* The 420px cap from the stacked rule still applies: a scale stretched to a
     663px tablet plate under a 154px fee is the same overhang the side-by-side
     layout was pulled back from. */
  .sec-pricing-calc .cx__state { max-width: none; }
  .sec-pricing-calc .cx__ax { font-size: 11px; }
}

/* Below this the four axis labels cannot all stand clear of one another, so
   the two interior numbers go and the two ENDPOINTS stay: a range with both
   its ends is legible; two labels clustered left of centre under an empty
   right half is not. A threshold tick with its number taken away is an
   unexplained hairline on the dial, so the tick leaves with its own label
   rather than without it; the note two lines below names both figures. */
@media (max-width: 560px) {
  .sec-pricing-calc .cx__ax--thr,
  .sec-pricing-calc .cx__tick { display: none; }
  .sec-pricing-calc .cx__out { column-gap: var(--s20); }
  .sec-pricing-calc .cx__gscale { font-size: 11px; }
}

@media (prefers-reduced-motion: reduce) {
  .sec-pricing-calc .cx__gdot { transition: none; }
}

/* ===== section:pricing-ceiling ===== */
/* ═══════════════════════════════════════════════════════════════════════
   pricing-ceiling — "What the ceiling means"
   A split header on one baseline, then one line chart on a white plate that
   the plot is deliberately bigger than.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── HEAD ─────────────────────────────────────────────────────────────
   Two rails. Eyebrow and claim hold the left; the sub hangs off a narrow
   right rail and shares the claim's bottom line. `align-items: end` does
   the baseline work — the sub is two short lines against the claim's two
   long ones, so the void lands above the sub, which is where it belongs. */
.sec-pricing-ceiling .ce-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 320px);
  grid-template-areas:
    'eyebrow .'
    'title   sub';
  column-gap: var(--s64);
  row-gap: var(--eyebrow-gap);
  align-items: end;
}
/* The head grid's row-gap is the eyebrow gap, so the component's own is off. */
.sec-pricing-ceiling .ce-eyebrow { grid-area: eyebrow; margin-bottom: 0; }
.sec-pricing-ceiling .ce-title   { grid-area: title; max-width: 860px; }
.sec-pricing-ceiling .ce-sub     { grid-area: sub; max-width: 320px; }

/* Optical, not metric: .h-lg sits on line-height 1.1 and .lede on 1.45, so
   box-bottom alignment leaves the sub's baseline a few pixels low. */
.sec-pricing-ceiling .ce-sub { padding-bottom: 5px; }

/* ── FIGURE ───────────────────────────────────────────────────────────
   Only one stage paints at a time; the other is display:none so its paths
   are never observed and never measured. */
.sec-pricing-ceiling .ce-fig { margin: 0; margin-top: var(--s48); }

.sec-pricing-ceiling .ce-stage { position: relative; }
.sec-pricing-ceiling .ce-stage--wide { aspect-ratio: 1200 / 400; }
.sec-pricing-ceiling .ce-stage--tall { display: none; aspect-ratio: 480 / 640; max-width: 480px; }

/* THE PLATE IS A WINDOW, AND IT IS A LETTERBOX. 880 of the 1200-unit stage
   wide, hard left on the page measure; 262 of its 400 tall, sitting on the
   stage's bottom edge.

   THE TOP CROP IS THE POINT OF THE PROPORTION. A full-height plate put the
   plot's diagonal across the lower half and left the upper half of a white
   card carrying nothing — a frame the drawing had failed to fill. Cutting the
   plate at y=138 means the far branch leaves through the TOP edge at x=779,
   so the white that remains is a triangle bounded by two inked lines instead
   of a bald rectangle, and the strip of fog above the card is where that
   branch does its escaping.

   AND THE EDGE IS RULED, NOT IMPLIED. This is the fix the whole composition
   was waiting on. .artifact is --raised (#ffffff); the band is --fog
   (#fafafb). Five values apart before the page grain, ~2 after it — and
   --sh-artifact throws its blur DOWNWARD, so the top edge (the one the escape
   crosses) and the bottom edge (the one that severs the stem) both had
   nothing on them at all. No token pair on this site can separate white from
   fog by value, so the boundary is DRAWN: one hairline ring at --hair-strong,
   which resolves to ~#dedfe0 over the fog, about 27 values of contrast, sat on
   top of the artifact shadow rather than replacing it. A window you can see
   is the difference between a plot that escapes a frame and a plot with a
   stray diagonal in it. */
.sec-pricing-ceiling .ce-plate {
  position: absolute;
  inset: 34.5% auto 0 0;
  width: 73.333%;
  box-shadow: var(--sh-artifact), 0 0 0 1px var(--hair-strong);
}
.sec-pricing-ceiling .ce-stage--tall .ce-plate {
  /* Portrait: the plate holds the lower 85% of a 480 x 640 stage, so the card
     is 480 x 544 — a PORTRAIT rectangle. It used to be 480 x 392 inside a
     480 x 560 stage, which is square to within 2%, and a square plate with a
     line through one corner of it has two dead quadrants by construction.
     The strip above is the fog the escaping branch and its label live in —
     15%, not the 30% it was, so the label lands ~20px clear of the plate's top
     edge instead of stranded ~75px out in a bare band. */
  inset: 15% 0 0 0;
  width: auto;
}

/* The chart overlays the whole stage, plate included. It is CLIPPED to the
   stage, and that clip is load-bearing: it is what severs the stem at the
   plate's bottom edge. Every other mark is authored inside the viewBox, so
   nothing else is touched by it. */
.sec-pricing-ceiling .ce-chart {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

/* ── MARKS ────────────────────────────────────────────────────────────
   Colour carries the hierarchy, not weight: one stroke width for the whole
   series. non-scaling-stroke keeps that width honest at every viewport, and
   the stage never renders wider than its viewBox, so the dash length the
   motion layer measures is always >= the drawn length. */
.sec-pricing-ceiling .ce-line,
.sec-pricing-ceiling .ce-tick,
.sec-pricing-ceiling .ce-ceiling {
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.sec-pricing-ceiling .ce-line--stem { stroke: var(--ink); }
.sec-pricing-ceiling .ce-line--us   { stroke: var(--ink); }

/* THE ESCAPE IS THE DARKEST MARK OUTSIDE THE PLATE. It was --smoke: the
   palest stroke in the palette, on the palest ground on the site, carrying
   the one sentence the section exists to make. The value logic is inverted
   now — the branch that runs off the page is --slate, plainly visible over
   bare fog, and the difference between the two series is carried by
   flat-versus-climbing rather than by one of them being hard to find. */
.sec-pricing-ceiling .ce-line--them { stroke: var(--slate); }

/* A CUT, NOT A TERMINUS. The stem is authored past the stage's bottom
   boundary and hard-clipped there, so it needs butt caps: a round cap would
   put a domed end on the severed edge and undo the crop the clip just made. */
.sec-pricing-ceiling .ce-line--stem { stroke-linecap: butt; }

/* THE CEILING. One hairline, dashed, at the ceiling fee — the datum the
   headline is about. Butt caps, not the round caps the series carry: a round
   cap on a 2-unit dash draws a lozenge, and the datum reads as a string of
   beads rather than a ruled limit. */
.sec-pricing-ceiling .ce-ceiling {
  stroke: var(--hair-strong);
  stroke-width: 1;
  stroke-linecap: butt;
  stroke-dasharray: 2 6;
}
/* The leader under the split. It is a shade stronger than the ceiling and
   solid where the ceiling is dashed: at --hair-strong it was invisible at
   1440, and a mark you cannot see is a mark that has left the number it
   carries floating unattached in the middle of the plate. */
.sec-pricing-ceiling .ce-tick { stroke: var(--smoke); stroke-width: 1; }
.sec-pricing-ceiling .ce-node { stroke: none; fill: var(--ink); }

/* ── CHOREOGRAPHY ─────────────────────────────────────────────────────
   The picture assembles in the order the argument is made: the shared stem
   climbs, the node lands, then the two branches leave it together and their
   labels follow. Durations are set per branch so the two leave that node at
   the same apparent speed rather than finishing together.

   Only durations and delays are set here. The transition itself, and its
   whole-cloth cancellation under prefers-reduced-motion, stay in the motion
   layer — `transition: none !important` there resets these longhands too. */
.sec-pricing-ceiling .ce-line--stem { transition-duration: 700ms;  transition-delay: 140ms; }
.sec-pricing-ceiling .ce-line--them { transition-duration: 1000ms; transition-delay: 840ms; }
.sec-pricing-ceiling .ce-line--us   { transition-duration: 760ms;  transition-delay: 840ms; }

/* A chart dot that slides 18px into place is a chart dot in the wrong place
   for 700ms. The nodes, the tick and the labels keep the reveal's fade and
   drop its travel. */
.sec-pricing-ceiling .ce-node,
.sec-pricing-ceiling .ce-tick { transform: none; transition-duration: 420ms; }
.sec-pricing-ceiling .ce-node--split,
.sec-pricing-ceiling .ce-tick      { transition-delay: 820ms; }
.sec-pricing-ceiling .ce-node--end { transition-delay: 1520ms; }

/* ── LABELS ───────────────────────────────────────────────────────────
   Real HTML type, not <text>: a label inside the viewBox would shrink with
   the stage and read at 10px on a tablet. These stay 14px everywhere and
   keep tabular figures. */
.sec-pricing-ceiling .ce-key {
  position: absolute;
  font-size: 14px;
  font-weight: 450;
  line-height: 1.3;
  letter-spacing: -0.005em;
  white-space: nowrap;
  transform: none;
}
.sec-pricing-ceiling .ce-key--them  { color: var(--slate); transition-delay: 1600ms; }
.sec-pricing-ceiling .ce-key--us    { color: var(--ink);   transition-delay: 1520ms; }
.sec-pricing-ceiling .ce-key--pivot { color: var(--ash); font-size: 13px; transition-delay: 900ms; }

/* EVERY OFFSET IS PERCENT-TO-THE-DATUM PLUS PIXELS-FOR-THE-GAP.
   Getting this wrong is what a first pass looks like: a label placed at a
   flat percentage keeps its distance from the line only at the one width it
   was tuned at, because the stage scales and the 14px type does not. So each
   label is pinned to its datum's percentage on the side that faces the mark,
   and the clearance is a constant margin in px. */

/* wide stage — 1200 x 400 */
/* their branch ends bare on the fog at (1046, 41.3) */
.sec-pricing-ceiling .ce-stage--wide .ce-key--them {
  left: 87.167%; top: 10.325%; margin-left: 12px; transform: translateY(-50%);
}
/* ours terminates on its node at (840, 250); the label rides above the flat
   run with its right edge over that node */
.sec-pricing-ceiling .ce-stage--wide .ce-key--us {
  right: 30%; bottom: 37.5%; margin-bottom: 10px;
}
/* the split is at (470, 250); the tick's foot is (470, 292) */
.sec-pricing-ceiling .ce-stage--wide .ce-key--pivot {
  left: 39.167%; top: 73%; margin-left: 11px; margin-top: 1px;
}

/* tall stage — 480 x 640. The escape label is pinned to the branch WHERE IT
   PASSES y=56 — (322.5, 56) = 67.188% / 8.75% — not to the branch's end, so
   the line runs on past its own label to y=30 and the label still sits ~20px
   off the plate's top edge. Ours is on its node at (420, 470) = 87.5% /
   73.438%; the tick's foot is (150, 512) = 31.25% / 80%. */
.sec-pricing-ceiling .ce-stage--tall .ce-key--them {
  left: 67.188%; top: 8.75%; margin-left: 10px; transform: translateY(-50%);
}
.sec-pricing-ceiling .ce-stage--tall .ce-key--us {
  right: 12.5%; bottom: 26.563%; margin-bottom: 9px;
}
.sec-pricing-ceiling .ce-stage--tall .ce-key--pivot {
  left: 31.25%; top: 80%; margin-left: 10px; margin-top: 1px;
}

/* ── THE AXIS NOTE ────────────────────────────────────────────────────
   Outside the plate, on the page rail, one line. It is the bottom-left
   terminus of the composition; the escaping branch's label is the top-right
   one, and the plate sits on the diagonal between them. */
.sec-pricing-ceiling .ce-note {
  margin-top: var(--s24);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ash);
}

/* ── RESPONSIVE ───────────────────────────────────────────────────────
   Not one layout squeezed. The split header needs the claim's longest line
   — "Your bill stops growing" — to hold the left column at display scale, so
   it stacks at 960 rather than start breaking a 64px serif. Below 780 the
   chart is re-plotted portrait. */
@media (max-width: 960px) {
  .sec-pricing-ceiling .ce-head {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'eyebrow' 'title' 'sub';
    row-gap: var(--s24);
  }
  .sec-pricing-ceiling .ce-sub { max-width: 460px; padding-bottom: 0; }
}

@media (max-width: 780px) {
  .sec-pricing-ceiling .ce-stage--wide { display: none; }
  .sec-pricing-ceiling .ce-stage--tall { display: block; }
  /* Portrait carries its own strip of fog above the plate for the escaping
     branch, so the figure's own top margin is trimmed — the two together
     should not stack into a band of blank with one line in the corner of it. */
  .sec-pricing-ceiling .ce-fig { margin-top: var(--s32); }
  .sec-pricing-ceiling .ce-note { margin-top: var(--s20); }
}

@media (max-width: 480px) {
  .sec-pricing-ceiling .ce-key { font-size: 13px; }
  .sec-pricing-ceiling .ce-key--pivot { font-size: 12px; }
  .sec-pricing-ceiling .ce-note { font-size: 12px; }
}

/* ===== section:pricing-lockin ===== */
/* ============================================================================
   pricing-lockin — the contract comparison. Everyone else needs two years.

   Six decisions, each one load-bearing:

   1. THE SECTION CARRIES ITS OWN GRAIN. The page grain is `body::after` with
      position:fixed, so it paints exactly one viewport and stops. Measured in
      the left gutter of this band: 248.4 with live per-pixel variance down to
      y=896, then a dead-flat 253.0 with ZERO variance to the section's end —
      a hard horizontal seam with the footnote and the whole pull quote sitting
      below it on textureless plate. The fix is the per-section absolute grain
      layer ai-pipeline, home-ai, home-traction and customers-more already
      carry; this section never got it. Same tile, same 180px, same 0.11
      multiply, so the tooth is continuous however tall the band is.

   2. THE TERM IS A LENGTH, NOT A WORD, AND IT IS MEASURED ON A SHARED AXIS.
      Every row carries the same 24-month track; the ink rule drawn on it is
      that vendor's term, scaled by one shared per-month unit (--lk-mo). So 24
      months measures precisely twice 12 at every width and nothing is nudged
      up to a legible minimum.

   3. ONE ENCODING FOR NOT-INK. The grey dashed scale — one dash per month —
      means months nobody has committed, and it is the ONLY grey mark on the
      axis. The stack's "12–24 months each" used to be a solid GREY rule from
      12 to 24, a second grey language for a different idea on the same 262px
      axis; it is now an INK rule dashed on the month pitch. Solid ink is
      certain term, dashed ink is a range, grey is scale. Three marks, one
      geometry, and the reader never has to guess which grey means what.

   4. THE BOX IS SHORT AND THE PLANK LEAVES THE PAGE. The panel used to be a
      dead-centred full-measure rectangle with equal 120px margins holding all
      five vendors — nothing cropped, nothing overlapped, and the section's
      entire argument was the fifth item in a list at the same size as the four
      above it. Now the box is sized by its own four tracks (it ends ~120px
      short of the measure), holds the four incumbents only, and Link Prism is
      a paper plank that OVERLAPS the box's bottom edge and runs off the right
      edge of the viewport. Its cells keep the box's first three tracks, so the
      comparison still lines up; only the term track is unbounded.

   5. "NONE" IS THE FOCAL POINT. Set in the serif at display scale — ~2.5x the
      figures in the box — over a bare month scale at the shared pitch that is
      cut by the screen edge rather than stopping at 24. That is the one place
      in the section where size is used as an argument, and it is honest: the
      pitch is the same unit the ink rules are drawn from, so a longer empty
      axis means exactly what it looks like.

   6. THE HEAD SPANS, IT DOES NOT STACK. Every other header on this site sets
      the eyebrow over the title on one rail. Here the tag is thrown to the far
      right of the 1200 measure behind a hairline lead-in while the headline
      holds the left at 880. The foot then flips: footnote left, pull right.

   7. NARROW SCREENS GET THE PAYLOAD, NOT A GESTURE. The old build put the
      Contract column — the only reason this section exists — entirely
      off-screen at rest under 640 behind a horizontal scroll. The fade ramp
      and the frozen vendor column were both correctly built and both solved
      the wrong problem. Under 760 the table reflows to two columns: the vendor
      block (name, price, what) on the left and Contract on the right, so
      "None" is on screen at rest with no swipe. The scroll machinery, its mask
      and its sticky column are deleted.
   ========================================================================== */

.sec-pricing-lockin {
  --lk-pad: 40px;                                                  /* plate inset */
  --lk-gap: 28px;                                                  /* column air  */
  --lk-mo: clamp(6px, 0.76vw, 11px);                               /* 1 month     */

  /* FIXED TRACKS, NOT FRACTIONS — and that is structural, not stylistic. The
     box and the plank are different widths (the plank bleeds to the viewport),
     so any `fr` in the first three tracks would resolve differently in each and
     the plank's cells would drift out from under the box's columns. Everything
     below is derived from the VIEWPORT rather than from a container, so both
     grids compute the identical number at every width. Only the plank's term
     track is free, and it is the one that is meant to run on.

     The box is therefore sized top-down: restate the page rail in vw, subtract
     a right void that opens as the window widens, take the chart's own width
     off the front (the axis is a measurement, so it is never the thing that
     gets squeezed), and split what is left in fixed proportions. */
  --lk-wrap: min(var(--page), calc(100vw - var(--gutter) * 2));
  /* The floor is a SCROLLBAR GUARD, not taste. --lk-wrap is stated in vw and vw
     includes the scrollbar, so on a real page it over-reads the rail by the
     scrollbar's width; with a void of 0 the tracks would then sum to more than
     the plate they sit on and the last one — the axis — would be clipped. 20px
     is wider than any classic scrollbar, so the box can never outgrow its rail. */
  --lk-void: clamp(20px, calc(50vw - 460px), 120px);
  --lk-w: calc(var(--lk-wrap) - var(--lk-void));     /* box width, borders in */
  --lk-c4: calc(var(--lk-mo) * 24 + var(--lk-pad));  /* 24 months + right inset */
  --lk-rest: calc(var(--lk-w) - 2px - var(--lk-c4));
  --lk-c1: calc(var(--lk-rest) * 0.375);         /* vendor  (includes --lk-pad) */
  --lk-c2: calc(var(--lk-rest) * 0.310);         /* monthly                     */
  --lk-c3: calc(var(--lk-rest) * 0.315);         /* what you get                */
  --lk-cols: var(--lk-c1) var(--lk-c2) var(--lk-c3) var(--lk-c4);

  /* THE PLANK'S LEFT OVERHANG, DERIVED, NOT TYPED. The plank has to cross the
     box's left border for the layering to read at all — a plank that starts on
     the same rail as the box it lies over hides the crossing behind itself. It
     is clamped to whatever slack exists between the measure and the page
     gutter, so it reaches 40px at 1440 and falls to 0 the moment the rail meets
     the gutter. It can never push the plank off the left edge. */
  --lk-out: clamp(0px, calc(50vw - var(--page) / 2 - var(--gutter)), 40px);

  --lk-line: color-mix(in srgb, var(--ink) 9%, var(--mist));
  --lk-axis: color-mix(in srgb, var(--ink) 15%, transparent);
}

/* ── The grain, restated for this band ───────────────────────────────────── */
.sec-pricing-lockin::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  opacity: 0.11;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.91' numOctaves='2' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='0 1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

/* ── Head ────────────────────────────────────────────────────────────────── */

.sec-pricing-lockin .lk-head {
  display: grid;
  grid-template-columns: minmax(0, 880px) minmax(0, 1fr);
  column-gap: 40px;
  align-items: start;
}

/* BOTH children are pinned to row 1, explicitly. The tag comes first in the
   DOM (it reads first), so sparse auto-placement put it in column 2 of row 1
   and then pushed the title down to row 2 — a stack wearing a span's CSS. */
.sec-pricing-lockin .lk-title { grid-column: 1; grid-row: 1; }

/* The tag is a marker, not a label: a 44px hairline runs into it so it reads
   as pinned to the right edge of the measure rather than adrift in the void.

   The offset puts the tag's baseline ON the headline's first baseline and it is
   derived rather than typed: Source Serif's first baseline sits 0.875 x
   font-size below the line-box top at our 1.1 leading, and the 14px sans tag
   carries ~15px of its own. So the pair stays aligned at 64px and at 48px
   without a second number. */
.sec-pricing-lockin .lk-tag {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  margin-top: calc(var(--t-h-lg) * 0.875 - 15px);
  margin-bottom: 0;
  white-space: nowrap;
}
.sec-pricing-lockin .lk-tag i {
  display: block;
  width: 44px;
  height: 1px;
  background: var(--hair-strong);
}

/* ── The box ─────────────────────────────────────────────────────────────── */

.sec-pricing-lockin .lk-panel {
  position: relative;
  z-index: 1;
  width: min(100%, var(--lk-w));    /* sized by its own tracks, not by the rail */
  margin-top: 72px;
  padding-bottom: 66px;             /* the apron the plank lands on */
  background: var(--mist);
  border: 1px solid var(--lk-line);
  border-radius: var(--r-card);
  overflow: hidden;
}

/* Kept only as a safety net: at every width the four tracks are sized to fit
   inside the box, so this never engages. It exists so that a font fallback that
   sets wider than Inter can never make a figure unreachable. */
.sec-pricing-lockin .lk-scroll { overflow-x: auto; overflow-y: hidden; }

.sec-pricing-lockin .lk-grid { width: 100%; }

.sec-pricing-lockin .lk-row {
  display: grid;
  grid-template-columns: var(--lk-cols);
  align-items: baseline;
  border-bottom: 1px solid var(--lk-line);
}
.sec-pricing-lockin .lk-row:last-child { border-bottom: 0; }

.sec-pricing-lockin .lk-c {
  padding-block: 26px;
  padding-right: var(--lk-gap);
  font-size: 16px;
  line-height: 1.3;
  color: var(--slate);
}

.sec-pricing-lockin .lk-c--name {
  padding-left: var(--lk-pad);
  font-size: 19px;
  font-weight: 450;
  color: var(--ink);
}

/* "six-vendor" is held whole: left to itself the narrow column breaks INSIDE
   the hyphenated word ("The six-" / "vendor stack"), which reads as a typo. */
.sec-pricing-lockin .lk-nb { white-space: nowrap; }

.sec-pricing-lockin .lk-c--fig {
  font-size: 19px;
  font-weight: 450;
  color: var(--ink);
}

.sec-pricing-lockin .lk-c--what { font-size: 16px; color: var(--slate); }

/* The term track is exactly 24 months of axis plus the plate's right inset, so
   the axis ends where the box's padding begins and the scale's right end is the
   same distance from the border in every row. */
.sec-pricing-lockin .lk-c--term { padding-right: var(--lk-pad); }

/* Column labels sit back — the table's argument is in the rows. */
.sec-pricing-lockin .lk-row--head .lk-c {
  padding-block: 22px 13px;
  font-size: 13px;
  font-weight: 400;
  color: var(--ash);
}

/* ── Terms ───────────────────────────────────────────────────────────────── */

.sec-pricing-lockin .lk-term {
  display: block;
  font-size: 16px;
  font-weight: 450;
  color: var(--ink);
}

/* The slot is a fixed 24-month width in every row and it PAINTS that width as a
   scale: one hairline dash per month, twenty-four of them, on the same --lk-mo
   unit the solid rules are drawn from. Dashes can never be mistaken for a bar,
   which is why the scale is the one thing allowed to be grey. */
.sec-pricing-lockin .lk-bar {
  display: block;
  position: relative;
  width: calc(var(--lk-mo) * 24);
  height: 2px;
  margin-top: 16px;
  background-image: repeating-linear-gradient(to right,
    var(--lk-axis) 0 calc(var(--lk-mo) * 0.34),
    transparent calc(var(--lk-mo) * 0.34) var(--lk-mo));
  background-size: 100% 1px;
  background-position: left center;
  background-repeat: no-repeat;
}
.sec-pricing-lockin .lk-seg {
  position: absolute;
  top: 0;
  left: calc(var(--o, 0) * var(--lk-mo));
  width: calc(var(--m) * var(--lk-mo));
  height: 2px;
  border-radius: var(--r-pill);
  background: var(--ink);
  transform-origin: left center;
}
/* The range: ink, because those months are real term for some of the six —
   dashed on the month pitch, because they are not term for all of them. The
   segment's own origin IS month 12, so the dashes land on month boundaries. */
.sec-pricing-lockin .lk-seg--range {
  border-radius: 0;
  background: repeating-linear-gradient(to right,
    var(--ink) 0 calc(var(--lk-mo) * 0.58),
    transparent calc(var(--lk-mo) * 0.58) var(--lk-mo));
}

.sec-pricing-lockin .lk-dag {
  font-size: 0.6em;
  line-height: 0;
  vertical-align: super;
  color: var(--ash);
  margin-left: 1px;
}

/* ── The plank ───────────────────────────────────────────────────────────── */

/* `margin-right: 50% - 50vw` inside the page rail is the full-bleed-right move:
   the box grows by exactly the distance from the measure's right edge to the
   viewport's. It stays a child of .wrap so it inherits the LEFT rail for free —
   which is the whole reason its columns can line up with the box above. */
.sec-pricing-lockin .lk-plank {
  position: relative;
  z-index: 2;
  margin-top: -46px;                      /* lies over the box's bottom apron */
  margin-left: calc(var(--lk-out) * -1);  /* …and over its left border        */
  margin-right: calc(50% - 50vw);
  background: var(--paper);
  border: 1px solid var(--hair-strong);
  border-right: 0;
  border-radius: var(--r-card) 0 0 var(--r-card);
}

/* The first track absorbs the overhang and the name cell's padding absorbs it
   again, so the plank can start 40px left of the box while every one of its
   cells still stands on the box's own column rails. */
.sec-pricing-lockin .lk-plank__in {
  display: grid;
  grid-template-columns: calc(var(--lk-c1) + var(--lk-out)) var(--lk-c2) var(--lk-c3) minmax(0, 1fr);
  align-items: baseline;
  padding-block: 44px;
}

.sec-pricing-lockin .lk-p-name,
.sec-pricing-lockin .lk-p-fig,
.sec-pricing-lockin .lk-p-what,
.sec-pricing-lockin .lk-p-term { padding-right: var(--lk-gap); }

.sec-pricing-lockin .lk-p-name {
  padding-left: calc(var(--lk-pad) + var(--lk-out));
  font-size: 21px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--ink);
}
.sec-pricing-lockin .lk-p-fig {
  font-size: 21px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--ink);
}
.sec-pricing-lockin .lk-p-what {
  font-size: 17px;
  font-weight: 450;
  line-height: 1.3;
  color: var(--slate);
}
.sec-pricing-lockin .lk-p-term { padding-right: 0; }

/* The argument, at the only size in the section that is allowed to argue. */
.sec-pricing-lockin .lk-none {
  display: block;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(38px, 3.9vw, 56px);
  line-height: 1;
  letter-spacing: var(--tr-h);
  color: var(--ink);
  /* The same sidebearing correction 10-base.css makes for every display serif
     on the site: the N's stem sits inside the glyph's left bearing, so flush to
     the term rail it reads a few pixels indented against the sans "24 months"
     directly above it. Stated in em so it re-derives at every step. */
  margin-left: -0.028em;
}

/* Same pitch, same tile, no width limit: the scale simply keeps going and the
   viewport cuts it. Nothing is ever laid on it. */
.sec-pricing-lockin .lk-bar--open {
  width: auto;
  margin-top: 22px;
}

/* ── Foot ────────────────────────────────────────────────────────────────── */

.sec-pricing-lockin .lk-foot {
  margin-top: 72px;
  display: grid;
  grid-template-columns: minmax(0, 260px) minmax(0, 680px);
  justify-content: space-between;
  column-gap: 48px;
  align-items: start;
}
.sec-pricing-lockin .lk-note {
  margin-top: 8px;                  /* optical: onto the pull's first baseline */
  font-size: 14px;
  line-height: 1.5;
  color: var(--ash);
}
.sec-pricing-lockin .lk-pull {
  color: var(--ink);
  text-wrap: pretty;
}

/* ── Narrower ────────────────────────────────────────────────────────────── */

@media (max-width: 1080px) {
  .sec-pricing-lockin { --lk-pad: 30px; --lk-gap: 22px; }
  .sec-pricing-lockin .lk-c { padding-block: 24px; }
  .sec-pricing-lockin .lk-c--name,
  .sec-pricing-lockin .lk-c--fig { font-size: 17px; }
  .sec-pricing-lockin .lk-c--what,
  .sec-pricing-lockin .lk-term { font-size: 15px; }
  .sec-pricing-lockin .lk-panel { margin-top: 60px; padding-bottom: 56px; }
  .sec-pricing-lockin .lk-plank { margin-top: -40px; }
  .sec-pricing-lockin .lk-plank__in { padding-block: 38px; }
  .sec-pricing-lockin .lk-p-name,
  .sec-pricing-lockin .lk-p-fig { font-size: 19px; }
  .sec-pricing-lockin .lk-p-what { font-size: 16px; }
  .sec-pricing-lockin .lk-foot {
    margin-top: 60px;
    grid-template-columns: minmax(0, 220px) minmax(0, 600px);
  }
}

@media (max-width: 900px) {
  .sec-pricing-lockin .lk-head { grid-template-columns: minmax(0, 1fr); row-gap: 20px; }
  .sec-pricing-lockin .lk-tag { grid-column: 1; grid-row: 1; justify-self: start; margin-top: 0; }
  .sec-pricing-lockin .lk-title { grid-column: 1; grid-row: 2; }
  .sec-pricing-lockin .lk-foot {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 30px;
  }
  .sec-pricing-lockin .lk-note { margin-top: 0; }
}

/*
 * UNDER 760: TWO COLUMNS, AND CONTRACT IS ONE OF THEM.
 *
 * Four tracks stop fitting a phone long before the type does, and the column
 * that gets pushed off the edge is always the last one — which here is the only
 * column the section exists to show. So the vendor, the price and the blurb
 * stack into one block on the left and Contract takes the right at full height.
 * Nothing scrolls sideways, nothing is masked, nothing sticks: the reader sees
 * "None" over a bare month scale at rest.
 *
 * The plank still bleeds, so its term track is widened by exactly the bleed
 * (one gutter) plus the box's border, which puts its scale's left end on the
 * same rail as the scales above it while still running off the screen.
 */
@media (max-width: 760px) {
  .sec-pricing-lockin {
    --lk-pad: 20px;
    --lk-gap: 14px;
    /* No left overhang on a phone: the rail IS the gutter, so there is nothing
       to overhang into, and the plank's job here is simply to be wider than the
       box and to carry the axis off the right edge. */
    --lk-out: 0px;
    /* A steeper slope than the desktop unit, and deliberately so: with only two
       columns the axis is competing with one block of text rather than three,
       so at 760 it should take about half the plate and at 390 about a third.
       A plain vw ratio cannot do both — this one is anchored at each end. */
    --lk-mo: clamp(4.6px, calc(3.2vw - 8px), 15px);
    --lk-c4: calc(var(--lk-mo) * 24 + var(--lk-pad));
    --lk-cols: minmax(0, 1fr) var(--lk-c4);
    --lk-w: 100%;
  }

  .sec-pricing-lockin .lk-row {
    align-items: start;
    padding-block: 18px;
    row-gap: 4px;
    grid-template-areas:
      'name term'
      'fig  term'
      'what term';
  }
  .sec-pricing-lockin .lk-c { padding-block: 0; padding-right: var(--lk-gap); }
  .sec-pricing-lockin .lk-c--name { grid-area: name; font-size: 17px; }
  .sec-pricing-lockin .lk-c--fig  { grid-area: fig;  font-size: 17px; padding-left: var(--lk-pad); }
  .sec-pricing-lockin .lk-c--what { grid-area: what; font-size: 14px; padding-left: var(--lk-pad); }
  /* Restated after the .lk-c reset above, which is equal-specificity and later
     in the file: the axis's right end must sit on the plate's inset, not on the
     column gap, or the scale stops short of where every row's border says it
     should. */
  .sec-pricing-lockin .lk-c--term { grid-area: term; padding-right: var(--lk-pad); }
  .sec-pricing-lockin .lk-term { font-size: 14px; }
  .sec-pricing-lockin .lk-bar { margin-top: 11px; }

  /* The head row keeps one label. "Monthly" and "What you get" were labelling
     columns that no longer exist, and an HK$ figure has never needed telling. */
  .sec-pricing-lockin .lk-row--head { padding-block: 15px 11px; grid-template-areas: 'name term'; }
  .sec-pricing-lockin .lk-row--head .lk-c { padding-block: 0; font-size: 12px; }
  .sec-pricing-lockin .lk-row--head .lk-c--fig,
  .sec-pricing-lockin .lk-row--head .lk-c--what { display: none; }

  .sec-pricing-lockin .lk-panel { margin-top: 44px; padding-bottom: 42px; }

  .sec-pricing-lockin .lk-plank { margin-top: -28px; }
  .sec-pricing-lockin .lk-plank__in {
    padding-block: 30px;
    row-gap: 4px;
    grid-template-columns: minmax(0, 1fr) calc(var(--lk-c4) + var(--gutter) + 1px);
    grid-template-areas:
      'name term'
      'fig  term'
      'what term';
    align-items: start;
  }
  .sec-pricing-lockin .lk-p-name { grid-area: name; font-size: 18px; }
  .sec-pricing-lockin .lk-p-fig  { grid-area: fig;  font-size: 18px; padding-left: var(--lk-pad); }
  .sec-pricing-lockin .lk-p-what { grid-area: what; font-size: 14px; padding-left: var(--lk-pad); }
  .sec-pricing-lockin .lk-p-term { grid-area: term; }
  .sec-pricing-lockin .lk-bar--open { margin-top: 14px; }

  .sec-pricing-lockin .lk-foot { margin-top: 44px; }
}

/* ===== section:pricing-terms ===== */
/* ===================================================================
   pricing-terms — "The terms, in full."

   THE FAILURE THIS REPLACES, twice over.

   Build 1 was four identical bordered cards at identical width, hairline,
   gap and type size. That is an even card grid stood on its end — the exact
   register this page spends two sections arguing against.

   Build 2 broke the cards up into a hairline rail, which was right, but set
   every clause as one 20px run-on line: term in ink, detail in slate, on the
   same size and the same baseline. Three lines of body copy beside three
   small circles is a checklist, and a checklist has no rank in it. It also
   left the section with nothing bigger than its own lede, so the argument
   ("here are the terms, before you call") arrived in the quietest voice on
   the page.

   Build 3 fixed the ladder's bottom and broke its top: it set the H2 and the
   total at the SAME rank (.h, 44px, a 30px cap on both). A section whose two
   biggest things are identical has no biggest thing, so its visual centre
   fell through to the three 26px Inter rows with a circle and a tick beside
   each — the most generic pattern on the commercial web — and the diagonal it
   claimed had two equal ends and read as an L.

   This build states the ladder outright — 64 / 26 / 44, ALL SERIF, over 16px
   slate — and lets the section have a focal point:

     · the H2 at .h-lg, the rank pricing-hero, pricing-ceiling and
       pricing-lockin already use on this same page. It is now the largest ink
       in the band by 20px and the composition resolves around it;
     · three NEGATIONS, each a 26px serif title over a single quiet line,
       struck down a hairline rail by a hand-drawn seal. Serif because .h-sm
       put Inter 450 between two serif lines and made the middle of the
       section read in a second voice — the checklist voice;
     · one POSITIVE — "Your data is yours." is the only sentence here that
       says what you get rather than what you are not charged — set as the
       ledger's TOTAL: past the rail's left edge, under a closing rule that
       leaves the page, kept at .h so it answers the H2 instead of shouting
       back at it, and stamped by a seal at nearly twice the size.

   Deleted rather than tuned, and staying deleted: the card boxes, the fixed
   term column (a 59–115px hole in the middle of every line, because "No
   contract." and "No charge per till." are not the same width and never
   will be), the spine's two fading ends, the sans in the clause titles, and
   the closing rule's right terminal.

   Every rule is scoped under .sec-pricing-terms.
   =================================================================== */

.sec-pricing-terms {
  /* THE GROUND IS WARM, NOT --fog. --fog samples (250,250,251): blue by a
     point, which is the palette of a generic SaaS feature list and the one
     thing the grain cannot rescue. This is paper stock — the same
     construction pricing-calc uses for its plate, tuned to a cream, so the
     page's one warm surface returns as the band that carries the terms. It
     is NOT the rationed peach card; --wash-2 is a ground tint token and is
     never used here as text, as a card or at card strength. */
  --pt-ground: color-mix(in srgb, var(--wash-2) 20%, var(--paper));

  /* One seal size for the three clauses; the ring is 13/20 of the box. */
  --pt-seal: 44px;
  /* The total's seal. Its viewBox scales with it (40 -> 71 units) so the
     apparent hairline stays identical across the family: 44/40 = 1.10 and
     78/71 = 1.10, so one 1.2 weight lands on the same fraction of a pixel
     for all four marks. */
  --pt-seal-total: 78px;
  /* Text inset from the rail. The ring's outer edge stands at 14.3px; this
     leaves ~46px of paper between the mark and the first stem. */
  --pt-inset: 60px;
  /* How far the closing rule and the total hang past the rail.
     IT IS 110, NOT 56, AND THE SEAL IS WHY. The total's disc occludes 0.78 x
     78 = 61px of rule, 30.4px of it left of the spine, so a 56px hang left a
     25px stub of rule standing outside the seal — a fragment, not a hang. At
     110 there is ~80px of unbroken rule to the seal's left and the total's
     text sets flush on the rule's own left terminal. */
  --pt-hang: 110px;
  /* Cap-line of a .h-sm-serif inside its own line box, and the cap CENTRE,
     which is where the seal centres. DERIVED FROM THE TOKEN, NOT MEASURED AT
     ONE SIZE: Source Serif 4 carries ascent+descent 1.375em and cap 0.67em, so
     at line-height 1.28 the cap top sits (1.28 - 1.375)/2 + 0.368 = 0.3205em
     below the block top and its centre a further half-cap down. Stating the
     ratio means the seal re-registers itself at every step of the ramp instead
     of needing a pixel pair per breakpoint — which is what the 26px pair used
     to be, and it went 1px out the moment --t-h-sm stepped to 22. */
  --pt-cap: calc(var(--t-h-sm) * 0.3205);
  --pt-mid: calc(var(--t-h-sm) * 0.6555);
  /* The drop from the last clause to the closing rule. Stated ONCE because
     two things must agree on it: the total's margin and the end of the
     spine. When they were separate numbers the spine over-ran the rule. */
  --pt-drop: var(--s64);
  /* Air under the closing rule before the total's cap. Must clear the half
     of the seal that hangs below the junction (39px) with room to spare. */
  --pt-lift: 52px;

  background: var(--pt-ground);
  /* The closing rule runs off the right of the page. `overflow-x: clip` on the
     ROOT contains it without a scrollbar, but a right overhang still counts
     toward document scrollWidth there — a 1440 capture came out 1490 wide —
     so the band clips its own bleed. X only: the y axis must stay visible or
     the reveal transforms get guillotined at the band edge. */
  overflow-x: clip;
  overflow-y: visible;
}

/* ——— The two columns ————————————————————————————————
   Not 1/2 and not 1/3: neither column is the "main" one by arithmetic.
   align-items:start is what leaves the bottom-left as paper — the head is
   ~250px tall against a ~520px ledger. */
.sec-pricing-terms .pt-grid {
  display: grid;
  grid-template-columns: minmax(0, 30fr) minmax(0, 52fr);
  column-gap: var(--s96);
  align-items: start;
}

/* ——— The argument ————————————————————————————————— */
.sec-pricing-terms .pt-sub {
  margin-top: var(--s20);
  max-width: 32ch;
}

/* ——— The ledger ————————————————————————————————————
   RIGHT-ALIGNED TO THE PAGE RAIL, not left-aligned to its column. The
   measure is fixed, so what floats is the ledger's LEFT edge: the H2 holds
   the page's left rail, the clauses hold its right one, and the void
   between the lede and the spine is the widest gap in the section rather
   than whatever the grid happened to leave over. */
.sec-pricing-terms .pt-rail {
  max-width: 540px;
  margin-left: auto;
}

/* OPTICAL REGISTRATION ACROSS THE COLUMNS, DERIVED AT EVERY WIDTH.

   The H2's cap sits 0.2305em below its block top (.h-lg, line-height 1.1) and
   the first clause's 0.3205em below its own (.h-sm-serif, 1.28); the list's
   padding is the difference, so the two columns — and the top of the spine —
   start on the same line of ink. At 1440 that is 6.4px.

   It is a calc, not a number, because the two ranks step on DIFFERENT
   breakpoints: --t-h-lg drops to 48 at 1080 while --t-h-sm holds 26 until 720.
   A single frozen figure was correct at 1440 and 3px out at 1024, which is
   exactly the width where both columns are still standing and the misalignment
   is visible. */
.sec-pricing-terms .pt-list {
  position: relative;
  list-style: none;
  margin: 0;
  padding: calc(var(--t-h-lg) * 0.2305 - var(--t-h-sm) * 0.3205) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--s48);
}

/* The spine. It begins on the cap-line and ends ON the closing rule — a
   T-junction, not a fade. A rule that fades out over nothing has an end
   that is nowhere; this one meets ink at both terminals, and its lower
   terminal is derived from the same token as the drop it crosses so the two
   cannot drift apart. */
.sec-pricing-terms .pt-list::before {
  content: '';
  position: absolute;
  left: 0;
  top: var(--pt-cap);
  bottom: calc(var(--pt-drop) * -1);
  width: 1px;
  background: var(--hair-strong);
  pointer-events: none;
}

/* ——— One clause ——————————————————————————————————
   A 26px SERIF title (.h-sm-serif) over ONE quiet line of 16px sans. The
   measure is set by the rail, so all three wrap — or, as it happens, do not
   wrap — at the same place, and the detail can never be mistaken for the
   term.

   The title used to be .h-sm, the sans rank at the same 26px. Same size, but
   it put Inter 450 in the middle of a section whose top and bottom lines are
   both serif, which is how three clauses on a rail came out reading as a
   stock SaaS feature list with a serif headline bolted above it. One face,
   three ranks: the section now has a single typographic voice, and the sans
   is left to do only what it is for here — the quiet 16px detail line. */
.sec-pricing-terms .pt-term {
  position: relative;
  padding-left: var(--pt-inset);
}
.sec-pricing-terms .pt-term__b { margin-top: 6px; }

/* ——— The total ————————————————————————————————————
   The one clause that is not a negation. It breaks the rail's left edge, it
   is set in the serif at .h — one rank UNDER the H2, so the pair reads as a
   loud question top-left and a quieter answer bottom-right rather than as two
   equal ends of an L — and its seal is the largest mark in the section.

   THE RULE ABOVE IT IS THE ONLY THING IN THIS COMPOSITION THAT CROSSES A
   BOUNDARY. It used to be .pt-total's border-top, which meant it began and
   ended on the box: its right terminal landed on the wrap edge at x=1319, one
   pixel shy of the page rail, so the section's last mark was a line that
   stopped politely inside the measure. It is now a pseudo-element that starts
   on the total's own left edge and runs off the right of the page.

   -50vw, not a computed bleed: at =940 the rail is 620px inside a wider wrap,
   so the distance from this box to the screen edge is not the page margin and
   any formula in terms of --page is wrong there. An overshoot clipped by the
   band is right at every width. */
.sec-pricing-terms .pt-total {
  position: relative;
  margin: var(--pt-drop) 0 0 calc(var(--pt-hang) * -1);
  width: calc(100% + var(--pt-hang));
  padding-top: var(--pt-lift);
}
.sec-pricing-terms .pt-total::before {
  content: '';
  position: absolute;
  left: 0;
  right: -50vw;
  top: 0;
  height: 1px;
  background: var(--hair-strong);
  pointer-events: none;
}
/* The total's line is capped just wide enough to set in ONE line, like the
   three clauses above it. At 34ch it broke as "…including the day / you
   leave.", which put a two-word rag under the only serif line in the column
   and made the conclusion look like a paragraph. */
.sec-pricing-terms .pt-total__b {
  margin-top: 14px;
  max-width: 560px;
}

/* ——— The seal ————————————————————————————————————
   Centred on the rail, not on a card edge: half the mark stands on paper to
   the left of the spine.

   HIDDEN-LINE REMOVAL is an opaque disc in the section's own ground, so the
   rail (and, for the total, the closing rule) BREAKS at the seal instead of
   drawing through it. The disc is 78% of the mark and the ring is 65%, so
   the occluder clears the ring by under three pixels. A disc the full size
   of the mark left a crescent of a different white standing behind every
   seal; sized to the ring, it disappears. */
.sec-pricing-terms .pt-mark {
  position: absolute;
  left: 0;
  top: var(--pt-mid);
  transform: translate(-50%, -50%);
  width: var(--pt-seal);
  height: var(--pt-seal);
  display: grid;
  place-items: center;
}
.sec-pricing-terms .pt-mark::before {
  content: '';
  position: absolute;
  width: 78%;
  height: 78%;
  border-radius: var(--r-pill);
  background: var(--pt-ground);
}

/* The total's seal is pinned to the crossing itself — the point where the
   spine meets the closing rule — so one disc breaks BOTH lines at once. */
.sec-pricing-terms .pt-mark--total {
  left: var(--pt-hang);
  top: 0;
  width: var(--pt-seal-total);
  height: var(--pt-seal-total);
}

/*
 * THE MARKS ARE UI, NOT DRAWINGS, so they do not carry `.illo`. A ring and a
 * tick drawn straight-on has no isometric axes and no visible faces; under
 * the drawing class it sat in the same set as a floor plan sixteen times its
 * size, which is what emptied that class of meaning. The weight is therefore
 * stated here, and stated so the family matches: both frames render at 1.10x,
 * so one 1.2 weight lands on 1.32 CSS px for all four marks — the same
 * hairline the drawings carry, arrived at rather than borrowed.
 */
.sec-pricing-terms .pt-seal {
  position: relative;
  width: 100%;
  height: 100%;
  color: var(--ink);
  opacity: 0.78;
  overflow: visible;
}
.sec-pricing-terms .pt-seal path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Struck, not placed. A degree or three of tilt on each — enough that the
   four are demonstrably four strokes, small enough that none of them reads
   as a misalignment against the rail they sit on. */
.sec-pricing-terms .pt-term:nth-child(1) .pt-seal { transform: rotate(-3deg); }
.sec-pricing-terms .pt-term:nth-child(2) .pt-seal { transform: rotate(2deg); }
.sec-pricing-terms .pt-term:nth-child(3) .pt-seal { transform: rotate(-1.5deg); }
.sec-pricing-terms .pt-total .pt-seal { transform: rotate(3.5deg); }

/* ——— 940 and under: one column ————————————————————
   The fold is at 940, not at 1100. At 1024 the two columns still measure 296
   and 512, which the head and the ledger both hold comfortably; folding them
   there put the whole section in the left half of a 968px page and left an
   unstructured 340px of nothing down the right — void as a leftover rather
   than as a decision. 940 is also where the nav drops its links, so the page
   changes shape once instead of twice.

   Folded, the ledger keeps its device instead of losing it. Indenting it by
   half a seal is what buys the room: the ring's outer edge then lands on the
   page measure, so no mark half-hangs into a gutter. The total still breaks
   out — it just breaks out to the page rail rather than into a column gap
   that no longer exists.

   THE HANG COMES BACK DOWN HERE, and it has to. 110px of hang on a phone would
   put the total's own text outside the page. At half a seal the rule's left
   terminal lands under the total seal's disc, so instead of a stub the rule
   simply emerges from the mark and runs off the right of the screen: the same
   one-terminal reading the desktop gets, arrived at from the other side. */
@media (max-width: 940px) {
  .sec-pricing-terms { --pt-hang: calc(var(--pt-seal) / 2); }
  .sec-pricing-terms .pt-grid {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--s64);
  }
  .sec-pricing-terms .pt-sub { max-width: 46ch; }
  .sec-pricing-terms .pt-rail {
    max-width: 620px;
    margin-left: calc(var(--pt-seal) / 2);
  }
}

/* ——— 700 and under ————————————————————————————————
   Genuinely redrawn rather than squashed: the seals come down a size, the
   text inset comes in with them, the drop and the lift come down one step
   each, and the type ramp does the rest.

   THE PHONE LADDER IS THE POINT OF THE RANK CHANGE. When the H2 and the total
   both wore .h, the three ranks measured 19 / 16 / 19 CSS px of cap here — a
   1.19x spread with the top and bottom TIED, which is not a hierarchy, it is
   three sizes of the same size. With .h-lg on the H2 the tokens hand back
   34 / 22 / 28 px, i.e. 22.8 / 14.7 / 18.8 of cap: a 1.55x spread, no tie, and
   the phone keeps the same reading order as the desktop. No size is named
   here; the ramp already had the ranks, the section was just asking for the
   wrong one. */
@media (max-width: 700px) {
  .sec-pricing-terms {
    --pt-seal: 34px;
    --pt-seal-total: 60px;
    --pt-inset: 46px;
    /* --pt-cap and --pt-mid are NOT restated: they are ratios of --t-h-sm,
       which the ramp has already stepped 26 -> 22 by here, so the seal follows
       the type down on its own. */
    --pt-drop: var(--s48);
    --pt-lift: 40px;
  }
  .sec-pricing-terms .pt-list { gap: var(--s40); }
  .sec-pricing-terms .pt-total__b { max-width: none; }
}

/* ===== section:pricing-metered ===== */
/* ————————————————————————————————————————————————
   pricing-metered — what is billed outside the fee.

   The honesty band, and the last content band on the pricing page. Everything
   here is a hairline or a sentence: no fill, no card, no panel, no tint, and —
   as of this pass — no drawing. The section above it (pricing-terms) is fog
   with a vertical spine and four struck seals, so this one has to be the
   quieter of the pair; a 300px isometric printer hung off the first rule was
   the opposite of that, and deleting it is the whole of this revision.

   What carries the section instead is one hairline that crosses the ENTIRE
   viewport, broken only where the columns are, with a serif masthead sitting on
   it. The band closes on the loose rhythm because it is the page's last word.
   ———————————————————————————————————————————————— */

/*
 * THE BLEED, STATED ONCE, AND NOW USED AT BOTH ENDS.
 *
 * The distance from the edge of the viewport to the edge of .wrap: half the
 * leftover when the page rail binds, the gutter when it does not. The first
 * rule leaves the page by exactly this on the left and the last by exactly this
 * on the right, so the line lands ON the screen edge at every width rather than
 * on a round number that overshoots at one of them.
 */
.sec-pricing-metered {
  background: var(--paper);
  padding-block: var(--section-y-loose);
  --pm-bleed: max(var(--gutter), (100vw - var(--page)) / 2);
}

/* — The masthead ————————————————————————————————
   One line, low and left, sitting directly on the ruled band the way a
   masthead sits on its column heads. No eyebrow, no lede, no buttons: this is
   the quietest band on the page and a three-part header would already be more
   apparatus than the four sentences beneath it.

   One line at every desktop width, deliberately — the section directly above
   authors a two-line break with its italic alone on line two, and repeating
   that here would make two consecutive H2s the same shape. */
.sec-pricing-metered .pm-title {
  max-width: 30ch;
  padding-bottom: var(--s48);
}

/* — The ruled band —————————————————————————————
 *
 * NOT four equal columns. Four equal anythings is a metronome, and this page
 * argues against exactly that shape elsewhere. The lead carries the section's
 * actual claim, so it is nearly twice its neighbours; the other three are peers.
 *
 * The ratio is set by the SETTING, not by taste. At 1.42 the lead's sentence
 * broke three lines and ended on a two-word widow ("by us.") while its peers
 * also broke three — the wider column bought nothing the eye could see. At 1.8
 * the lead sets its two sentences as two lines of large type against three
 * lines of small, so the block reads a different SHAPE from its neighbours and
 * not merely a wider one.
 */
.sec-pricing-metered .pm-band {
  display: grid;
  grid-template-columns: 1.8fr 1fr 1fr 1fr;
  column-gap: var(--s40);
}

.sec-pricing-metered .pm-item {
  position: relative;
  padding-top: var(--s48);
}

.sec-pricing-metered .pm-rule {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--hair-strong);
}

/*
 * THE TWO ENDS LEAVE THE PAGE, AND NOTHING ELSE DOES.
 *
 * Symmetric on purpose. The band reads as one hairline cut from a wider sheet
 * and interrupted at the gutters, which is a different thing from a rule that
 * bleeds at one end and stops 21px short at the other.
 */
.sec-pricing-metered .pm-item--lead .pm-rule { left: calc(var(--pm-bleed) * -1); }
.sec-pricing-metered .pm-item:nth-child(4) .pm-rule { right: calc(var(--pm-bleed) * -1); }

/* [data-bar] is observed per element, so without this all four rules wipe at
   once while their text staggers. The step matches the group's 90ms. */
.sec-pricing-metered .pm-item:nth-child(2) .pm-rule { transition-delay: 90ms; }
.sec-pricing-metered .pm-item:nth-child(3) .pm-rule { transition-delay: 180ms; }
.sec-pricing-metered .pm-item:nth-child(4) .pm-rule { transition-delay: 270ms; }

/*
 * ONE RANK FOR ALL FOUR TERMS — this is the baseline grid.
 *
 * The lead used to wear .card__title--lead, which put its baseline at y=417
 * against y=412 for its three siblings and its first body line at y=460
 * against y=452: a label row that visibly stepped down at its loudest end. The
 * lead's emphasis is now carried entirely by measure and by the rank of its
 * sentence, both of which cost the row nothing, so the four labels sit on one
 * line and the four first lines of copy sit on the next.
 */
.sec-pricing-metered .pm-term { color: var(--ink); margin-bottom: var(--s12); }

.sec-pricing-metered .pm-copy {
  font-size: 16px;
  line-height: 1.55;
  letter-spacing: -0.003em;
  color: var(--slate);
  text-wrap: pretty;
}
/* The lead's whole distinction, on the body rank rather than the label rank. */
.sec-pricing-metered .pm-item--lead .pm-copy {
  font-size: var(--t-body-lg);
  line-height: 1.45;
  letter-spacing: var(--tr-body-lg);
}

/* — Narrower desktop ——————————————————————————— */
@media (max-width: 1120px) {
  .sec-pricing-metered .pm-band { column-gap: var(--s32); grid-template-columns: 1.7fr 1fr 1fr 1fr; }
  .sec-pricing-metered .pm-copy { font-size: 15px; }
  .sec-pricing-metered .pm-item--lead .pm-copy { font-size: 18px; }
}

/*
 * TWO COLUMNS, AND THE BLEED FOLLOWS THE COLUMN IT IS IN.
 *
 * Four columns stop working long before the phone does — under 1000px each of
 * the three peers is below 190px and every sentence sets five ragged lines. The
 * fold keeps the rule logic intact instead of dropping it: whichever rules are
 * now on the left of the grid run off the left of the screen, whichever are on
 * the right run off the right. The band is still one sheet cut at both edges,
 * just cut into two lines of it.
 */
@media (max-width: 1000px) {
  .sec-pricing-metered .pm-band {
    grid-template-columns: 1fr 1fr;
    column-gap: var(--s40);
    row-gap: var(--s40);
  }
  .sec-pricing-metered .pm-item:nth-child(1) .pm-rule,
  .sec-pricing-metered .pm-item:nth-child(3) .pm-rule { left: calc(var(--pm-bleed) * -1); right: 0; }
  .sec-pricing-metered .pm-item:nth-child(2) .pm-rule,
  .sec-pricing-metered .pm-item:nth-child(4) .pm-rule { left: 0; right: calc(var(--pm-bleed) * -1); }

  .sec-pricing-metered .pm-copy { font-size: 16px; }
  .sec-pricing-metered .pm-item { padding-top: var(--s32); }
}

/* — Phone ————————————————————————————————————
 *
 * DESIGNED, NOT REFLOWED.
 *
 * One column means every rule is an outer rule, so on the phone ALL FOUR bleed
 * both edges and the type alone holds the gutter. That is one inset for the
 * whole stack — the previous build ran rule 1 full-bleed and rules 2–4 inset by
 * 40, which is not a rhythm, it is a leftover — and it turns the band into what
 * a phone can actually carry: four ruled ledger lines across the screen.
 *
 * The enlarged-first-block emphasis does NOT come over. In a stack there is no
 * wider measure to enlarge, so all that survives the fold is one odd-sized item
 * in a list of four; the lead's emphasis on the phone is that it is first.
 */
@media (max-width: 720px) {
  .sec-pricing-metered .pm-title {
    max-width: 13ch;
    padding-bottom: var(--s32);
  }

  .sec-pricing-metered .pm-band {
    grid-template-columns: 1fr;
    column-gap: 0;
    row-gap: 0;
  }
  .sec-pricing-metered .pm-item .pm-rule {
    left: calc(var(--pm-bleed) * -1);
    right: calc(var(--pm-bleed) * -1);
  }

  .sec-pricing-metered .pm-item { padding-top: var(--s28); padding-bottom: var(--s28); }
  .sec-pricing-metered .pm-item:last-child { padding-bottom: 0; }

  .sec-pricing-metered .pm-term { margin-bottom: var(--s8); }
  .sec-pricing-metered .pm-copy,
  .sec-pricing-metered .pm-item--lead .pm-copy {
    font-size: var(--t-body);
    line-height: 1.5;
    letter-spacing: -0.003em;
    max-width: 34ch;
  }
}

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