/* ============================================================
   Control - body (below the fold)

   Companion to hero.css, which stays HERO-ONLY. Every rule here is
   either a shared primitive (lifted verbatim from
   website-planning/design-rules.md §3.2 - the hero does not use any
   of these class names, so nothing can bleed upward) or is prefixed
   with its section class:

     .b-outcome    §1  Buy the outcome. Not the software.   (BO1)
     .b-steps      §2  20 minute setup. Pipeline on autopilot. (BT1)
     .b-roas       §4  8X ROAS                              (RO3)
     .b-logos      §5  The new meta is buying outcomes.     (LG1)
     .b-negation   §6  The negation                    (BA4 x BA1)
     .b-close      §7  Buy distribution.                 (CLOSE)
     .b-foot       §7  the white footer

   NO new custom properties, colours, radii or gradients are defined
   in this file. Everything comes from the :root block in hero.css.

   Animation targets are addressed by data-* attributes, never by a
   generic class name, because a <line class="core"> once collided
   with a <div class="core"> and rendered a whole section empty.
   ============================================================ */


/* ------------------------------------------------------------
   Page shell corrections - the two consequences of the page no
   longer ending at the fold. Neither changes anything visible
   above it.
   ------------------------------------------------------------ */

/* hero.css sets html,body{height:100%} so .page could fill the screen
   exactly. Once the document is taller than the viewport that height
   turns <body> into its own scroll container, because overflow-x:hidden
   forces the other axis from visible to auto. Releasing the height
   while keeping the same minimum restores normal document scrolling. */
html,body{height:auto;min-height:100%}

/* .page is a column flex box and .hero is flex:1 1 auto, so the hero
   used to absorb all the free space between the nav and the fold. With
   sections below it there is no free space left to grow into, and the
   hero would silently collapse to its content height. Pin the height it
   already had. This is the only rule in this file that touches the
   hero, and it exists purely to keep the hero identical. */
.page > .hero{min-height:calc(100svh - 80px)}
@media (max-width:400px){
  .page > .hero{min-height:calc(100svh - 68px)}
}


/* ============================================================
   SHARED PRIMITIVES  (design-rules.md §3.2)
   ============================================================ */
.wrap{max-width:1160px;margin:0 auto;padding:0 var(--pad-x);width:100%}
.wrap-n{max-width:900px;margin:0 auto;padding:0 var(--pad-x);width:100%}
/* Section rhythm (founder, 2026-07-25: "the spacing between sections and
   divides definitely has issues... giant whitespace between S5 and S6").
   Root cause was not any one seam: EVERY section carried 132px top AND bottom,
   and adjacent paddings stack, so every seam was a flat 264px dead band
   regardless of what the sections on either side of it were doing. Bringing
   the pair to 96px puts seams at 192px, which still reads as deliberate air
   on a light page but stops the sections reading as detached slides. */
.sec{padding:clamp(64px,7.2vw,96px) 0}
.sec-tight{padding:clamp(52px,7vw,88px) 0}
.center{text-align:center}

/* The .eyebrow .sdot is deliberately absent from every body section:
   a 6px sample of the spectrum resolves to a single olive dot and
   reads as a colour bug rather than an accent. */
.eyebrow{
  display:inline-flex;align-items:center;gap:9px;
  font-size:11.5px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  color:var(--faint);
}

h2.h2,.h2{
  font-weight:300;letter-spacing:-.032em;line-height:1.04;
  font-size:clamp(2rem,4.2vw,3.4rem);color:var(--ink);
}
.h2 em{font-style:normal;font-weight:600}
.h2 .num{font-variant-numeric:tabular-nums;font-weight:400}
.h3{font-weight:400;font-size:clamp(1.1rem,1.6vw,1.35rem);letter-spacing:-.018em;line-height:1.3}
.lede{color:var(--ink-soft);font-size:clamp(15.5px,1.25vw,17.5px);line-height:1.62;max-width:58ch}
.lede-c{margin-left:auto;margin-right:auto}
.small{font-size:13.5px;color:var(--muted);line-height:1.6}
.tiny{font-size:11.5px;color:var(--faint);letter-spacing:.02em}
.mt-s{margin-top:14px}.mt-m{margin-top:22px}.mt-l{margin-top:38px}
.mt-xl{margin-top:clamp(44px,5.5vw,68px)}

.label-u{
  font-size:11.5px;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint);
}
.nums{font-variant-numeric:tabular-nums}

/* .btn, .cta-primary and .cta-secondary already live in hero.css.
   The inverted button only exists on the dark closing band. */
.cta-invert{background:#FBFAF9;color:var(--ink);padding:13px 24px;font-size:15px}
.cta-invert:hover{background:#fff}

/* scroll reveal - body.js swaps .rv for .rv.in */
.rv{opacity:0;transform:translateY(12px);transition:opacity .7s ease,transform .7s cubic-bezier(.22,.61,.36,1)}
.rv.in{opacity:1;transform:none}
.rv.d1{transition-delay:.06s}.rv.d2{transition-delay:.13s}.rv.d3{transition-delay:.2s}
.rv.d4{transition-delay:.27s}.rv.d5{transition-delay:.34s}.rv.d6{transition-delay:.41s}

@media (prefers-reduced-motion:reduce){
  .rv{opacity:1;transform:none;transition:none}
}

/* ======================================================================
   SECTION 1 - Buy the outcome. Not the software.  (O26 'The Wash')
   ====================================================================== */
/* ============================================================
   §1 · .s-o26 - "Buy the outcome. Not the software."   (O26 "The Wash")
   Lifted from mockups/body-v3/o26-wash-front.html.

   FOUNDER EDIT ON LIFT: the section is UNBOXED. No plate, no border,
   no card wrapper. It sits directly on the page background. The
   .exit-card is the one intentional box and its 2px spectrum top rule
   is the section's ONE accent (280px+ of run, never clipped to a word).

   One fluid front sweeps top-down through a 48-item register. The
   register is rendered twice: the base copy (in flow) holds the
   DRAINED end state (no wash, text at --faint - legible, "handled not
   gone"); a JS-built clone sits absolutely over it holding the LOADED
   state (#F7F5F3 wash + --ink-soft) and is clipped away top-down with
   ONE animated clip-path. A 2px ink hairline rides the clip edge on
   the same duration and easing, so the drain is driven by the front's
   position: one sheet, zero per-row stagger, two animated elements
   rather than 96 backgrounds.

   When the front reaches the funnel the sweep converges along 48
   curved brace hairlines: a second copy of every path carries a short
   dark dash (pathLength="1" normalises all 48 curves to one unit)
   whose stroke-dashoffset runs .16 → -1.2, so darker segments flush
   down every curve at the same instant and vanish into the throat.
   Dash period is 2 (> path length 1 + dash .16) so the settled offset
   can never wrap the dark segment back onto the path - a period of 1
   re-lit the top 16% of every line ("upper tips stay black").

   Driver classes (added by body.js):
     .armed  overlay clone exists; base may show its drained end state
     .press  the surface line surfaces at the top of the register
     .run    the single flush
   Choreography under .run (~1.45s, one uninterrupted gesture):
     0ms     front + clip start together, .8s ease-in toward the throat
     620ms   funnel flush starts (.55s)
     1000ms  throat opens
     1120ms  exit card blooms
   All start/end values live in class rules - no inline styles on
   anything animated. Every hook is a data-* attribute.

   NO new custom properties, colours, radii or gradients.
   ============================================================ */
.s-o26{background:var(--bg)}
.s-o26 .unboxed{position:relative}

/* ---- head: a label row over a hairline, not a card header ---- */
.s-o26 .reg-head{
  display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding-bottom:10px;
  border-bottom:1px solid var(--hair);
}
.s-o26 .reg-count{font-size:12.5px;color:var(--muted)}

/* ---- the 48-item register ----
   Resting state: every cell carries the faint warm wash (#F7F5F3 - the
   sanctioned tinted-panel value, not a new token) so the grid reads as
   full / loaded. */
.s-o26 .washwrap{position:relative}
.s-o26 .register{display:grid;grid-template-columns:repeat(8,1fr)}
.s-o26 .register span{
  display:block;
  font-size:11px;line-height:1.35;color:var(--ink-soft);
  padding:9px 10px;
  border-bottom:1px solid var(--hair);
  background:#F7F5F3;
  overflow-wrap:break-word;
}
/* once armed, the base copy holds the drained end state underneath the
   loaded overlay. It is never seen until the front reveals it - and what
   it reveals is still legible: --faint, never dimmer. */
.s-o26 .unboxed.armed .register[data-o26-base] span{
  background:transparent;
  color:var(--faint);
}
/* the loaded overlay - a JS clone of the base register, clipped away top-down */
.s-o26 .register[data-o26-over]{
  position:absolute;inset:0;
  clip-path:inset(0 0 0 0);
  pointer-events:none;
}
.s-o26 .unboxed.run .register[data-o26-over]{
  clip-path:inset(100% 0 0 0);
  transition:clip-path .7s cubic-bezier(.4,.12,.5,.88);
}

/* ---- the front: the fluid surface line riding the clip edge ---- */
.s-o26 .front{
  position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--ink);
  opacity:0;
  pointer-events:none;
}
.s-o26 .unboxed.press .front{
  opacity:.45;
  transition:opacity .26s ease;
}
.s-o26 .unboxed.run .front{
  top:100%;
  opacity:0;
  transition:top .7s cubic-bezier(.4,.12,.5,.88),
             opacity .2s ease .56s;
}

/* ---- the funnel: 48 curved hairlines, generated at runtime ---- */
.s-o26 .funnel{
  position:relative;
  width:100%;
  aspect-ratio:1160/210;
  margin-top:4px;
}
.s-o26 .funnel-svg{display:block;width:100%;height:100%}
.s-o26 .funnel-svg path{
  fill:none;
  stroke:var(--hair-strong);
  stroke-width:1;
  vector-effect:non-scaling-stroke;
}
/* the draft's accent rhythm: every 8th strand is ink at low opacity, so the
   fan reads as 48 discrete lines instead of one grey wash */
.s-o26 .funnel-svg [data-o26-lines] path[data-o26-key]{
  stroke:var(--ink);
  opacity:.45;
}

/* ---- the converging flush: the path-accurate dash device on 48 curves ---- */
.s-o26 .funnel-svg [data-o26-flow] path{
  stroke:var(--ink);
  stroke-width:1.5;
  opacity:.9;
  stroke-dasharray:.16 1.84;
  stroke-dashoffset:.16;
}
.s-o26 .unboxed.run .funnel-svg [data-o26-flow] path{
  stroke-dashoffset:-1.2;
  transition:stroke-dashoffset .9s cubic-bezier(.4,.12,.5,.88) .52s;
}

/* ---- the throat: closed until the flush converges, then snaps open ---- */
.s-o26 .throat{
  position:absolute;left:50%;bottom:0;
  width:0;height:3px;
  transform:translate(-50%,50%);
  background:var(--ink);
  border-radius:999px;
  transition:width .2s cubic-bezier(.34,1.56,.64,1) 1.1s;
}
.s-o26 .unboxed.run .throat{width:58px}

/* ---- exit card: the one intentional box, and the section's ONE accent ---- */
.s-o26 .exit{
  display:flex;justify-content:center;
  padding:clamp(26px,4vw,36px) 20px clamp(30px,4.5vw,40px);
}
.s-o26 .exit-card{
  position:relative;overflow:hidden;
  display:inline-flex;flex-direction:column;align-items:flex-start;gap:9px;
  min-width:280px;
  background:var(--card);
  border:1px solid var(--hair-strong);
  border-radius:10px;
  padding:20px 26px 22px;
  opacity:0;
  transform:translateY(8px) scale(.96);
  transition:opacity .26s cubic-bezier(.34,1.56,.64,1) 1.18s,
             transform .26s cubic-bezier(.34,1.56,.64,1) 1.18s;
}
.s-o26 .unboxed.run .exit-card{opacity:1;transform:none}
/* the accent runs the full 280px+ width of the card - never clipped onto
   a short word or a small shape, where the gradient would resolve to one
   muddy hue */
.s-o26 .exit-card::before{
  content:"";position:absolute;top:0;left:0;right:0;height:2px;
  background:var(--spectrum);
}
.s-o26 .exit-kicker{color:var(--faint)}
.s-o26 .exit-title{
  font-size:clamp(1.15rem,1.9vw,1.5rem);
  font-weight:500;letter-spacing:-.02em;color:var(--ink);
}
.s-o26 .exit-quals{list-style:none;display:flex;flex-direction:column;gap:6px;margin:4px 0 0;padding:0}
.s-o26 .exit-quals li{display:flex;align-items:center;gap:7px;font-size:13px;color:var(--ink-soft)}
.s-o26 .exit-quals svg{flex:none;color:var(--ink)}

/* ---- reduced motion: land on the settled end state in one frame ---- */
.s-o26 .unboxed.instant,
.s-o26 .unboxed.instant *{transition:none !important;animation:none !important}

@media (prefers-reduced-motion:reduce){
  .s-o26 .register span,
  .s-o26 .register[data-o26-over],
  .s-o26 .front,
  .s-o26 .throat,
  .s-o26 .exit-card{transition:none !important;animation:none !important}
  .s-o26 .funnel-svg [data-o26-flow] path{transition:none !important}
}

/* ---- responsive ---- */
@media (max-width:900px){
  .s-o26 .register{grid-template-columns:repeat(4,1fr)}
  /* the 1160/210 ratio collapses the funnel to a ~63px sliver on a phone;
     floor the height instead - preserveAspectRatio="none" just steepens
     the curves. */
  .s-o26 .funnel{aspect-ratio:auto;height:150px}
}
/* MOBILE PASS 2026-07-25 - O1. Ragged rows are what breaks a register,
   not narrow ones: a grid row is as tall as its tallest cell, so ONE
   wrapped label inflates every sibling in its row. Three columns left
   a cell 90-97px of text at 360 and 115px at 430, against a longest
   label ("Deliverability monitoring") that measures 126px at 11px -
   so it wrapped at both, and cell heights alternated 32px / 47px.
   Two columns give 146px at 360 and 179px at 430, which holds it on
   one line and buys a font size back: 24 uniform rows at ~33px
   instead of 16 ragged ones. The register is a VOLUME argument - 48
   rows read as "a lot" at two columns - so the column count moves and
   the type never shrinks.
   NOTE the same longest label still wraps in the 4-column band
   (481-650, 7 cells at 560) and in the 8-column desktop band (5 cells
   at 1440). Both are pre-existing and neither is a mobile call: the
   register needs either a shorter label or a fourth step in this
   ladder, and that is a desktop decision. Measured, not fixed here. */
@media (max-width:480px){
  .s-o26 .register{grid-template-columns:repeat(2,1fr)}
  .s-o26 .register span{padding:8px 7px;font-size:11.5px}
  .s-o26 .funnel{height:132px}
}
/* O3: .exit-card's 280px floor plus .exit's 20px side padding exceeds
   the column below a ~320px viewport. Release the floor and let the
   card fill the column instead; its 2px spectrum cap then runs the
   full card width (~280px at 320), well over the 120px the gradient
   needs before it resolves to one muddy hue.
   The funnel's 48 curves are NOT reduced at any width: 48 curves is
   the same number as 48 register cells and that is the whole device. */
@media (max-width:400px){
  .s-o26 .exit{padding-inline:0}
  .s-o26 .exit-card{min-width:0;width:100%}
}

/* ======================================================================
   SECTION 2 - Reach millions 1:1  (RM6 'Compounding')
   ====================================================================== */
/* ============================================================
   SECTION 2 - Reach millions 1:1        (.s-rm6, candidate RM6)
   Lifted from mockups/body-v2/assets/b2.css, fence "===== RM6 ====="
   plus the tabular-figure rule it depends on.

   Nothing new is defined here: no new colour, no new token, no
   radius outside the sanctioned set, no new primitive class. The
   only custom property is the local animation parameter --p on the
   rail fill, which is the same device .b-roas already uses for --h
   and --d. Every rule is prefixed .s-rm6 so it cannot leak.

   ACCENT BUDGET
   The section spends its whole spectrum budget on ONE object: the
   progress rail. It is a full-width 2px line (>=120px of run at every
   breakpoint) revealed left to right by clip-path, never a filled
   surface, and no ping on the canvas ever carries a hue. The eyebrow
   carries no .sdot, matching every other body section.
   ============================================================ */

/* The headline contains "1:1", so the section's tabular-nums duty is
   not discharged by the figure styles alone. */
.s-rm6 .h2{font-variant-numeric:tabular-nums}

/* The map host. aspect-ratio rather than a fixed height, so the canvas
   box is always the country's own proportion and the dot field fills
   it edge to edge instead of floating in a letterboxed panel. The
   renderer letterboxes internally if the box is ever taller than the
   country, so the stage can never overflow its column. */
/* founder, 2026-07-25: "only" was dropping to its own line. text-wrap:pretty
   is the purpose-built fix (it reflows to avoid a short last line); the
   max-width is the fallback for engines without it. */
.s-rm6 .lede{max-width:52ch;text-wrap:pretty}

.s-rm6 .rm6-stage{
  position:relative;width:100%;
  margin-top:clamp(26px,3.4vw,44px);
  aspect-ratio:1.83/1;max-height:660px;min-height:160px;
}
.s-rm6 .rm6-stage canvas{position:absolute;inset:0;width:100%;height:100%;display:block}

.s-rm6 .rm6-rail{
  position:relative;height:1px;background:var(--hair-strong);
  margin-top:clamp(18px,2.4vw,30px);
}
/* The one spectrum moment: a full-width rail whose inked length is
   the same number as the counter beneath it. */
.s-rm6 .rm6-fill{
  --p:100%;
  position:absolute;left:0;top:-1px;height:2px;width:100%;
  background:var(--spectrum);opacity:.9;
  -webkit-clip-path:inset(0 calc(100% - var(--p)) 0 0);
          clip-path:inset(0 calc(100% - var(--p)) 0 0);
}

.s-rm6 .rm6-foot{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:10px 26px;
  justify-content:space-between;padding-top:14px;
}
.s-rm6 .rm6-read{display:inline-flex;align-items:baseline;flex-wrap:wrap;min-width:0}
.s-rm6 .rm6-n{
  font-variant-numeric:tabular-nums;font-weight:300;
  font-size:clamp(2.2rem,5vw,3.8rem);letter-spacing:-.045em;line-height:1;color:var(--ink);
}
.s-rm6 .rm6-l{
  font-size:11.5px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  color:var(--faint);margin-left:12px;
}
.s-rm6 .rm6-tag{font-size:13.5px;color:var(--muted);line-height:1.6}

/* Reduced motion: the rail rests fully inked and the counter is left
   at its authored 550, so the settled end state is what is shown.
   s2.js makes the same call for the canvas. */
@media (prefers-reduced-motion:reduce){
  .s-rm6 .rm6-fill{-webkit-clip-path:inset(0 0 0 0);clip-path:inset(0 0 0 0)}
}

/* MOBILE PASS 2026-07-25 - R1.
   Stacked, the stage measured 350x191 inside a 621px section: 31% of
   its own section against 57% at 1440. Every other object on the page
   keeps its proportional weight when it stacks; this one halved it,
   and it is the only thing carrying the "Reach millions 1:1" claim.
   The stage is aspect-locked, so the only lever is width.
   .wrap is full width below 1160px, so a negative --pad-x margin lands
   exactly on the viewport edge. Do NOT use 100vw here: it counts the
   scrollbar on desktop and fights body{overflow-x:hidden}.
   Releasing max-height cannot letterbox - the renderer letterboxes
   internally, so a taller box adds padding, never an empty panel. */
@media (max-width:760px){
  .s-rm6 .rm6-stage{
    margin-inline:calc(var(--pad-x) * -1);
    width:auto;max-height:none;
  }
}

/* ======================================================================
   SECTION 3 - How it works  (three bentos)
   ====================================================================== */
/* ============================================================
   §3 · .b-steps - "20 minute setup. Pipeline on autopilot."
   Lifted from body-v2 candidate BT1 (s3-bentos.html + x3.css),
   round-3 revision. Three compact bentos, one specimen plate flush
   to the bottom of each so the diagrams sit on a single line across
   the row. Card 01's plate is the ICP filter set resolving to a TAM
   figure; card 02's is the proposal table (no per-call figure in it);
   card 03's is the booked-calendar grid.
   No new tokens: every value below composes hero.css :root.
   ============================================================ */
.b-steps .bento{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(14px,1.6vw,20px);margin-top:clamp(40px,5vw,62px);
}
.b-steps .bcard{
  position:relative;overflow:hidden;
  background:var(--card);border:1px solid var(--hair);border-radius:10px;
  padding:clamp(20px,2.4vw,28px);
  display:grid;grid-template-areas:"idx" "ttl" "txt" "viz";
  grid-template-rows:auto auto auto 1fr;
}
/* the section's ONE accent: card 03 gets a full-card-width spectrum
   cap (~330px of run at 1440, well over the 120px legibility floor).
   border-top is dropped on that card so the rule IS the top edge
   rather than a second line sitting 1px inside a hairline; the 1px is
   given back as padding so all three 01/02/03 indices share a baseline */
.b-steps .bcard.pay{border-top:0;padding-top:calc(clamp(20px,2.4vw,28px) + 1px)}
.b-steps .bcard.pay::before{
  content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--spectrum);opacity:.9;
}
.b-steps .idx{
  grid-area:idx;font-size:11.5px;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint);font-variant-numeric:tabular-nums;
}
/* All three headings are single-line strings, which is what puts the
   three cards' body paragraphs on one shared baseline. If a heading
   ever grows to two lines that alignment is lost - keep them short. */
.b-steps .bcard h4{
  grid-area:ttl;margin-top:14px;font-size:clamp(1rem,1.3vw,1.1rem);
  font-weight:500;letter-spacing:-.02em;line-height:1.32;max-width:28ch;
}
/* In the three-across layout card 02 runs to three lines and cards 01
   and 03 to two, which pushed card 02's plate 21.6px below its
   siblings'. Reserving three lines in every card puts all three plate
   TOP edges on one line as well as their bottoms. Stated in em so it
   tracks the type; it is a floor, never a cap, and it is released once
   the cards stack (see the 980px block). */
.b-steps .bcard > p{
  grid-area:txt;margin-top:9px;font-size:13.5px;color:var(--muted);line-height:1.6;
  min-height:4.8em;
}
.b-steps .viz{grid-area:viz;align-self:stretch;display:flex;flex-direction:column;padding-top:20px}
.b-steps .plate{
  flex:1;
  border:1px solid var(--hair);border-radius:var(--radius);background:#FBFAF9;
  padding:13px;min-height:clamp(110px,11vw,130px);
  display:flex;flex-direction:column;justify-content:center;
}
.b-steps .pcap{margin-top:11px;font-size:11px;color:var(--faint);line-height:1.5}

/* card 01 - the ICP filter set. Every axis named in the subhead gets a
   token; the set then resolves to an addressable figure below the rule,
   so the plate shows a computation and not a list. */
.b-steps .chips{display:flex;flex-wrap:wrap;gap:6px}
.b-steps .chips span{
  font-size:11.5px;color:var(--muted);border:1px solid var(--hair);
  border-radius:999px;padding:4px 10px;background:var(--card);
  white-space:nowrap;
}
.b-steps .chips span.on{border-color:var(--ink);color:var(--ink)}
.b-steps .tamrow{
  display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  margin-top:13px;padding-top:11px;border-top:1px solid var(--hair-strong);
}
.b-steps .tamkey{
  font-size:11.5px;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint);
}
.b-steps .tamout{display:flex;align-items:baseline;gap:6px;min-width:0}
.b-steps .tamval{
  font-size:19px;font-weight:400;letter-spacing:-.02em;color:var(--ink);
  font-variant-numeric:tabular-nums;line-height:1;
}
.b-steps .tamunit{font-style:normal;font-size:11.5px;color:var(--faint)}

/* card 02 - the proposal table. Four hairline rows, key on the left,
   value on the right, tabular. The rate row carries no figure by
   design: the price of a qualified call is never published.

   THE INDENT BUG (fixed here). The row was a flex box with
   justify-content:space-between, so the label column had no width of
   its own - it got whatever the value left over. "Rate per qualified
   call" was the longest label on the sheet, so it was squeezed to
   113px, wrapped onto a second line, and grew that row 17px taller
   than its three siblings; align-items:baseline then pinned the value
   to the FIRST line and left the orphaned word hanging below, which is
   what read as an indented, out-of-line row. Below 1140px the old
   flex-wrap:wrap + margin-left:auto rescue made it literally indent -
   the value dropped to its own line at a different x.
   Fix: a real two-column grid. The value track is auto (its content
   width), the label track is minmax(0,1fr) and never has to compete
   for it, min-height locks the row rhythm, and the type steps down at
   the narrow end instead of wrapping. All four rows now share one left
   edge, one right edge and one height. */
.b-steps .sheet{border:1px solid var(--hair);border-radius:var(--radius);background:var(--card);overflow:hidden}
.b-steps .srow{
  display:grid;grid-template-columns:minmax(0,1fr) auto;
  align-items:baseline;column-gap:12px;min-height:32px;
  padding:7px 10px;font-size:11.5px;color:var(--muted);
  border-top:1px solid var(--hair);
}
.b-steps .srow:first-child{border-top:none;min-height:31px}
.b-steps .srow span{min-width:0}
.b-steps .srow b{
  font-weight:500;color:var(--ink);text-align:right;font-variant-numeric:tabular-nums;
}
.b-steps .srow.head{background:#FBFAF9}

.b-steps .calhead{display:grid;grid-template-columns:repeat(5,1fr);gap:5px;margin-bottom:6px}
.b-steps .calhead span{font-size:11px;color:var(--faint);letter-spacing:.04em}
.b-steps .cal{display:grid;grid-template-columns:repeat(5,1fr);gap:5px}
.b-steps .cal i{display:block;width:100%;height:17px;border-radius:var(--radius);background:#F7F5F3}
.b-steps .cal i.b{background:var(--ink)}

/* Between ~980 and ~1140 the three cards are at their narrowest while
   still side by side. Step the sheet's type and padding down so every
   row still fits on one line - never let it wrap, which is what broke
   the row rhythm before. */
@media (max-width:1140px){
  .b-steps .srow{font-size:11px;padding:6px 8px;column-gap:8px;min-height:29px}
  .b-steps .srow:first-child{min-height:28px}
  .b-steps .chips span{font-size:11px;padding:4px 9px}
  .b-steps .tamval{font-size:18px}
}
@media (max-width:980px){
  .b-steps .bento{grid-template-columns:1fr;gap:14px}
  .b-steps .bcard{
    grid-template-areas:"idx viz" "ttl viz" "txt viz";
    grid-template-columns:minmax(0,1fr) minmax(0,320px);
    grid-template-rows:auto auto 1fr;column-gap:clamp(20px,3.5vw,44px);
  }
  .b-steps .bcard > p{min-height:0}
  .b-steps .viz{align-self:center;padding-top:0}
  .b-steps .srow{font-size:11.5px;padding:7px 10px;column-gap:12px;min-height:32px}
  .b-steps .srow:first-child{min-height:31px}
  .b-steps .chips span{font-size:11.5px;padding:4px 10px}
  .b-steps .tamval{font-size:19px}
}
@media (max-width:620px){
  .b-steps .bcard{
    grid-template-areas:"idx" "ttl" "txt" "viz";
    grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto auto auto;
  }
  .b-steps .viz{align-self:end;padding-top:18px}
  .b-steps .plate{min-height:0}
}
/* MOBILE PASS 2026-07-25 - S1. At 360 card 01's chip set wrapped to a
   fourth row, which grew its plate from 168px to 201px and left card
   01 33px taller than its siblings - the three-card rhythm the rules
   above work to protect at 1140 and 980 was lost at the narrow end.
   The chips step down instead. No chip is dropped: every axis named in
   the card's own subhead has to have a token in the set. */
@media (max-width:400px){
  .b-steps .chips{gap:5px}
  .b-steps .chips span{font-size:11px;padding:4px 8px}
}

/* ======================================================================
   SECTION 4 - The economics  (8X ROAS)
   ====================================================================== */
/* ============================================================
   §4 · .b-roas · "8X ROAS"   (re-modelled 2026-07-25, v3 constants)

   THE GEOMETRY: a column's full height is the CW revenue that one
   month of spend returns (cohort-indexed, not calendar-indexed). The
   dark block at its base is that spend, and it is 12.5% of EVERY
   column - a constant one-eighth share - which is why the 8X headline
   and the picture agree by construction. All twelve heights are one
   strictly ascending 1.09 series, 38.75% -> 100%, so there is no step
   at the outline/fill boundary. Absolute spend climbs 2.58x across
   the twelve (G20).

   MONTHS 01-03 ARE DRAWN, NOT BLANK: the spend is real and stays full
   ink; the return above it is a dashed outline because on a 90-day
   cycle nothing at all has closed during the opening quarter. Three
   empty slots read as broken (G34); an outline reads as pending. The
   ghost bars are the SAME .bar element on the SAME keyframes with the
   same duration, easing and 0.10s stagger - .ghost only swaps fill
   for outline. The legend's third key names it.

   ALIGNMENT: the ledger is a card, so its inner label row is inset by
   the card's padding. .left is given the same padding-top, which puts
   the hairline that opens the chart and the hairline that opens the
   ledger back on exactly the same y. Do not change one without the
   other.

   THE ONE ACCENT: a 2px spectrum rule spanning the full width of the
   ledger's last row, marking the figure the other two produce. Full
   width of its own rule (G26), well over 120px at every breakpoint
   (G24), and a line rather than a surface (G27).

   No new custom property, colour, radius or gradient is introduced
   here. The only inline custom properties are --h (a bar's height)
   and --d (its reveal delay), both carried over from the previous
   build of this section.
   ============================================================ */
.b-roas .h2{font-size:clamp(2.4rem,5.4vw,4.2rem)}
.b-roas .h2 em{font-variant-numeric:tabular-nums}

.b-roas .split{
  margin-top:clamp(34px,4vw,56px);display:grid;
  grid-template-columns:minmax(0,1.62fr) minmax(0,1fr);
  gap:clamp(24px,3.6vw,56px);align-items:stretch;
}

/* ---- the chart column ---- */
/* padding-top mirrors the ledger card's padding so the two opening
   hairlines still land on the same y (see ALIGNMENT above) */
.b-roas .left{display:flex;flex-direction:column;padding-top:calc(clamp(18px,2.1vw,26px) + 1px)}

.b-roas .ytop{
  display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding-bottom:11px;
}
/* .ylab, .ytick and .bhead all carry the shared .label-u primitive.
   Only the line-height is pinned here, because the two rules that open
   the chart and the ledger have to land on the same y */
.b-roas .ylab,.b-roas .ytick,.b-roas .bhead{line-height:1.1}
.b-roas .ytick{color:var(--ink-soft);letter-spacing:.06em;white-space:nowrap}

.b-roas .chart{
  position:relative;display:flex;align-items:flex-end;gap:clamp(3px,.7vw,10px);
  height:clamp(272px,29vw,400px);
  border-top:1px solid var(--hair);
  border-bottom:1px solid var(--hair-strong);
  margin-bottom:28px;
}
.b-roas .col{flex:1 1 0;min-width:0;height:100%;position:relative}

.b-roas .bar{
  position:absolute;left:0;right:0;bottom:0;height:var(--h);
  display:flex;flex-direction:column;justify-content:flex-end;overflow:hidden;
  border-radius:var(--radius) var(--radius) 0 0;
}
.b-roas .ret{flex:1 1 auto;min-height:0;background:var(--hair);border-top:1px solid var(--hair-strong)}
.b-roas .cost{flex:none;height:12.5%;background:var(--ink)}

/* months 01-03: the spend block is real and stays full ink; the return
   it will produce is an unfilled dashed outline at the height of the
   solid column it becomes three months later. No border-bottom - the
   axis rule already draws that edge */
.b-roas .bar.ghost{
  background:transparent;
  border:1px dashed var(--hair-strong);border-bottom:0;
}
.b-roas .ghost .ret{background:transparent;border-top:0}

.b-roas .mlab{
  position:absolute;top:100%;left:0;right:0;margin-top:11px;text-align:center;
  font-size:11.5px;color:var(--faint);font-variant-numeric:tabular-nums;letter-spacing:.02em;
}

.b-roas .keys{
  display:flex;gap:clamp(14px,2.2vw,26px);flex-wrap:wrap;
  border-top:1px solid var(--hair);padding-top:13px;
}
.b-roas .key{display:inline-flex;align-items:center;gap:9px;font-size:12.5px;color:var(--ink-soft)}
.b-roas .key i{flex:none;width:20px;height:9px}
.b-roas .key i.pay{background:var(--ink)}
.b-roas .key i.get{background:var(--hair);border:1px solid var(--hair-strong)}
.b-roas .key i.pend{background:transparent;border:1px dashed var(--hair-strong)}

/* ---- the ledger column, as a card ---- */
/* justify-content is deliberately NOT space-between: that would push a
   gap between .bhead and the first row and drop the ledger's opening
   hairline below the chart's. The rows flex-grow instead, so row one's
   rule sits directly under .bhead and lands on exactly the same y as
   the rule that opens the chart */
.b-roas .book{
  display:flex;flex-direction:column;justify-content:flex-start;
  background:var(--card);border:1px solid var(--hair);border-radius:var(--radius);
  padding:clamp(18px,2.1vw,26px) clamp(18px,2.1vw,26px) clamp(20px,2.3vw,28px);
}
.b-roas .bhead{display:block;padding-bottom:11px}
.b-roas .book > div{
  flex:1 1 auto;
  border-top:1px solid var(--hair);
  padding:clamp(16px,1.9vw,24px) 0;
}
.b-roas .fig .v{
  display:block;font-weight:300;font-size:clamp(2rem,3.2vw,2.75rem);letter-spacing:-.045em;
  line-height:1;font-variant-numeric:tabular-nums;color:var(--ink);
}
.b-roas .fig .l{display:block;margin-top:9px;font-size:12.5px;color:var(--muted);line-height:1.5}

/* the section's ONE accent: the ledger's last rule is the spectrum,
   full width of that rule. border-top is dropped on the row so the
   rule IS its top edge rather than a second line 1px inside a
   hairline, and the 1px is given back as padding so all three rows
   keep the same optical gap under their rule */
.b-roas .book > .roas{position:relative;border-top:0;padding-top:calc(clamp(16px,1.9vw,24px) + 1px)}
.b-roas .book > .roas::before{
  content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--spectrum);opacity:.9;
}
.b-roas .roas .v{
  display:flex;align-items:baseline;font-weight:300;font-size:clamp(2.6rem,4.4vw,3.6rem);
  letter-spacing:-.05em;line-height:1;font-variant-numeric:tabular-nums;color:var(--ink);
}
.b-roas .roas .v b{font-weight:300}
.b-roas .roas .v em{font-style:normal;font-weight:400;font-size:.46em;color:var(--muted);margin-left:1px}
.b-roas .roas .l{display:block;margin-top:10px;font-size:12.5px;color:var(--muted);line-height:1.5}

/* ---- the datapoint claim: last row inside the card ---- */
/* a <p>, not a <div>, so .book > div never gives it the figure-row
   flex-grow and it stays pinned to the bottom of the card */
.b-roas .claim{
  margin-top:clamp(18px,2.1vw,26px);
  border-top:1px solid var(--hair);padding-top:clamp(14px,1.6vw,18px);
  font-size:clamp(12.5px,.95vw,13.5px);color:var(--ink-soft);line-height:1.6;
}
.b-roas .note-i{
  margin-top:clamp(16px,1.9vw,24px);max-width:92ch;line-height:1.65;
  font-variant-numeric:tabular-nums;
}

/* the bars grow on scroll - slow and singular, ~2.25s end to end for
   the whole sweep of nine */
@keyframes b-roas-grow{from{height:0}to{height:var(--h)}}
.b-roas .left.rv.in .bar{
  animation:b-roas-grow 1.15s cubic-bezier(.22,.61,.36,1) both;
  animation-delay:var(--d);
}

@media (max-width:900px){
  .b-roas .split{grid-template-columns:1fr;gap:34px}
  /* stacked: nothing left to align the chart's opening hairline to, so
     the inset that mirrored the card's padding is given back */
  .b-roas .left{padding-top:0}
  /* MOBILE PASS 2026-07-25 - E2. Once .split stacks, the chart is the
     only visual in a 1,263px section and it carried 17% of it against
     the ~60% a section's object is meant to hold. The bars ARE the
     argument, so they earn the height: 214px -> 280px at 390. Getting
     past this means dropping one of the five beats below the chart,
     not resizing another one. */
  .b-roas .chart{height:clamp(280px,62vw,340px)}
}
@media (max-width:560px){
  /* every one of the twelve columns is kept at every width - dropping
     months would break the argument that spend climbs month over
     month. The AXIS thins instead: first, last and the two structural
     turns. visibility rather than opacity so the eight dropped labels
     leave the accessibility tree with the pixels; both are absolutely
     positioned, so neither reflows the chart. */
  .b-roas .chart{gap:3px}
  .b-roas .mlab{visibility:hidden}
  .b-roas .col[data-key] .mlab{visibility:visible}
  .b-roas .ylab{letter-spacing:.08em}
}
@media (prefers-reduced-motion:reduce){
  .b-roas .left.rv.in .bar{animation:none}
}

/* ======================================================================
   SECTION 5 - The new meta  (customer wall)
   ====================================================================== */
/* ============================================================
   §5 · .b-logos - "World-class teams require world-class outcomes."

   COMPLETE replacement for the previous §5 block in body.css. Nothing
   from the old block survives - the flip card, [data-stmt], [data-tag],
   [data-face], [data-go], [data-note] and all three of their @media
   overrides are gone. Delete the whole old §5 block, don't merge.

   THE WALL
   Eight cells on a plain repeat(4,1fr) with a 1px gap over a
   --hair-strong ground, so both rows share every vertical rule by
   construction and every cell is exactly 1fr wide. 2-up under 760px,
   still one shared set of gridlines.

   THE HOVER  (founder: "a black bg fill sliding up and maybe a rainbow
   underline/overline on the card you're hovering over")
   [data-wipe] is one opaque --ink block parked a full card-height below
   the card and translated to 0 on hover/focus. It is a transform, so it
   composites on the GPU - never a background-color transition. Its own
   top edge carries the 2px spectrum, so the rainbow rides the leading
   edge of the fill up the card and settles as that card's overline. The
   gradient runs the full cell width (258px at 1440, ~175px at 390),
   both well over the ~120px the spectrum needs before it collapses into
   one muddy hue (G24/G26). [data-inv] is a second, light copy of the
   same content clipped to exactly the fill's edge - biased 1px behind
   it so a rounding seam can never flash light-on-light - so the mark
   and name appear to invert ALONG the wipe instead of cross-fading
   behind it. Only the hovered cell moves; siblings are untouched.

   COLOUR AT REST, GRAYSCALE ON HOVER  (round 3, founder override)
   The polarity of the wall is INVERTED from how it first shipped: the
   eight marks now carry their real brand colour at rest and drop to
   grayscale as the ink wipe takes the card. This deliberately reverses
   design-rules.md §6 ("body logo walls go grayscale, real brand colour
   is hero-only") - founder call, recorded in handoff/open-decisions.md.
   The wipe itself is unchanged: same 420ms cubic-bezier, same spectrum
   hairline on the leading edge, same clipped inverted copy.

   OPTICAL NORMALISATION OF THE EIGHT MARKS
   The roster arrives as seven full-bleed icons (disc / circle / square /
   petal), one stacked wordmark lockup, two polarities and a ~200-point
   spread in lightness - and now four brand palettes (lime-teal,
   blue-green, cyan, green) against four achromatic near-blacks. Three
   corrections, all here:
     1 SIZE - every mark sits in one shared 44px slot, 24-41px inside it.
       Sizes are set so the marks carry equal optical MASS, not equal
       box height: a solid black circle at 32px outweighs an open
       four-point star at 37px, so Forge comes down to 30px and
       HealthCues goes up to 41px. Flex Health is the one wordmark and
       is set by CAP HEIGHT, not bounding box.
     2 CROP - Coworker's viewBox is tightened to the disc's own bounds so
       it does not render undersized inside its own padding. The crop
       lives in the markup.
     3 TONE - at rest the marks are unfiltered brand colour, with a
       saturate/brightness pair on the three light-field marks
       (Coworker, Creator, HealthCues) only. Those three are light
       enough that raw colour on a near-white ground left them reading
       ~40% under the black marks; deepening the hue closes the gap
       without touching the hue itself. Measured on dE76-vs-ground x
       area, the eight now span 1.23x (was 1.76x in grayscale).
       NO polarity flip is used any more. Creator's flip existed only to
       make its light green field survive grayscale on white; in colour
       the green carries itself, and inverting it would have produced a
       magenta that is not Creator's brand at all.
   On ink the chain is grayscale(1) invert(1) for the six dark-field
   marks. Coworker and Creator are NOT inverted: their fields grayscale
   to L~193-235, already light against ink, and inverting them would
   sink the mark into the fill. Both faces were measured, not eyeballed.
   ============================================================ */

/* the statement uses the page's own .h2 primitive; the only local rule
   is a no-break guard so "World-class" can never break at its hyphen */
.b-logos .h2 span{white-space:nowrap}

/* the <symbol> library - out of flow, zero-sized, never painted */
.b-logos [data-sprite]{position:absolute;width:0;height:0;overflow:hidden}

.b-logos .wall{
  margin-top:clamp(34px,4.4vw,56px);
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;
  background:var(--hair-strong);border:1px solid var(--hair-strong);
}

.b-logos [data-story]{
  position:relative;display:block;min-width:0;
  min-height:clamp(126px,12vw,164px);
  background:var(--bg);color:var(--ink);text-decoration:none;
  overflow:hidden;
}

/* the two faces are identical boxes, so the inversion is positional */
.b-logos [data-body],
.b-logos [data-inv]{
  position:absolute;inset:0;
  display:flex;flex-direction:column;justify-content:space-between;align-items:flex-start;
  padding:clamp(18px,1.7vw,24px);
}
.b-logos [data-body]{z-index:1}
.b-logos [data-inv]{
  z-index:3;pointer-events:none;color:#FBFAF9;
  clip-path:inset(calc(100% + 1px) 0 0 0);
  transition:clip-path .42s cubic-bezier(.22,.61,.36,1);
}

/* the ink fill, with the spectrum riding its leading edge */
.b-logos [data-wipe]{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  background:var(--ink);
  transform:translate3d(0,100%,0);
  transition:transform .42s cubic-bezier(.22,.61,.36,1);
  will-change:transform;
}
.b-logos [data-wipe]::after{
  content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--spectrum);
}

/* ---- 1 · size: one 44px slot, equal optical MASS inside it ---- */
.b-logos [data-mark]{display:flex;align-items:center;height:44px;flex:none}
.b-logos [data-mark] svg{display:block}

.b-logos [data-mark="coworker"] svg  {width:34px;height:34px}
.b-logos [data-mark="writer"] svg    {width:32px;height:32px}
.b-logos [data-mark="healthcues"] svg{width:41px;height:41px}
.b-logos [data-mark="flexhealth"] svg{width:49px;height:38px}
.b-logos [data-mark="redacted"] svg  {width:27px;height:27px}
.b-logos [data-mark="creator"] svg   {width:31px;height:31px}
.b-logos [data-mark="forge"] svg     {width:30px;height:30px}
/* Orvyn is a SOLID square with no counter, so it puts more ink on the
   page per pixel than any other mark - 24px is where its mass lands in
   the same band as the rest. Do not scale it to match their box height. */
.b-logos [data-mark="orvyn"] svg     {width:24px;height:24px}

/* ---- 3 · tone: the resting face is real brand colour ----
   Only the three light-field marks are corrected, and only far enough
   to close the contrast gap against the near-white ground. */
.b-logos [data-body] [data-mark] svg             {filter:none}
.b-logos [data-body] [data-mark="coworker"] svg  {filter:saturate(1.35) brightness(.86)}
.b-logos [data-body] [data-mark="creator"] svg   {filter:saturate(1.18) brightness(.88)}
.b-logos [data-body] [data-mark="healthcues"] svg{filter:saturate(1.1)}
.b-logos [data-body] [data-mark="orvyn"]         {color:var(--ink)}

/* ---- 3 · tone: the face riding the ink wipe is grayscale ----
   Six dark-field marks invert. Coworker and Creator do not: grayscale
   already lands them light, so inverting would bury them in the fill. */
.b-logos [data-inv] [data-mark] svg             {filter:grayscale(1) invert(1)}
.b-logos [data-inv] [data-mark="coworker"] svg  {filter:grayscale(1) brightness(.94)}
.b-logos [data-inv] [data-mark="creator"] svg   {filter:grayscale(1) brightness(1.12)}
.b-logos [data-inv] [data-mark="healthcues"] svg{filter:grayscale(1) invert(1) brightness(1.45)}
/* the square is currentColor, so it is the invert(1) above that lifts it
   to light on ink - which is exactly what Orvyn's own site does */
.b-logos [data-inv] [data-mark="orvyn"]         {color:var(--ink)}

.b-logos [data-name]{
  font-size:13.5px;font-weight:500;letter-spacing:-.008em;line-height:1.2;
  color:var(--muted);
}
.b-logos [data-inv] [data-name]{color:#FBFAF9}

/* ---- the one interaction: hover and keyboard get the same state ----
   MOBILE PASS 2026-07-25 - L1. No :active fallback is added for touch,
   deliberately. The cells are no longer links (see the note in
   index.html): there is no destination, so a phone is shown a logo
   wall and nothing that behaves like it can be opened. The moment the
   customer stories ship and the cells go back to <a>, an :active pair
   mirroring the two :hover rules below is the touch equivalent. */
@media (hover:hover){
  .b-logos [data-story]:hover [data-wipe]{transform:translate3d(0,0,0)}
  .b-logos [data-story]:hover [data-inv]{clip-path:inset(1px 0 0 0)}
}
.b-logos [data-story]:focus-visible [data-wipe]{transform:translate3d(0,0,0)}
.b-logos [data-story]:focus-visible [data-inv]{clip-path:inset(1px 0 0 0)}
.b-logos [data-story]:focus-visible{outline:2px solid var(--ink);outline-offset:2px;z-index:4}

@media (max-width:760px){
  .b-logos .wall{grid-template-columns:repeat(2,minmax(0,1fr))}
}

/* reduced motion: the same state change, arriving instantly */
@media (prefers-reduced-motion:reduce){
  .b-logos [data-wipe],
  .b-logos [data-inv]{transition:none}
}

/* ======================================================================
   SECTION 6 + 7 - FAQ, with the negation welded on  (FQ6)
   ====================================================================== */
/* ============================================================
   §6 · .b-faq - On the record, then the negation.
   ONE section, two beats. Port of body-v2 FQ6 (round 3, chosen)
   with the production negation welded on as its closing beat.

   NO new custom property, colour, radius or primitive class is
   defined in this file. Everything comes from the :root block in
   hero.css and the primitives already in body.css.

   EVERY selector is scoped under .b-faq, so nothing here can reach
   another section. Animation targets are data-* hooks, never
   generic class names.

   ONE ACCENT FOR THE WHOLE SECTION. FQ6 shipped a spectrum rule
   under its lede; since the FAQ and the negation are now one
   section they share one accent budget, so that rule is plain
   --hair-strong here and the spectrum is spent once, on the
   centre-out divider (G17/G18).

   THE FLAT LAYOUT IS THE DEFAULT. Every pinning rule lives inside
   the one media query below AND is gated on .is-pinned, which
   s6.js adds only when motion is allowed and position:sticky
   actually computes. So prefers-reduced-motion, a narrow
   viewport, a short viewport and a browser without sticky all
   resolve to the same thing: six stacked cards, all visible, no
   motion. Suppression is opacity plus transform only, never
   display:none, so every answer stays in the DOM, selectable and
   findable.
   ============================================================ */


/* ------------------------------------------------------------
   1 · the frame
   ------------------------------------------------------------ */
.b-faq .faqrule{
  height:1px;background:var(--hair-strong);
  margin-top:clamp(30px,4vw,44px);
}
/* Tightened (founder, 2026-07-25). With the eyebrow and lede gone the
   heading is one line, so 56px here plus the frame's own top padding read as
   a hole between the heading and the first card. */
.b-faq .faqtrack{margin-top:clamp(20px,2.2vw,30px)}
/* Server-rendered answers for crawlers and no-JS readers. body.js swaps the
   whole track out on load, so this only paints when scripts never run. */
.b-faq .faqstatic{display:grid;gap:22px;max-width:62ch}
.b-faq .faqstatic dt{font-weight:500;color:var(--ink)}
.b-faq .faqstatic dd{margin:6px 0 0;color:var(--ink-soft);line-height:1.55}

.b-faq .faqin{
  display:grid;grid-template-columns:262px minmax(0,1fr);
  gap:clamp(28px,5vw,68px);align-items:start;
}

/* the rail - a position indicator, not a second set of headings */
.b-faq .faqrt{display:block;margin-bottom:16px}
.b-faq .faqrailwrap{position:relative;padding-left:20px}
.b-faq .faqprog{position:absolute;left:0;top:0;bottom:0;width:1px;background:var(--hair)}
.b-faq .faqprog i{display:block;width:1px;height:100%;background:var(--ink);transform-origin:top center}
.b-faq .faqlist{list-style:none;display:grid;gap:2px}
.b-faq .faqitem{
  position:relative;display:grid;grid-template-columns:26px minmax(0,1fr);
  column-gap:10px;padding:9px 0;
}
.b-faq .faqix{
  grid-column:1;grid-row:1;padding-top:2px;
  font-size:11px;font-weight:600;letter-spacing:.08em;color:var(--faint);
  font-variant-numeric:tabular-nums;
}
.b-faq .faqk{
  grid-column:2;grid-row:1;
  font-size:14px;font-weight:500;letter-spacing:-.014em;color:var(--ink-soft);line-height:1.3;
}

/* the cards */
.b-faq .faqstack{display:grid;gap:clamp(34px,4.4vw,54px)}
.b-faq .faqcard{
  background:var(--card);border:1px solid var(--hair);border-radius:10px;
  padding:clamp(22px,2.7vw,34px);
  /* THE CARD IS THE CONSTRAINT, NOT THE VIEWPORT. .faqin spends a
     fixed 262px on the rail before the card gets anything, so a 768px
     viewport produced a 391px card while the inner visuals' own
     fallbacks did not fire until a 640px VIEWPORT. Declaring the card
     a size container lets those fallbacks key on the box they
     actually live in - see §4b. */
  container-type:inline-size;
  container-name:faqcard;
}
.b-faq .faqck{
  font-weight:300;letter-spacing:-.026em;line-height:1.1;
  font-size:clamp(1.4rem,2.3vw,1.85rem);color:var(--ink);
}
.b-faq .faqcq{margin-top:10px;font-size:12.5px;color:var(--faint);letter-spacing:.01em;line-height:1.45}
.b-faq .faqans{
  margin-top:18px;font-variant-numeric:tabular-nums;
  font-size:clamp(14.5px,1.15vw,16px);letter-spacing:-.008em;
  color:var(--ink);line-height:1.55;max-width:52ch;
}
.b-faq .faqvis{margin-top:22px;padding-top:20px;border-top:1px solid var(--hair)}
/* the card already draws a rule above the visual; the invoice
   drawing its own below it reads as a doubled hairline rather than
   as a table head. One rule, not two. */
.b-faq .faqvis .fq-inv{border-top:0}


/* ------------------------------------------------------------
   2 · the micro-visuals - one per answer.
   Required, not decorative: an FAQ is natively text and a
   text-only section is not read at all (F1/G14), while a panel
   carrying one hairline and a label is still text-only (G35).
   Each of these carries real content.
   Monochrome throughout - the section's one accent is the
   centre-out divider and nothing here spends it again.
   ------------------------------------------------------------ */
.b-faq .fq-v{width:100%}

.b-faq .fq-m{width:8px;height:8px;flex:none;background:var(--ink)}
.b-faq .fq-m-off{background:transparent;border:1px solid var(--hair-strong)}

/* inline brand marks, grayscale per the body logo rule (real
   colour is hero-only). Never hotlinked. */
.b-faq .fq-mk{display:inline-flex;align-items:center;filter:grayscale(1);opacity:.62}
.b-faq .fq-mk svg{display:block;flex:none}
/* Slack's mark is authored fill="currentColor" while the CRM marks
   carry their own brand fills; grayscaled at the same opacity that
   leaves Slack markedly darker than its neighbours. Knock it back
   so the set reads as one. No new colour is introduced. */
.b-faq .fq-mk-soft{color:var(--muted)}

/* --- 01 Fit: the AND-gate --- */
.b-faq .fq-fit{display:grid;grid-template-columns:minmax(0,1fr) 26px auto;align-items:stretch}
.b-faq .fq-fit-set{display:grid;gap:9px;padding:3px 0}
.b-faq .fq-fit-r{display:flex;align-items:center;gap:10px;font-size:13.5px;color:var(--ink-soft);line-height:1.4}
.b-faq .fq-fit-br{margin-left:12px;border:1px solid var(--hair-strong);border-left:0}
.b-faq .fq-fit-out{
  position:relative;align-self:center;margin-left:26px;white-space:nowrap;
  font-size:11.5px;font-weight:500;letter-spacing:.03em;text-transform:uppercase;
  color:var(--card);background:var(--ink);padding:5px 12px;border-radius:999px;
}
.b-faq .fq-fit-out::before{content:"";position:absolute;left:-26px;top:50%;width:26px;height:1px;background:var(--hair-strong)}
.b-faq .fq-fit-out-off{background:transparent;color:var(--muted);border:1px solid var(--hair-strong)}
.b-faq .fq-fit-neg{margin-top:14px;padding-top:14px;border-top:1px solid var(--hair)}

/* --- 02 Pricing: the invoice. Every line a buyer expects to be
   billed for reads $0; the one live line's amount is the proposal,
   never a published rate (F2). "$0 upfront" is a statement about
   WHEN you pay (F3). --- */
.b-faq .fq-inv{border-top:1px solid var(--ink)}
.b-faq .fq-inv-h{display:flex;justify-content:space-between;gap:16px;padding:9px 0;border-bottom:1px solid var(--hair-strong)}
.b-faq .fq-inv-r{
  display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  padding:9px 0;border-bottom:1px solid var(--hair);
  font-size:13.5px;color:var(--muted);line-height:1.4;
}
.b-faq .fq-inv-z{color:var(--faint);font-variant-numeric:tabular-nums}
.b-faq .fq-inv-live{color:var(--ink);font-size:14.5px;font-weight:500;border-bottom:1px solid var(--hair-strong)}
.b-faq .fq-inv-rate{font-size:13px;font-weight:400;color:var(--ink-soft);white-space:nowrap}
.b-faq .fq-inv-f{
  display:flex;justify-content:space-between;gap:16px;padding:10px 0 0;
  font-size:11.5px;color:var(--faint);letter-spacing:.02em;
}

/* --- 03 Why it works: the algorithm. Six numbered steps in the
   spec register, bracketed as one procedure. The bracket is also
   the feedback path: it closes on step 01 with an inked head,
   because step 06 (Data) is what feeds the next pass. Resolves to
   one returned value. The stack ELEMENTS are named; their
   internals (domains, mailboxes, warm-up, placement) are not
   (G15/G16). --- */
.b-faq .fq-alg-h{display:flex;justify-content:space-between;gap:16px;padding-bottom:9px;border-bottom:1px solid var(--hair-strong)}
.b-faq .fq-alg-body{position:relative;margin-top:10px;padding-left:23px}
.b-faq .fq-alg-loop{
  position:absolute;left:0;top:12px;bottom:14px;width:13px;
  border:1px solid var(--ink);border-right:0;
}
.b-faq .fq-alg-loop::after{content:"";position:absolute;right:-3px;top:-3px;width:6px;height:6px;background:var(--ink)}
.b-faq .fq-alg-rows{list-style:none}
.b-faq .fq-alg-r{
  display:grid;grid-template-columns:24px 116px minmax(0,1fr);column-gap:14px;align-items:baseline;
  padding:8px 0;border-bottom:1px solid var(--hair);
}
.b-faq .fq-alg-r:last-child{border-bottom:0}
.b-faq .fq-alg-n{font-size:11px;font-weight:600;letter-spacing:.08em;color:var(--faint);font-variant-numeric:tabular-nums}
.b-faq .fq-alg-k{font-size:13.5px;font-weight:500;letter-spacing:-.012em;color:var(--ink);line-height:1.3}
.b-faq .fq-alg-g{font-size:11.5px;color:var(--faint);letter-spacing:.01em;line-height:1.4}
.b-faq .fq-alg-ret{display:flex;align-items:center;gap:14px;margin-top:4px;padding:13px 0 0 23px;border-top:1px solid var(--ink)}
.b-faq .fq-alg-rk{font-size:11.5px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);flex:none}
.b-faq .fq-alg-rr{flex:1 1 auto;min-width:20px;height:1px;background:var(--hair-strong)}
.b-faq .fq-alg-rv{
  flex:none;white-space:nowrap;
  font-size:11.5px;font-weight:500;letter-spacing:.03em;text-transform:uppercase;
  color:var(--card);background:var(--ink);padding:5px 13px;border-radius:999px;
}

/* --- 04 Data & compliance: regime status on one rule. The three
   terms in force sit on the INKED segment with filled ticks;
   SOC 2 Type II hangs off the DASHED continuation with a hollow
   tick and a knocked-back label. It must never read as complete:
   no badge, no check, no "compliant" styling, and its status word
   is the only one set in --faint. --- */
.b-faq .fq-reg-h{display:flex;justify-content:space-between;gap:16px;padding-bottom:14px}
.b-faq .fq-reg-track{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
.b-faq .fq-reg-g{position:relative;min-width:0;padding-top:21px}
.b-faq .fq-reg-g::before{content:"";position:absolute;left:0;top:0;width:calc(100% + 16px);height:1px;background:var(--ink)}
.b-faq .fq-reg-g::after{content:"";position:absolute;left:0;top:-3px;width:7px;height:7px;background:var(--ink)}
.b-faq .fq-reg-g:last-child::before{
  width:100%;
  background:repeating-linear-gradient(90deg,var(--hair-strong) 0 3px,transparent 3px 7px);
}
.b-faq .fq-reg-g:last-child::after{top:-3px;background:transparent;border:1px solid var(--hair-strong)}
.b-faq .fq-reg-k{
  display:block;font-variant-numeric:tabular-nums;font-size:13px;font-weight:500;
  letter-spacing:-.008em;color:var(--ink);line-height:1.3;
}
/* two lines reserved: "Data processing agreement" wraps and without
   a floor the four status words fall out of alignment, which in a
   spec register reads as a bug. */
.b-faq .fq-reg-s{display:block;margin-top:5px;min-height:32px;font-size:11.5px;color:var(--faint);line-height:1.4}
.b-faq .fq-reg-st{display:block;margin-top:11px;font-size:11.5px;color:var(--muted);letter-spacing:.02em}
.b-faq .fq-reg-open .fq-reg-k{color:var(--muted)}
.b-faq .fq-reg-open .fq-reg-st{color:var(--faint)}

/* --- 05 Integration: the booked call flowing into a 2x2 of the
   customer's own stack, bottom right. The path is continuous end
   to end (G32): the source row's rule fills its own remaining
   width, the box bridges the gutter with its ::before, and they
   meet on the box edge. --- */
.b-faq .fq-into{
  display:grid;grid-template-columns:minmax(0,1fr) 30px auto;grid-template-rows:auto auto;
  row-gap:13px;align-items:center;
}
.b-faq .fq-into-rt{grid-column:3;grid-row:1}
.b-faq .fq-into-src{grid-column:1;grid-row:1 / span 2;min-width:0}
.b-faq .fq-into-lt{display:block;margin-bottom:13px}
.b-faq .fq-into-node{display:flex;align-items:center;gap:12px;min-width:0}
.b-faq .fq-into-dot{width:11px;height:11px;flex:none;background:var(--ink)}
.b-faq .fq-into-k{font-size:13.5px;color:var(--ink);letter-spacing:-.008em;line-height:1.3}
.b-faq .fq-into-run{flex:1 1 auto;min-width:14px;height:1px;background:var(--ink)}
.b-faq .fq-into-box{
  position:relative;grid-column:3;grid-row:2;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  border:1px solid var(--hair-strong);border-radius:6px;
}
/* the flow, in three connected pieces: the run out of the source
   node meets a short lead-in, which meets a vertical bus spanning
   the two rows, which feeds a stub into each row. Nothing points
   at empty space (G32). */
.b-faq .fq-into-box::before{content:"";position:absolute;left:-30px;top:50%;width:17px;height:1px;background:var(--ink)}
.b-faq .fq-into-box::after{content:"";position:absolute;left:-13px;top:25%;bottom:25%;width:1px;background:var(--ink)}
.b-faq .fq-into-c{position:relative;min-width:136px;padding:13px 15px}
.b-faq .fq-into-c:nth-child(1)::before,
.b-faq .fq-into-c:nth-child(3)::before{content:"";position:absolute;left:-13px;top:50%;width:13px;height:1px;background:var(--ink)}
/* the four marks read as one set: two are brand SVGs greyscaled,
   two are currentColor. Same weight for all four in this box. */
.b-faq .fq-into-c .fq-mk{opacity:.74}
.b-faq .fq-into-c .fq-mk-soft{color:var(--ink-soft)}
.b-faq .fq-into-c:nth-child(odd){border-right:1px solid var(--hair)}
.b-faq .fq-into-c:nth-child(-n+2){border-bottom:1px solid var(--hair)}
.b-faq .fq-into-n{display:block;margin-top:9px;font-size:12.5px;font-weight:500;letter-spacing:-.008em;color:var(--ink);line-height:1.3}
.b-faq .fq-into-r{display:block;margin-top:3px;font-size:11px;color:var(--faint);line-height:1.35}

/* --- 06 Existing outbound: two lanes into one pipeline. The
   hollow lane is "Other outbound", which covers an in-house team
   and an agency alike. Both lanes carry the SAME number of marks,
   so no volume or performance claim is made - the argument is
   parallel, not better. --- */
.b-faq .fq-par{display:grid;grid-template-columns:minmax(0,1fr) 24px auto;align-items:stretch}
.b-faq .fq-par-lanes{display:grid;gap:34px}
.b-faq .fq-par-lane{display:grid;grid-template-columns:140px minmax(0,1fr);align-items:center;gap:14px;height:30px}
.b-faq .fq-par-k{font-size:11.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);white-space:nowrap}
.b-faq .fq-par-k-on{color:var(--ink)}
.b-faq .fq-par-rule{position:relative;display:flex;align-items:center;justify-content:space-between;height:10px}
.b-faq .fq-par-rule::before{content:"";position:absolute;left:0;right:0;top:50%;height:1px;background:var(--hair-strong)}
.b-faq .fq-par-rule i{position:relative;width:8px;height:8px;flex:none;background:var(--bg);border:1px solid var(--hair-strong)}
.b-faq .fq-par-on::before{background:var(--ink)}
.b-faq .fq-par-on i{background:var(--ink);border-color:var(--ink)}
.b-faq .fq-par-br{align-self:stretch;margin:15px 0;border:1px solid var(--hair-strong);border-left:0}
.b-faq .fq-par-out{
  position:relative;align-self:center;margin-left:24px;white-space:nowrap;
  font-size:11.5px;font-weight:500;letter-spacing:.03em;text-transform:uppercase;
  color:var(--card);background:var(--ink);padding:6px 13px;border-radius:999px;
}
.b-faq .fq-par-out::before{content:"";position:absolute;left:-24px;top:50%;width:24px;height:1px;background:var(--hair-strong)}


/* ------------------------------------------------------------
   3 · the negation - the FAQ's closing beat, not a new section.

   No eyebrow and no fresh .sec top padding: the gap is on the
   .sec-tight scale so the two beats read as one document.

   THE DIVIDER: one element, full width, painted ONCE with
   var(--spectrum) at its final size and revealed by animating
   clip-path inset(0 50% 0 50%) to inset(0 0 0 0). Both insets
   shrink by the same amount on the same curve in the same frame,
   so symmetry is structural - no seam, no banding, no two halves
   to mismatch, and it lands flush on both viewport edges at any
   width. Lifted from the shipped implementation, not rebuilt.

   THE RESTING STATE: an earlier candidate parked its receded
   lines at opacity .16 and was killed for it (G36). Here the
   lines are --muted rather than --faint and recede only to .78.
   Do not take this lower.
   ------------------------------------------------------------ */
.b-faq .faqneg{margin-top:clamp(52px,7vw,88px)}
.b-faq .faqneg.rv{opacity:1;transform:none;transition:none}

.b-faq .faqnegs{
  list-style:none;
  display:flex;flex-direction:column;gap:clamp(6px,1vw,13px);
}
.b-faq [data-faq-neg]{
  list-style:none;position:relative;width:max-content;max-width:100%;
  font-weight:300;letter-spacing:-.032em;line-height:1.08;
  font-size:clamp(1.4rem,4.2vw,3rem);color:var(--muted);
  transition:opacity .5s ease .92s,transform .5s cubic-bezier(.22,.61,.36,1) .92s;
}
.b-faq [data-faq-neg]::after{
  content:"";position:absolute;left:0;top:54%;height:2px;width:0;
  background:var(--muted);
  transition:width .34s cubic-bezier(.22,.61,.36,1);
}
.b-faq [data-faq-neg]:nth-child(1)::after{transition-delay:.08s}
.b-faq [data-faq-neg]:nth-child(2)::after{transition-delay:.23s}
.b-faq [data-faq-neg]:nth-child(3)::after{transition-delay:.38s}
.b-faq [data-faq-neg]:nth-child(4)::after{transition-delay:.53s}
.b-faq .faqneg.in [data-faq-neg]::after{width:100%}
.b-faq .faqneg.in [data-faq-neg]{opacity:.78;transform:translateY(6px)}

.b-faq [data-faq-div]{
  position:relative;display:block;width:100%;height:2px;
  background:var(--hair);
  margin:clamp(34px,4.4vw,56px) 0;
}
.b-faq [data-faq-bow]{
  position:absolute;inset:0;display:block;background:var(--spectrum);
  -webkit-clip-path:inset(0 50% 0 50%);clip-path:inset(0 50% 0 50%);
  transition:-webkit-clip-path .72s cubic-bezier(.22,.61,.36,1) 1.02s,
             clip-path .72s cubic-bezier(.22,.61,.36,1) 1.02s;
}
.b-faq .faqneg.in [data-faq-bow]{-webkit-clip-path:inset(0 0 0 0);clip-path:inset(0 0 0 0)}

.b-faq [data-faq-pay]{
  opacity:0;transform:translateY(14px);
  transition:opacity .55s ease 1.2s,transform .55s cubic-bezier(.22,.61,.36,1) 1.2s;
}
.b-faq .faqneg.in [data-faq-pay]{opacity:1;transform:none}
.b-faq .faqpay{
  max-width:22ch;font-weight:400;letter-spacing:-.032em;line-height:1.1;
  font-size:clamp(1.6rem,4.6vw,3.2rem);color:var(--ink);
}


/* ------------------------------------------------------------
   4 · narrow viewports
   ------------------------------------------------------------ */
/* MOBILE PASS 2026-07-25 - F5, and the layout half of F4.
   The rail's only job is position indication, and there is no position
   to indicate outside the pinned frame: .is-now / .is-past / .faqprog i
   are all styled inside the pin media query and nowhere else. Below the
   pin gate the rail rendered "CONTENTS" plus the same six labels the
   six cards immediately below repeat verbatim as their own headings -
   ~147px of scroll spent reading the same six words twice, and at 768
   it was 262px of a 691px row, leaving the card 391px. Flat layout
   drops it; the markup stays, because it is the desktop rail.
   Gated on width OR a coarse pointer, so a hybrid touch device wide
   enough to clear 1024 still gets the flat layout it will actually be
   driven with. */
@media (max-width:1023px){
  .b-faq .faqin{grid-template-columns:minmax(0,1fr);gap:26px}
  .b-faq .faqrailcol{display:none}
}
@media (pointer:coarse){
  .b-faq .faqin{grid-template-columns:minmax(0,1fr);gap:26px}
  .b-faq .faqrailcol{display:none}
}
@media (max-width:760px){
  .b-faq .faqstack{gap:30px}
}

/* F6 - "Qualified calls booked" wraps against a one-line amount at
   360, so the invoice's one live row - the row the whole visual points
   at - measured 79.9px against its siblings' 59.6px and was the only
   thing breaking the rhythm. Stacking the label over its amount is
   honest at that width; wrapping the label under a right-aligned
   amount is not. */
@media (max-width:400px){
  .b-faq .fq-inv-live{flex-direction:column;align-items:flex-start;gap:4px}
  .b-faq .fq-inv-rate{white-space:normal}
}

@media (max-width:640px){
  .b-faq .fq-fit{grid-template-columns:minmax(0,1fr) 18px auto}
  .b-faq .fq-fit-out{margin-left:14px}
  .b-faq .fq-fit-out::before{left:-14px;width:14px}

  .b-faq .fq-reg-track{grid-template-columns:minmax(0,1fr);gap:20px}
  .b-faq .fq-reg-g{padding-top:0;padding-left:14px;border-left:1px solid var(--ink)}
  .b-faq .fq-reg-g:last-child{border-left:1px dashed var(--hair-strong)}
  .b-faq .fq-reg-g::before,.b-faq .fq-reg-g::after{display:none}
  .b-faq .fq-reg-s{min-height:0}

  .b-faq .fq-into{grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto auto;row-gap:8px}
  .b-faq .fq-into-src,.b-faq .fq-into-rt,.b-faq .fq-into-box{grid-column:1}
  .b-faq .fq-into-src{grid-row:1;margin-bottom:16px}
  .b-faq .fq-into-rt{grid-row:2;margin-top:4px}
  .b-faq .fq-into-box{grid-row:3}
  .b-faq .fq-into-box::before{left:12px;top:-16px;width:1px;height:16px}
  .b-faq .fq-into-box::after,
  .b-faq .fq-into-c:nth-child(1)::before,
  .b-faq .fq-into-c:nth-child(3)::before{display:none}
  .b-faq .fq-into-c{min-width:0;padding:12px}

  .b-faq .fq-par{grid-template-columns:minmax(0,1fr)}
  .b-faq .fq-par-lanes{gap:22px}
  .b-faq .fq-par-lane{grid-template-columns:minmax(0,1fr);height:auto;gap:8px}
  .b-faq .fq-par-k{font-size:11px;letter-spacing:.08em}
  .b-faq .fq-par-br{display:none}
  .b-faq .fq-par-out{justify-self:start;margin-left:0;margin-top:20px}
  .b-faq .fq-par-out::before{left:0;top:-20px;width:1px;height:20px;background:var(--ink)}
}

@media (max-width:520px){
  /* 24px + 116px + 14px gutters leaves the gloss ~120px at 390 and
     it wraps to three lines against a one-line key. Stack it. */
  .b-faq .fq-alg-r{grid-template-columns:24px minmax(0,1fr);row-gap:3px}
  .b-faq .fq-alg-n{grid-column:1;grid-row:1}
  .b-faq .fq-alg-k{grid-column:2;grid-row:1}
  .b-faq .fq-alg-g{grid-column:2;grid-row:2}

  /* MOBILE PASS 2026-07-25 - F7. The negation stack was 23.01px
     against a 25.74px payoff: a 1.12x gap, so the page's one piece of
     deliberate swagger read as the same voice twice and landed flat.
     Desktop carries the same weak ratio on scale alone (48px / 51.2px);
     at phone size there is no scale to carry it. The stack steps DOWN
     and the payoff steps UP - 18px against 32px, a 1.78x gap - so the
     contrast is size, never weight. "Calls on the calendar." still sets
     on one line: ~318px at 32px/400 against 350px of column at 390, and
     ~292px against 320px at 360. */
  .b-faq [data-faq-neg]{font-size:clamp(1rem,4.6vw,1.25rem)}
  .b-faq .faqpay{font-size:clamp(1.5rem,8.2vw,2.1rem);max-width:100%}
}


/* ------------------------------------------------------------
   4b · card-width queries - the constraint the blocks above miss.

   Everything above is keyed on the VIEWPORT, which is the wrong gate
   for anything inside a fixed-width column. .faqin spends 262px on the
   rail before the card gets anything, so a 768px viewport produced a
   391px card while the fallbacks above did not fire until 640px.
   Measured casualties at 768x1024: .fq-into-src computed to width 0
   (answer 05 lost its source node outright), .fq-par-rule 19px against
   72px of content, .fq-reg-g 72px against 88px.

   A card's inner width is card - 2*padding, so a 520px card is a 476px
   container and a 640px card a ~594px one: 480 and 400 below reproduce
   the 520/640 viewport behaviour exactly on a phone, and additionally
   catch the 391px card at 768 and the 455px card at 844x390.

   The viewport blocks above are deliberately KEPT rather than re-keyed,
   as the fallback for engines without container queries (Chrome <105,
   Safari <16). Where both fire the declarations are identical, so there
   is nothing for the cascade to resolve.
   ------------------------------------------------------------ */
@container faqcard (max-width:480px){
  .b-faq .fq-fit{grid-template-columns:minmax(0,1fr) 18px auto}
  .b-faq .fq-fit-out{margin-left:14px}
  .b-faq .fq-fit-out::before{left:-14px;width:14px}

  .b-faq .fq-reg-track{grid-template-columns:minmax(0,1fr);gap:20px}
  .b-faq .fq-reg-g{padding-top:0;padding-left:14px;border-left:1px solid var(--ink)}
  .b-faq .fq-reg-g:last-child{border-left:1px dashed var(--hair-strong)}
  .b-faq .fq-reg-g::before,.b-faq .fq-reg-g::after{display:none}
  .b-faq .fq-reg-s{min-height:0}

  .b-faq .fq-into{grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto auto;row-gap:8px}
  .b-faq .fq-into-src,.b-faq .fq-into-rt,.b-faq .fq-into-box{grid-column:1}
  .b-faq .fq-into-src{grid-row:1;margin-bottom:16px}
  .b-faq .fq-into-rt{grid-row:2;margin-top:4px}
  .b-faq .fq-into-box{grid-row:3}
  .b-faq .fq-into-box::before{left:12px;top:-16px;width:1px;height:16px}
  .b-faq .fq-into-box::after,
  .b-faq .fq-into-c:nth-child(1)::before,
  .b-faq .fq-into-c:nth-child(3)::before{display:none}
  .b-faq .fq-into-c{min-width:0;padding:12px}

  .b-faq .fq-par{grid-template-columns:minmax(0,1fr)}
  .b-faq .fq-par-lanes{gap:22px}
  .b-faq .fq-par-lane{grid-template-columns:minmax(0,1fr);height:auto;gap:8px}
  .b-faq .fq-par-k{font-size:11px;letter-spacing:.08em}
  .b-faq .fq-par-br{display:none}
  .b-faq .fq-par-out{justify-self:start;margin-left:0;margin-top:20px}
  .b-faq .fq-par-out::before{left:0;top:-20px;width:1px;height:20px;background:var(--ink)}
}

@container faqcard (max-width:400px){
  .b-faq .fq-alg-r{grid-template-columns:24px minmax(0,1fr);row-gap:3px}
  .b-faq .fq-alg-n{grid-column:1;grid-row:1}
  .b-faq .fq-alg-k{grid-column:2;grid-row:1}
  .b-faq .fq-alg-g{grid-column:2;grid-row:2}
}


/* ------------------------------------------------------------
   5 · the pinned frame - the only motion in beat 1.

   360vh of track over 6 answers = 60vh each. The founder cut this
   from 580vh: "Scrolling through the cards a touch too long,
   feels like I get trapped."

   Everything below is doubly gated - this media query AND the
   .is-pinned class s6.js only adds once it has verified that the
   frame actually computes to position:sticky.

   MOBILE PASS 2026-07-25 - F4. NEVER PIN ON A TOUCH DEVICE. Momentum
   scrolling makes a sticky track either a trap (a flick that lands
   mid-track) or a skip (a flick that clears the whole track in one
   gesture), and there is no tuning that fixes both. At 768x1024 the
   old gate matched and built 3,686px of sticky track driving six
   panels through a 391px card next to a 262px rail. (pointer:fine)
   and a 1024 floor disable it on every phone and touch tablet at
   once. The height floor moves 641 -> 700 for the same reason the
   track is budgeted by content: 60vh per answer is one comfortable
   flick at 1024px tall and two answers per gesture under ~700px.
   pinDrive() in body.js needs no change - it already reads the
   computed position and calls flat() when this query does not match. */
@media (min-width:1024px) and (min-height:700px) and (pointer:fine) and (prefers-reduced-motion:no-preference){
  .b-faq .faqtrack.is-pinned{height:360vh}
  .b-faq .faqtrack.is-pinned .faqframe{
    /* align-items:flex-start, not center: a 600px card centred in a 100vh
       frame parks ~300px of empty page above itself, which is what read as
       "giant whitespace" on entering the section. Starting it high and
       letting the bottom padding absorb the slack puts the card where the
       reader is already looking. */
    position:sticky;top:0;height:100vh;display:flex;align-items:flex-start;
    padding:clamp(96px,13vh,150px) 0 clamp(28px,5vh,54px);
  }
  .b-faq .faqtrack.is-pinned .faqin{width:100%;align-items:center}
  .b-faq .faqtrack.is-pinned .faqprog i{
    transform:scaleY(var(--pv,.1667));
    transition:transform .42s cubic-bezier(.22,.61,.36,1);
  }
  .b-faq .faqtrack.is-pinned .faqk{color:var(--faint);transition:color .3s ease}
  .b-faq .faqtrack.is-pinned .faqitem.is-past .faqk{color:var(--muted)}
  .b-faq .faqtrack.is-pinned .faqitem.is-now .faqk{color:var(--ink)}
  .b-faq .faqtrack.is-pinned .faqitem.is-now::before{
    content:"";position:absolute;left:-23px;top:13px;width:7px;height:7px;background:var(--ink);
  }

  /* G4/G34: the card has to CARRY the pinned frame, not float in
     it. One min-height for all six panels also stops the card
     resizing between answers, which reads as a layout bug when it
     happens every 60vh. */
  .b-faq .faqtrack.is-pinned .faqstack{display:grid;align-content:center;gap:0}
  .b-faq .faqtrack.is-pinned .faqcard{
    min-height:min(64vh,600px);display:flex;flex-direction:column;justify-content:center;
    padding:clamp(26px,3.2vw,40px);
  }
  .b-faq .faqtrack.is-pinned .faqck{font-size:clamp(1.5rem,2.5vw,2rem)}
  .b-faq .faqtrack.is-pinned .faqans{margin-top:20px;font-size:clamp(15px,1.25vw,16.5px)}
  .b-faq .faqtrack.is-pinned .faqvis{margin-top:26px;padding-top:24px}
  .b-faq .faqtrack.is-pinned .fq-inv-h,
  .b-faq .faqtrack.is-pinned .fq-inv-r{padding:11px 0}
  .b-faq .faqtrack.is-pinned .fq-fit-set{gap:12px}
  .b-faq .faqtrack.is-pinned .fq-par-lanes{gap:38px}
  .b-faq .faqtrack.is-pinned .fq-alg-r{padding:9px 0}
  .b-faq .faqtrack.is-pinned .fq-reg-track{padding-top:2px}

  .b-faq .faqtrack.is-pinned .faqpanel{
    grid-area:1 / 1;opacity:0;transform:translateY(20px);pointer-events:none;
    transition:opacity .34s ease,transform .4s cubic-bezier(.22,.61,.36,1);
  }
  .b-faq .faqtrack.is-pinned .faqpanel.is-prev{transform:translateY(-20px)}
  .b-faq .faqtrack.is-pinned .faqpanel.is-now{opacity:1;transform:none;pointer-events:auto}
}


/* ------------------------------------------------------------
   6 · reduced motion - everything resolves to its end state and
   nothing moves. The pinning block above is already excluded by
   its own media query, so beat 1 is six stacked cards.
   ------------------------------------------------------------ */
@media (prefers-reduced-motion:reduce){
  .b-faq [data-faq-neg],.b-faq [data-faq-neg]::after,
  .b-faq [data-faq-bow],.b-faq [data-faq-pay]{transition:none}
  .b-faq [data-faq-neg]::after{width:100%}
  .b-faq [data-faq-neg]{opacity:.78;transform:none}
  .b-faq [data-faq-bow]{-webkit-clip-path:inset(0 0 0 0);clip-path:inset(0 0 0 0)}
  .b-faq [data-faq-pay]{opacity:1;transform:none}
}

/* ======================================================================
   THE CLOSE + FOOTER
   ====================================================================== */
/* ============================================================
   §7 · .b-close / .b-foot - "Buy distribution." then the footer

   REPLACES the existing §7 block in website/assets/css/body.css
   (everything from ".b-close{" to the end of the file).
   Adds no new custom properties: every value below resolves from
   the :root token block in hero.css.

   Full-bleed with zero radius on purpose: an inset or rounded card
   reads as a component sitting on the page, and this has to read as
   the page ending. No spectrum on the band. The footer returns to
   the near-white canvas so the dark is a band the page passes
   through, not the colour it ends on.

   Founder, 2026-07-25: "'Buy distribution' closing banner text is a
   bit too big and heavy. Maybe mirror the weight and size from the
   hero header." So the statement now uses the hero h1 metrics
   verbatim - weight 300, clamp(2.7rem,7.4vw,5.75rem), -.032em -
   instead of the old 6rem at weight 600. Weight 300 is also what
   lets the band lose height without feeling cramped.

   QA round 2 (s8-close) fixed here:
   1. the band was 579px tall with ~200px of content in the middle.
      Vertical padding and the statement-to-button gap are retuned so
      the strip lands at ~405px at 1440 and ~250px at 390.
   2. the hand-built C mark plus a ~24px gradient dash sampled one
      band of the spectrum and rendered as a blue line with an orange
      speck. The footer uses the real control-icon.svg at 30px (G24).
   3. the social marks sat on grey rounded tiles. Brand invariant is
      that logos render with no background tile, so the tiles are gone
      and the hit area is padding, not a visible box.
   ============================================================ */
.b-close{
  background:var(--ink);
  padding:clamp(60px,7.5vw,104px) 0;
}
.b-close .wrap{display:flex;flex-direction:column;align-items:center;text-align:center}

/* mirrors hero.css h1 exactly, on the inverted ground */
.b-close h2{
  color:#FBFAF9;
  /* founder, 2026-07-25: "a touch more weight". Still mirrors the hero's size
     and tracking; 400 against the hero's 300 is one step, not a jump to 600. */
  font-weight:400;
  font-size:clamp(2.7rem,7.4vw,5.75rem);
  line-height:1.02;
  letter-spacing:-.032em;
}
.b-close .btn{margin-top:clamp(34px,4.2vw,52px)}
.b-close .btn:focus-visible{outline:2px solid #FBFAF9;outline-offset:3px}


/* ------------------------------------------------------------
   footer - white ground, four link columns.

   The nav-less-lander research argues a fat footer on a one-page
   site advertises the sub-pages you do not have. Founder overruled
   it on 2026-07-25: it has to look enterprise. The compromise is
   discipline rather than absence - four tight columns, 13.5px
   muted links, one hairline per zone, nothing decorative.

   Only /demo, /privacy and /terms resolve. Everything else is
   href="#" with a data-inert hook; body.js already suppresses
   `.b-foot a[href="#"]` centrally.
   ------------------------------------------------------------ */
.b-foot{
  background:var(--bg);
  padding:clamp(48px,6vw,72px) 0 clamp(30px,3.6vw,42px);
}

/* --- zone 1: link columns ---
   hero.css styles the bare element selector `nav` as the 80px-tall
   top bar (flex, space-between, its own padding, z-index:5). A
   <nav> in the footer inherits all of it, which collapsed the
   columns into an 80px flex row that overlapped the identity block.
   Every one of those properties is reset explicitly below. */
.b-foot .fnav{
  display:grid;
  grid-template-columns:repeat(5,minmax(0,1fr));
  column-gap:20px;row-gap:34px;
  height:auto;padding:0;position:static;z-index:auto;
  align-items:start;justify-content:normal;
}
.b-foot .fcol h3{margin-bottom:14px}
.b-foot .fcol ul{list-style:none}
.b-foot .fcol li + li{margin-top:9px}
.b-foot .fcol a{
  font-size:13.5px;letter-spacing:-.005em;line-height:1.45;
  color:var(--muted);text-decoration:none;
  transition:color .18s ease;
}
.b-foot .fcol a:hover{color:var(--ink)}
.b-foot .fcol a:focus-visible{outline:2px solid var(--ink);outline-offset:3px;border-radius:2px}

/* --- zone 2: identity + social --- */
.b-foot .fbase{
  margin-top:clamp(40px,5vw,60px);
  border-top:1px solid var(--hair);
  padding-top:clamp(24px,3vw,32px);
  display:flex;align-items:flex-start;justify-content:space-between;gap:24px;
}
.b-foot .fid{display:flex;align-items:flex-start;gap:14px}
.b-foot .fmark{display:block;height:30px;width:auto;flex:none}
.b-foot .fidtext{display:flex;flex-direction:column;gap:4px;padding-top:1px}
.b-foot .fname{font-size:13.5px;font-weight:500;letter-spacing:-.01em;color:var(--ink)}
.b-foot .faddr{
  font-size:12px;line-height:1.5;color:var(--faint);letter-spacing:.01em;
  font-variant-numeric:tabular-nums;
}

/* no tile behind the marks - the padding is the hit area, not a box.
   No negative margin here on purpose: pulling the row 8px past the
   column edge made .fbase report scrollWidth 1040 against clientWidth
   1032, which is exactly the per-element overflow G28 asks you to
   check for. The 8px optical inset is invisible; the overflow is not. */
.b-foot .fsoc{display:flex;align-items:center;gap:14px}
.b-foot .fsoc a{
  display:inline-flex;align-items:center;justify-content:center;
  padding:8px;color:var(--muted);text-decoration:none;
  transition:color .18s ease;
}
.b-foot .fsoc a:hover{color:var(--ink)}
.b-foot .fsoc a:focus-visible{outline:2px solid var(--ink);outline-offset:0;border-radius:var(--radius)}

/* --- zone 3: copyright + the quiet legal footnote --- */
.b-foot .fbot{
  margin-top:clamp(24px,3vw,32px);
  border-top:1px solid var(--hair);
  padding-top:clamp(18px,2.2vw,24px);
}
.b-foot .fmeta{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  font-size:12.5px;color:var(--faint);letter-spacing:.01em;
}
.b-foot .fdisc{
  margin-top:12px;max-width:64ch;
  font-size:11.5px;line-height:1.55;letter-spacing:.02em;
  font-style:italic;color:var(--faint);
}

/* --- responsive --- */
/* four columns hold down to ~700px: the longest label
   ("Customer Stories") is ~105px against a ~139px track there. */
@media (max-width:700px){
  .b-foot .fnav{grid-template-columns:repeat(2,minmax(0,1fr));row-gap:32px}
}
@media (max-width:560px){
  .b-close h2{letter-spacing:-.028em}
  .b-foot .faddr{font-size:11.5px}
  .b-foot .fbase{flex-direction:column;align-items:flex-start;gap:22px}
  .b-foot .fmeta{flex-direction:column;align-items:flex-start;gap:8px}
}

/* MOBILE PASS 2026-07-25 - C1 / C2, the tap-target floor.
   Nineteen links at 37.8 x 16px with a 9px gap is a 25px pitch: you
   cannot reliably hit "Privacy Policy" without hitting "Terms of
   Service", and those are two of the only three links in this footer
   that resolve at all. An inline <a> cannot be given a hit area, so
   the link becomes a block and the row gap becomes its padding -
   16.2 + 30 = 46px, comfortably over the 44px floor, and adjacent
   targets no longer share a boundary the finger has to split.
   The fat footer is kept WHOLE (playbook option A): the founder
   overruled the research to get an enterprise footer, so every column
   survives and the footer pays for it in height. That is free at the
   end of a page - it costs ~320px of scroll that nobody has to read.
   The h3 gives back most of its margin because the first link now
   brings its own 15px.
   Gated on width OR a coarse pointer so a touch tablet gets it too. */
@media (max-width:700px),(pointer:coarse){
  .b-foot .fcol h3{margin-bottom:2px}
  .b-foot .fcol li + li{margin-top:0}
  .b-foot .fcol a{display:block;padding:15px 0;line-height:1.2}
  /* 17px and 16px marks: 14px of padding puts both at 44-45px without
     a visible box - the padding is the hit area, never a tile. The gap
     comes down by the amount the padding went up, so the optical
     spacing between the two marks is unchanged. */
  .b-foot .fsoc{gap:6px}
  .b-foot .fsoc a{padding:14px}
}


/* ======================================================================
   MOBILE PASS 2026-07-25 - the two cross-cutting rules.
   Every selector below still carries its own section prefix or is a
   body primitive; nothing new is defined.
   ====================================================================== */

/* G4 - .sec{padding:clamp(76px,10vw,132px) 0} floors at 76px, so on a
   390px phone the section boundaries alone are 76 x 16 = 1,216px, 11%
   of the page, and the longest empty band measured anywhere in the
   body is a .sec top pad. 56px recovers ~320px of scroll and takes air
   from no object - it is the gap BETWEEN sections, not inside one. */
@media (max-width:560px){
  .sec{padding:56px 0}
}

/* G1 - the contrast floor. THE BLOCKER.
   --faint #97979C is 2.84:1 on --bg #FDFCFC, below even the 3:1
   large-text floor. On a 27-inch monitor that reads as "quiet"; at
   11-11.5px on a phone in daylight it reads as nothing - and it is
   carrying the legal disclaimer, the model's assumptions, the caption
   that stops the ROAS chart looking like a rendering bug, and the
   register's entire drained end state.
   Every selector below is small type set in --faint. They move to
   --muted (3.56:1). NO new token and no new colour: this only
   re-assigns which existing token small text uses.
   NOT touched: the 13.5px --muted body voice, which also fails AA at
   3.56:1 but is the brand's speaking volume and a founder call
   (playbook open decision 5.1), and the 12.5px --faint on .faqcq and
   .fmeta, which sit on the boundary rather than under it.
   Two --faint uses are deliberately EXCLUDED because there the token
   carries meaning rather than quiet: .fq-reg-open (SOC 2 Type II must
   never read as complete - its status word is the only one in --faint)
   and .fq-inv-z (the $0 lines against their --muted labels). */
@media (max-width:760px){
  .eyebrow,.tiny,.label-u,
  .s-rm6 .rm6-l,
  .b-steps .idx,.b-steps .pcap,.b-steps .tamkey,.b-steps .tamunit,
  .b-steps .calhead span,
  .b-roas .mlab,
  .b-faq .faqix,.b-faq .fq-inv-f,.b-faq .fq-alg-n,.b-faq .fq-alg-g,
  .b-faq .fq-alg-rk,.b-faq .fq-reg-s,.b-faq .fq-into-r,.b-faq .fq-par-k,
  .b-foot .faddr,.b-foot .fdisc{color:var(--muted)}

  /* O2 - the end state the reader is actually left with once the wash
     drains. The rule it overrides says "legible, handled not gone";
     at 11px and 2.84:1 it was not. --muted keeps the drained/loaded
     distinction intact (3.56:1 against --ink-soft's 9.26:1) without
     the drained copy falling off the page. */
  .s-o26 .unboxed.armed .register[data-o26-base] span{color:var(--muted)}
}
