
    :root {
      --draft-shadow-rest: 0 1px 1px var(--hairline-paper), 0 16px 38px -26px var(--brass-hair);
      --draft-shadow-lift: 0 1px 1px var(--hairline-paper), 0 22px 48px -26px var(--brass-hair);
    }

    html {
      min-width: 320px;
      min-height: 100%;
      overflow-x: hidden;
      overflow-x: clip;
      scroll-behavior: smooth;
      scroll-padding-top: 4rem;
      scroll-snap-type: y proximity;
    }

    body {
      min-width: 320px;
      min-height: 100%;
      overflow-x: hidden;
      overflow-x: clip;
      overflow-wrap: break-word;
      background: var(--paper);
      color: var(--ink);
      font-size: 17px;
    }

    h1,
    h2,
    h3,
    p,
    ul,
    ol,
    figure {
      margin: 0;
    }

    ul {
      padding: 0;
      list-style: none;
    }

    a {
      color: inherit;
      text-decoration: none;
    }

    canvas {
      display: block;
    }

    .site-main {
      overflow: clip;
    }

    /* ========================================================
       SECTION 1 — APPROVED H13 HERO
       ======================================================== */
    .sphere-uniform-hero {
      position: relative;
      min-height: 100vh;
      min-height: 100svh;
      overflow: clip;
      scroll-snap-align: start;
      isolation: isolate;
      background: var(--paper);
      color: var(--ink);
    }

    /* Full width on the page inset, not a centred 1440 shell. The wordmark has
       to start on the same vertical as the headline under it, and the headline
       now starts at --page-inset; a centred shell put the two in the same place
       only on a 1440 screen and drifted apart on every wider one. */
    .sphere-uniform-header {
      position: absolute;
      z-index: 4;
      top: 0;
      left: 0;
      display: grid;
      grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
      align-items: center;
      width: 100%;
      padding: 28px var(--page-inset) 0;
    }

    .sphere-uniform-header__brand {
      justify-self: start;
      text-decoration: none;
    }

    /* Can's ruling 2026-09-02: the site bar sits over the hero from the first
       frame and carries the wordmark, so the hero's own is hidden rather than
       doubled. visibility, not display: the header keeps its height and the
       hero's layout does not move. Script on only: without script the bar
       and the wordmark are both simply there, and no authored word is hidden. */
    html:not(.no-js) .sphere-uniform-header__brand { visibility: hidden; }

    .sphere-uniform-header__balance {
      grid-column: 3;
      min-width: 1px;
    }

    /* Full-bleed line field behind the hero. The canvas is transparent and
       cleared each frame rather than repainted with paper, so the sphere reads
       through it; a destination-out gradient then erases line ink around the
       sphere so the two fields never turn to mud where they overlap. */
    .hero-lines {
      position: absolute;
      z-index: var(--hero-line-z, 0);
      inset: 0;
      overflow: hidden;
      overflow: clip;
      contain: strict;
      pointer-events: none;
    }

    .hero-lines canvas {
      position: absolute;
      inset: 0;
      display: block;
      width: 100%;
      height: 100%;
      mix-blend-mode: var(--hero-line-blend, normal);
      pointer-events: none;
    }

    /* Optional paper wash behind the headline, off by default. CSS rather than
       canvas: it costs nothing per frame. */
    .hero-lines::after {
      position: absolute;
      z-index: 1;
      inset: 0;
      opacity: var(--hero-copy-wash, 0);
      pointer-events: none;
      background: radial-gradient(
        ellipse 56% 62% at 13% 52%,
        color-mix(in srgb, var(--paper) 97%, transparent) 0 26%,
        color-mix(in srgb, var(--paper) 88%, transparent) 46%,
        color-mix(in srgb, var(--paper) 52%, transparent) 66%,
        color-mix(in srgb, var(--paper) 0%, transparent) 84%
      );
      content: "";
    }

    .sphere-uniform-inner {
      position: relative;
      z-index: 1;
      display: grid;
      grid-template-columns: minmax(460px, 1.02fr) minmax(0, 0.98fr);
      align-items: center;
      gap: clamp(36px, 4.5vw, 76px);
      width: 100%;
      /* Capped since 2026-09-03, Can's ruling on an ultra wide screen: past
         a point the copy and the sphere were drifting to opposite edges of
         the room. Up to 2400 nothing changes; wider than that the whole
         composition sits centred in a 2400 shell and the sphere runs off
         the shell's edge instead of the screen's. */
      max-width: 2400px;
      min-height: 100vh;
      min-height: 100svh;
      margin: 0 auto;
      /* Left inset only — the sphere is meant to run off the right edge, so the
         grid has no right padding to give it. The left used to resolve to a
         centred 1440 shell, which is what put 386px of nothing to the left of
         "Meet Aita." on a 2120 screen and 606px on a 2560. --page-inset holds
         it at a tenth of the screen instead, at every width. */
      padding: 116px 0 58px var(--page-inset);
    }

    .sphere-uniform-copy {
      position: relative;
      z-index: 2;
      /* 720, up from 660 on 2026-08-20. The lede is three authored lines now and
         the middle one, "manage AI employees for your business.", has to hold
         unwrapped at every desktop width. It needs about 590px at the size
         below, and 660 left it wrapping at 1366 and 1440. */
      max-width: 720px;
      padding-top: 8px;
    }

    .sphere-uniform-title {
      max-width: none;
      margin: 0;
      font-family: var(--display);
      /* Founder pick, 2026-08-19, chosen in lab/hero-copy against the real
         sphere and lockup and against section 2 beat 1. The ceiling is reached
         at 966px, so every desktop width sets this at 102.4px — deliberately
         fixed rather than fluid, because the copy column it wraps in is itself
         capped at 660px and has nothing left to gain above that.

         The tracking replaces 0.018em, which was set for Bodoni and kept for
         Gambetta; A7 moved this heading to Satoshi on 2026-08-18 and 0.004em
         is what section 2 sets Satoshi at. The leading comes off 0.94 for the
         same reason — 0.94 was a Bodoni-at-display-size value that survived
         two typeface changes untouched.

         Against section 2's 67.3px H2 this lands at 1.52x, inside the 1.5-2.2x
         band where the two read as one system. */
      /* Fluid below 1552 since 2026-09-03, Can's ruling: the headline is
         three authored lines and must never break to four. At the fixed
         102.4px "Your managed" is 635px, and the copy column falls under
         that from about 1550 down, so the size now tracks the viewport at
         6.6vw, which keeps that line inside its column at every width the
         two column layout runs. Above 1552 nothing changes. */
      font-size: clamp(4rem, 5.6vw, 6.4rem);
      font-weight: var(--display-weight);
      letter-spacing: 0.004em;
      line-height: 1.02;
    }

    .sphere-uniform-title__line {
      display: block;
    }

    /* ── The light on "managed" ──────────────────────────────────────────
       Can's pick of 2026-09-04 out of lab/hero-text/v3.html. The word the
       headline turns on carries the light the buttons carry: a band of
       light crosses it once on load, lighting the fill and the letter
       outlines together, each letter turning brass as the band reaches it,
       and then a slow chase stays on the outlines for as long as the page
       is open.

       Everything here is paint. The three SVG layers and the seven letter
       outlines in them are built by assets/hero-word.js, which also runs
       the chase and the per letter colour; this file owns the band, which
       is one sweep of a gradient masked to the letters, and the same sweep
       masking the outline layer so the two light together.

       The word itself is never repainted. It is the page's own text in the
       page's own ink under all of it, so with the script absent, or the
       outlines failing to measure, the headline is what it always was.

       THE EDGE comes down with the type. At the headline's full size a
       1.6px outline is the weight Can picked; on a phone, where the type
       is at its 4rem floor, the same line reads heavy, and he settled on
       0.9 there. The tablet step between them keeps the two ends honest. */
    .hero-word {
      position: relative;
      display: inline-block;
      --hw-run: 1400ms;
      --hw-band: 14%;
      --hw-light: color-mix(in srgb, #fff 35%, var(--brass));
      --hw-glow: 8px;
      --hw-edge: 1.6;
    }

    @media (max-width: 900px) { .hero-word { --hw-edge: 1.1; } }
    @media (max-width: 560px) { .hero-word { --hw-edge: 0.9; } }

    .hero-word__fills,
    .hero-word__band,
    .hero-word__edges {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      overflow: visible;
      pointer-events: none;
    }

    .hero-word__fills { z-index: 1; filter: drop-shadow(0 0 var(--hw-glow) var(--hw-light)); }
    .hero-word__band { z-index: 2; opacity: 0; filter: drop-shadow(0 0 var(--hw-glow) color-mix(in srgb, var(--hw-light) 70%, transparent)); }
    .hero-word__edges { z-index: 3; filter: drop-shadow(0 0 calc(var(--hw-glow) * 0.5) var(--hw-light)); }

    .hero-word__fill { fill: var(--brass); opacity: 0; }
    .hero-word__lit { fill: var(--hw-light); }
    .hero-word__edge {
      fill: none;
      stroke: var(--hw-light);
      stroke-width: var(--hw-edge);
      stroke-linecap: round;
      opacity: 0;
    }

    /* The band. Its centre travels from a word and a bit off the left to
       the same off the right, which is what the script's per letter delays
       are measured against: change the 130% or the -30% here and the
       colour stops arriving with the light. */
    .hero-word.is-arriving .hero-word__band,
    .hero-word.is-arriving .hero-word__edges {
      -webkit-mask-image: linear-gradient(
        100deg,
        transparent 0%,
        transparent calc(50% - var(--hw-band)),
        #000 50%,
        transparent calc(50% + var(--hw-band)),
        transparent 100%
      );
      mask-image: linear-gradient(
        100deg,
        transparent 0%,
        transparent calc(50% - var(--hw-band)),
        #000 50%,
        transparent calc(50% + var(--hw-band)),
        transparent 100%
      );
      -webkit-mask-size: 300% 100%;
      mask-size: 300% 100%;
      -webkit-mask-repeat: no-repeat;
      mask-repeat: no-repeat;
      animation: hero-word-band var(--hw-run) linear 1 forwards;
    }

    .hero-word.is-arriving .hero-word__band { opacity: 1; }
    .hero-word.is-arriving .hero-word__edge { opacity: 1; }

    @keyframes hero-word-band {
      from { -webkit-mask-position: 130% 0; mask-position: 130% 0; }
      to   { -webkit-mask-position: -30% 0; mask-position: -30% 0; }
    }

    /* No light for a reader who asked for none. The script mounts nothing
       under reduced motion and takes its layers away if the setting flips
       while the page is open; this is the belt to that pair of braces. */
    html[data-reduced-motion="true"] .hero-word__fills,
    html[data-reduced-motion="true"] .hero-word__band,
    html[data-reduced-motion="true"] .hero-word__edges { display: none; }

    @media (prefers-reduced-motion: reduce) {
      .hero-word__fills,
      .hero-word__band,
      .hero-word__edges { display: none; }
    }

    .sphere-uniform-lede {
      /* No measure of its own since 2026-08-20. All three lines are authored
         in the markup and none of them may wrap, so the only cap that should
         apply is the copy column's, which is sized for the longest of them.
         The old 24ch broke the middle line in two and left the lede reading
         four lines deep under a three line headline.

         2rem at the top, up from a flat 1.9: Can asked for it a little larger,
         and the wider column pays for it. Below about 1390 it scales with the
         viewport instead, because the column narrows faster than the type
         would. Every width from 1000 up is measured in qa/hero-lede-probe.mjs. */
      max-width: none;
      margin-top: clamp(1.65rem, 3.5vh, 2.25rem);
      color: var(--ink-soft);
      /* 2vw since 2026-09-03, with the headline's move to 5.6vw: the lede
         was at its 2rem cap from 1390 up and read heavy beside the smaller
         headline on a laptop. The cap is now reached at 1600. */
      font-size: clamp(1.25rem, 2vw, 2rem);
      line-height: 1.54;
    }

    /* One sentence per line, written rather than left to wrapping. */
    .sphere-uniform-lede__line { display: block; }

    /* "genius" in the accent, Can's ask 2026-09-03. Colour, not italic: the
       type rules say emphasis is one or the other, never both. */
    .sphere-uniform-lede em { color: var(--brass); font-style: normal; }

    .sphere-uniform-visual {
      position: relative;
      display: grid;
      place-items: center;
      align-self: stretch;
      justify-self: stretch;
      width: auto;
      min-width: 0;
      overflow: visible;
      pointer-events: none;
    }

    .sphere-uniform-stage {
      --hero-stage-scale: 1.06;
      --hero-offset-x: -24%;
      --hero-offset-y: 0%;
      --hero-mark-size: 294px;
      position: absolute;
      top: 50%;
      right: 0;
      /* 56vw with a 1020 cap since 2026-09-03, Can's laptop round. The old
         clamp(800px, 65vw, 1020px) was 1081px at every width from 1570 up,
         which at 1570 is the copy column and then some. Fluid, the sphere
         is 932px at 1570 and 855 at 1440, and with the stronger push right
         below it sits clear of the headline at every two column width; the
         cap is reached at 1821, so the wide layout Can approved is where it
         was. Sizes above 2000 change only in the push. */
      width: calc(clamp(600px, 56vw, 1020px) * var(--hero-stage-scale));
      aspect-ratio: 1;
      /* The translate property composes with the media-query transforms,
         so offsets survive every breakpoint's own translate(). */
      translate: var(--hero-offset-x) var(--hero-offset-y);
      /* 34% against the panel's -24% offset is a net tenth of the stage
         off the right edge. Can: "I don't mind if some portion is out of
         the right side." */
      transform: translate(34%, -50%);
    }

    .sphere-uniform-field {
      position: absolute;
      inset: 0;
      display: grid;
      place-items: center;
      width: 100%;
      max-width: none;
      aspect-ratio: 1;
      contain: layout paint style;
    }

    .sphere-uniform-field:empty::before {
      content: "";
      display: block;
      width: 58%;
      aspect-ratio: 1;
      border: 1px solid var(--brass-hair);
      border-radius: 50%;
      background:
        radial-gradient(circle at 50% 50%, var(--brass) 0 2px, transparent 2.5px),
        radial-gradient(circle at 50% 50%, transparent 0 42%, var(--brass-wash) 42.5% 43.5%, transparent 44%);
    }

    .sphere-uniform-field .aita-spiral__svg {
      display: block;
      width: 100%;
      height: 100%;
      overflow: hidden;
      shape-rendering: geometricPrecision;
    }

    .sphere-uniform-anchor {
      position: absolute;
      z-index: 3;
      top: 50%;
      left: 50%;
      display: grid;
      place-items: center;
      width: var(--hero-mark-size);
      aspect-ratio: 1;
      transform: translate(-50%, -50%);
    }

    .sphere-uniform-anchor .sphere-uniform-mark__text {
      color: var(--ink);
      font-size: 68px;
      white-space: nowrap;
    }

    .sphere-uniform-anchor .sphere-uniform-mark__logo {
      display: block;
      width: 78%;
      height: auto;
      overflow: visible;
      color: var(--ink);
      fill: currentColor;
    }

    /* ========================================================
       CAPABILITY CALLOUTS — script-mounted, ships in production.
       A dot lights up and names something Aita can be, do, know
       or reach. Never mounted under reduced motion.
       ======================================================== */
    /* Body level, not inside the stage: label text nested in a content section
       would show up in that section's textContent only when JS runs, which is
       exactly what the no-JS gate compares.

       DOCUMENT coordinates since 2026-09-03, not fixed. Fixed pills had to be
       re-anchored by script on every frame of a scroll, and on a phone that
       is always a frame behind the page: Can saw them "having a hard time
       catching up". Absolute at body level, the layer scrolls with the page
       on the compositor and the script only moves a pill for the sphere's
       own turning. Zero height and overflow visible: the pills are placed
       well inside the field by the fit check, so nothing here can widen or
       lengthen the page. */
    .hero-callouts {
      position: absolute;
      z-index: 5;
      top: 0;
      left: 0;
      width: 100%;
      height: 0;
      overflow: visible;
      pointer-events: none;
    }

    /* Zero-size anchor parked on the tracked dot; both children hang off it. */
    .hero-callout {
      position: absolute;
      top: 0;
      left: 0;
      width: 0;
      height: 0;
      opacity: 0;
    }

    /* The one dot: the grown marker standing in for the engine's own circle.
       There is no second dot inside the pill — the labels are monochrome.
       Can ruled 2026-09-03, after seeing coloured dots with a legend and
       three ways of naming the business area on the pill: no departments,
       no colour, the label alone. */
    .hero-callout__dot {
      position: absolute;
      top: 0;
      left: 0;
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: var(--brass);
      box-shadow:
        0 0 0 1px color-mix(in srgb, var(--brass) 40%, transparent),
        0 0 14px 3px color-mix(in srgb, var(--brass) 26%, transparent);
      translate: -50% -50%;
    }

    .hero-callout__pill {
      position: absolute;
      top: 0;
      left: 15px;
      padding: 5px 11px;
      border: 1px solid var(--hairline-paper);
      border-radius: 999px;
      background: color-mix(in srgb, var(--paper) 82%, transparent);
      backdrop-filter: blur(10px);
      color: var(--ink);
      font-family: var(--mono);
      font-size: 12px;
      line-height: 1.35;
      white-space: nowrap;
      translate: 0 -50%;
    }

    .hero-callout[data-side="left"] .hero-callout__pill {
      right: 15px;
      left: auto;
    }

    /* ========================================================
       LOCAL TUNE PANEL — mounted by script on localhost only.
       Copied from lab/hero/h15-torus.html so both read the same.
       ======================================================== */
    .lab-tools {
      --lab-inset: 16px;
      position: fixed;
      z-index: 20;
      bottom: var(--lab-inset);
      left: var(--lab-inset);
      display: grid;
      gap: 8px;
      justify-items: start;
      color: var(--ink);
      font-family: var(--sans);
      pointer-events: auto;
    }

    /* The dock is draggable and snaps to a corner. Which corner is a data
       attribute rather than four rules: the script writes the four inset
       properties, and the only thing CSS owns is which edge the buttons line
       up against, because a right-hand dock whose buttons start at the left
       reads as broken. */
    .lab-tools[data-corner$="r"] { justify-items: end; }

    .lab-tools__grip {
      display: grid;
      place-items: center;
      width: 100%;
      min-height: 22px;
      padding: 0 12px;
      border: 1px solid var(--brass-hair);
      border-radius: 999px;
      background: color-mix(in srgb, var(--paper) 82%, transparent);
      backdrop-filter: blur(12px);
      color: color-mix(in srgb, var(--ink) 45%, transparent);
      cursor: grab;
      font-family: var(--mono);
      font-size: 10px;
      letter-spacing: 0.22em;
      text-transform: uppercase;
      touch-action: none;
      user-select: none;
    }
    .lab-tools__grip:hover { color: var(--brass); border-color: var(--brass); }

    .lab-tools.is-dragging {
      z-index: 60;
      cursor: grabbing;
    }
    .lab-tools.is-dragging .lab-tools__grip {
      cursor: grabbing;
      color: var(--brass);
      border-color: var(--brass);
    }
    .lab-tools.is-dragging > :not(.lab-tools__grip) { pointer-events: none; }

    /* The four places it can land, shown only while something is in the air. */
    .lab-corners {
      position: fixed;
      inset: 0;
      z-index: 59;
      pointer-events: none;
      opacity: 0;
      transition: opacity 140ms ease;
    }
    .lab-corners[data-shown="true"] { opacity: 1; }
    .lab-corners i {
      position: absolute;
      width: 76px;
      height: 76px;
      border: 1px dashed var(--brass);
      border-radius: 14px;
      opacity: 0.35;
      transition: opacity 120ms ease, background-color 120ms ease;
    }
    .lab-corners i[data-near="true"] {
      opacity: 1;
      background: color-mix(in srgb, var(--brass) 14%, transparent);
    }
    .lab-corners i[data-corner="tl"] { top: 12px; left: 12px; }
    .lab-corners i[data-corner="tr"] { top: 12px; right: 12px; }
    .lab-corners i[data-corner="bl"] { bottom: 12px; left: 12px; }
    .lab-corners i[data-corner="br"] { bottom: 12px; right: 12px; }

    .lab-tools__toggle,
    .lab-tools__theme,
    .lab-tools__lines,
    .lab-tools__ground,
    .lab-tools__power,
    .lab-tools__copy,
    .lab-tools__icons,
    .lab-tools__nudges,
    .lab-panel__copy,
    .lab-preset__save {
      min-height: 36px;
      border: 1px solid var(--brass-hair);
      border-radius: 999px;
      background: color-mix(in srgb, var(--paper) 90%, transparent);
      color: var(--ink);
      cursor: pointer;
      font-size: 13px;
      font-weight: 500;
      line-height: 1;
      backdrop-filter: blur(12px);
    }

    .lab-tools__toggle,
    .lab-tools__theme,
    .lab-tools__lines,
    .lab-tools__power,
    .lab-tools__copy,
    .lab-tools__icons,
    .lab-tools__nudges {
      padding: 9px 13px;
    }

    /* The line panel carries far more rows than the sphere one, so it groups
       them the way the type lab does rather than running one flat list. */
    .lab-lines__group {
      display: grid;
      gap: 9px;
      padding-top: 11px;
      border-top: 1px solid var(--hairline-paper);
    }

    .lab-lines__group:first-of-type {
      padding-top: 2px;
      border-top: 0;
    }

    .lab-lines__name {
      margin: 0;
      color: var(--ink);
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.16em;
      text-transform: uppercase;
    }

    /* The copy panel is a list of whole candidates rather than rows of
       controls, so it gets its own item shape: a mono key, then the name,
       wrapping to as many lines as it needs. Same group headers as the line
       panel above so the two docks read as one system. */
    .lab-copy__list {
      display: grid;
      gap: 3px;
    }

    .lab-copy__item {
      display: grid;
      grid-template-columns: 24px minmax(0, 1fr);
      gap: 8px;
      align-items: baseline;
      padding: 6px 8px;
      border: 1px solid transparent;
      border-radius: 8px;
      background: transparent;
      color: var(--ink);
      cursor: pointer;
      font-family: var(--sans);
      font-size: 12px;
      text-align: left;
      transition: border-color 0.16s var(--ease), background 0.16s var(--ease);
    }

    .lab-copy__item:hover {
      border-color: var(--brass-hair);
    }

    .lab-copy__item[aria-pressed="true"] {
      border-color: var(--brass-hair);
      background: var(--brass-wash);
    }

    .lab-copy__key {
      color: var(--clay);
      font-family: var(--mono);
      font-size: 10px;
      letter-spacing: 0.08em;
    }

    .lab-copy__item[aria-pressed="true"] .lab-copy__key {
      color: var(--brass);
    }

    .lab-copy__name {
      line-height: 1.35;
    }

    /* Clear radius prints as "1.15× · 584px", which overflows the sphere
       panel's 58px readout column. */
    #line-controls .lab-panel__row {
      grid-template-columns: minmax(74px, 0.72fr) minmax(84px, 1fr) 76px;
    }

    #line-controls .lab-panel__row > select,
    #line-controls .lab-panel__row > input[type="color"] {
      grid-column: 2 / -1;
    }

    .lab-tools__theme {
      min-width: 74px;
    }

    /* Type lab. Reuses the sphere panel chrome wholesale; only the per-level
       grouping and its captions are new. */
    .lab-type__group {
      display: grid;
      gap: 9px;
      padding-top: 11px;
      border-top: 1px solid var(--hairline-paper);
    }

    .lab-type__group:first-of-type {
      padding-top: 2px;
      border-top: 0;
    }

    .lab-type__name {
      margin: 0;
      color: var(--ink);
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.16em;
      text-transform: uppercase;
    }

    .lab-type__note {
      margin: 0;
      color: var(--ink-soft);
      font-size: 11px;
      line-height: 1.5;
    }

    /* Tracking prints as -0.045em, which overflows the sphere panel's 58px
       readout column, and the family selects need the readout column's width
       too since they have nothing to put in it. */
    #type-controls .lab-panel__row {
      grid-template-columns: minmax(62px, 0.7fr) minmax(84px, 1fr) 68px;
    }

    #type-controls .lab-panel__row > select {
      grid-column: 2 / -1;
    }

    .lab-panel {
      display: grid;
      gap: 12px;
      width: min(316px, calc(100vw - 32px));
      /* Leaves room for the four dock buttons below it (4 x 36px + 3 x 8px
         gaps + the 16px inset), so a full panel never runs off the top. */
      max-height: calc(100svh - 200px);
      padding: 15px;
      overflow-y: auto;
      border: 1px solid var(--hairline-paper);
      border-radius: 14px;
      background: color-mix(in srgb, var(--paper) 92%, transparent);
      backdrop-filter: blur(16px);
      overscroll-behavior: contain;
    }

    .lab-panel[hidden] {
      display: none;
    }

    .lab-panel__heading {
      margin: 0;
      color: var(--clay);
      font-family: var(--mono);
      font-size: 13px;
      font-weight: 500;
      letter-spacing: 0.05em;
      line-height: 1.3;
      text-transform: uppercase;
    }

    .lab-panel__row {
      display: grid;
      grid-template-columns: minmax(86px, 0.84fr) minmax(92px, 1fr) 58px;
      align-items: center;
      gap: 9px;
      min-height: 28px;
      font-size: 13px;
      line-height: 1.3;
    }

    .lab-panel__row label {
      min-width: 0;
    }

    .lab-panel input[type="range"] {
      width: 100%;
      margin: 0;
      accent-color: var(--brass);
    }

    .lab-panel input[type="color"] {
      width: 100%;
      min-width: 0;
      height: 30px;
      padding: 2px;
      border: 1px solid var(--hairline-paper);
      border-radius: 6px;
      background: var(--paper);
      cursor: pointer;
    }

    .lab-panel select {
      width: 100%;
      min-width: 0;
      min-height: 32px;
      border: 1px solid var(--hairline-paper);
      border-radius: 6px;
      background: var(--paper);
      color: var(--ink);
      font-family: var(--sans);
      font-size: 13px;
    }

    .lab-panel output {
      overflow: hidden;
      color: var(--ink-soft);
      font-family: var(--mono);
      font-size: 13px;
      line-height: 1.3;
      text-align: right;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .lab-panel__footer {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 10px;
      padding-top: 2px;
    }

    .lab-panel__copy,
    .lab-preset__save {
      padding: 9px 13px;
      background: var(--brass-wash);
    }

    .lab-panel__status {
      min-width: 0;
      color: var(--clay);
      font-size: 13px;
      line-height: 1.3;
    }

    .lab-panel__presets {
      display: grid;
      gap: 9px;
      justify-items: start;
      padding-top: 11px;
      border-top: 1px solid var(--hairline-paper);
    }

    .lab-presets {
      display: flex;
      flex-wrap: wrap;
      gap: 6px;
    }

    /* Two buttons share one pill: the name applies, the cross deletes. They
       cannot nest, so the pill border lives on the wrapper. */
    .lab-preset {
      display: inline-flex;
      align-items: stretch;
      overflow: hidden;
      border: 1px solid var(--brass-hair);
      border-radius: 999px;
      background: var(--brass-wash);
    }

    .lab-preset__apply,
    .lab-preset__remove {
      border: 0;
      background: none;
      color: var(--ink);
      cursor: pointer;
      font-family: var(--mono);
      font-size: 13px;
      line-height: 1;
    }

    .lab-preset__apply {
      padding: 8px 4px 8px 12px;
    }

    .lab-preset__remove {
      padding: 8px 11px 8px 4px;
      color: var(--clay);
    }

    .lab-preset__remove:hover {
      color: var(--ink);
    }

    /* ========================================================
       DRAFT SECTION SYSTEM — carried over without redesign
       ======================================================== */
    /* ── The band rhythm ─────────────────────────────────────────────────
       One padding, shared by every section that has been taken off full
       height: whats-next, integrations, the founder, security and the close.
       Because all five use the same value, the run between any two of them is
       the same 2 x --band-pad, which is what Can asked for on 2026-08-26 after
       measuring the gap above the security band against the gap below it.

       DOUBLED 2026-08-26, second pass. Can read the security to close run as
       squeezed and asked for twice the air. It was clamp(3.5rem, 7vh, 5.5rem).
       Because the value is shared, every band opened up together, which is the
       point: widening one run only would put the page out of rhythm.

       Each of those five is selected as .draft-section.<name>, on two classes
       rather than one, ON PURPOSE. A later rule inside the 899px media query
       sets .draft-section { padding-block } and would otherwise win the tie
       on source order, which is exactly what it was doing: the two runs
       agreed on desktop and came out 127 against 152 on a phone. Do not
       lower those selectors back to one class. */
    :root { --band-pad: clamp(7rem, 14vh, 11rem); }

    .draft-section {
      position: relative;
      display: flex;
      align-items: center;
      min-height: 100vh;
      min-height: 100svh;
      padding-block: clamp(4.5rem, 9vh, 7rem);
      scroll-snap-align: start;
      isolation: isolate;
    }

    /* The line fan behind the founder and the close on wide screens, mounted
       by assets/section-lines.js. The section isolates, so minus one sits
       above its ground and below every word; the page field is further back
       still. Back on 2026-09-08 after the hero's own soft endings proved
       smooth on the real domain: the clunk Can was judging turned out to be
       Vercel's toolbar on a preview deployment, not the canvases. */
    .section-lines {
      position: absolute;
      z-index: -1;
      inset: 0;
      overflow: hidden;
      overflow: clip;
      pointer-events: none;
      background: var(--paper);
    }

    .draft-section--bone {
      background: var(--bone);
    }

    /* Centred sections. Founder call, 2026-08-20: left alignment is reserved
       for the places where the page is composed against something, which is
       the hero, the four beat flow and the roster's own picker. Everywhere the
       section is just a title with copy under it, the copy sets centred.

       Applied as a modifier rather than a default so the exceptions stay
       visible in the markup. Structures that carry their own alignment, the
       ledger's two columns and the promise rows, are put back to left inside
       it: a definition list reads as noise centred. */
    .draft-section--centred .wrap { text-align: center; }

    .draft-section--centred .h2,
    .draft-section--centred .body,
    .draft-section--centred .statement,
    .draft-section--centred .never,
    .draft-section--centred .ledger,
    .draft-section--centred .promises,
    .draft-section--centred .promise { margin-inline: auto; }

    .draft-section--centred .ledger,
    .draft-section--centred .promise { text-align: left; }

    /* Centred type shows an orphan the way left aligned type does not: the
       last word sits alone in the middle of the measure with white space on
       both sides. Balance splits the lines evenly instead. */
    .draft-section--centred .h2,
    .draft-section--centred .body,
    .draft-section--centred .statement,
    .draft-section--centred .close-line,
    .draft-section--centred .close-note { text-wrap: balance; }

    .draft-section--centred .close-stack { align-items: center; }

    .wrap {
      position: relative;
      z-index: 1;
      /* Same shell as the roster. The 1100px cap these drafts carried put
         their first word 30% into a 2560 screen, which is the gap Can called
         out in the hero and it reads worse here, on plain left-aligned copy. */
      width: var(--shell);
      margin-inline: auto;
    }

    .body {
      max-width: 60ch;
      color: var(--ink-soft);
      font-size: var(--t-body);
      line-height: 1.6;
    }

    .body--56 {
      max-width: 56ch;
    }

    .statement {
      max-width: 17ch;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-statement);
      font-weight: var(--display-weight);
      letter-spacing: -0.02em;
      line-height: 1.1;
    }

    .h2 {
      max-width: 22ch;
      color: var(--ink);
      font-family: var(--sans);
      font-size: var(--t-h2);
      font-weight: var(--display-weight);
      /* Was -0.03em at weight 600. Both moved 2026-08-20 with the type scale:
         this is the same role as the roster headline, so it now sets at the
         same size, the same 300 the type decision in tokens.css calls for, and
         the same tracking. A light face at 49px cannot carry -0.03em. */
      letter-spacing: -0.018em;
      line-height: 1.14;
    }

    .eyebrow,
    .mono-label {
      display: block;
      color: var(--ink-soft);
      font-family: var(--mono);
      font-size: var(--t-small);
      letter-spacing: 0.2em;
      text-transform: uppercase;
    }

    .btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 0.5rem;
      min-height: 46px;
      padding: 0.95rem 1.75rem;
      border: 0;
      border-radius: 999px;
      background: var(--brass);
      box-shadow: var(--draft-shadow-rest);
      color: var(--ink);
      cursor: pointer;
      font-family: var(--sans);
      font-size: var(--t-small);
      font-weight: 600;
      letter-spacing: -0.01em;
      line-height: 1;
      transition: translate 450ms var(--ease), background-color 450ms var(--ease), box-shadow 450ms var(--ease);
    }

    .btn:hover {
      background: var(--brass-bright);
      box-shadow: var(--draft-shadow-lift);
      translate: 0 -2px;
    }

    .statement-block .statement {
      margin-bottom: 2rem;
    }

    .turn-lines {
      margin-bottom: 2.4rem;
    }

    .turn-line {
      max-width: 17ch;
      font-family: var(--display);
      font-size: var(--t-statement);
      font-weight: var(--display-weight);
      letter-spacing: -0.02em;
      line-height: 1.12;
    }

    .turn-line--quiet {
      color: var(--clay);
    }

    .turn-line--lead {
      color: var(--ink);
    }

    .sec-head {
      margin-bottom: clamp(2.5rem, 5vh, 3.5rem);
    }

    .sec-head .eyebrow {
      margin-bottom: 1rem;
    }

    .cards {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: clamp(1rem, 2vw, 1.5rem);
    }

    .card {
      padding: clamp(1.6rem, 2.6vw, 2.2rem);
      border: 1px solid var(--hairline-paper);
      border-radius: 22px;
      background: var(--paper);
      box-shadow: var(--draft-shadow-rest);
      transition: translate 450ms var(--ease), box-shadow 450ms var(--ease);
    }

    .card:hover {
      box-shadow: var(--draft-shadow-lift);
      translate: 0 -2px;
    }

    .card h3 {
      margin-bottom: 0.6rem;
      color: var(--ink);
      font-family: var(--sans);
      font-size: var(--t-lead);
      font-weight: 600;
      letter-spacing: -0.02em;
    }

    .card p {
      color: var(--ink-soft);
      font-size: var(--t-body);
      line-height: 1.55;
    }

    .cards-note {
      max-width: 52ch;
      margin: clamp(2.2rem, 4.5vh, 3rem) auto 0;
      color: var(--ink-soft);
      font-size: var(--t-body);
      text-align: center;
    }

    .ledger {
      margin-top: clamp(2.2rem, 4.5vh, 3.2rem);
    }

    .ledger-head,
    .ledger-row {
      display: grid;
      grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
      align-items: baseline;
      gap: clamp(1.25rem, 4vw, 3.5rem);
    }

    .ledger-head {
      padding-bottom: 1.2rem;
    }

    .ledger-row {
      padding-block: clamp(1.3rem, 2.6vh, 2.1rem);
    }

    .ledger-what {
      color: var(--ink);
      font-size: var(--t-lead);
      font-weight: 500;
      letter-spacing: -0.01em;
      line-height: 1.45;
    }

    .ledger-part {
      color: var(--ink-soft);
      font-size: var(--t-body);
    }

    .ledger-part .mono-label {
      display: none;
      margin-bottom: 0.5rem;
    }

    .punchline {
      display: block;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-punch);
      font-weight: var(--display-weight);
      letter-spacing: -0.02em;
      line-height: 1;
    }

    .never {
      margin-top: clamp(2.2rem, 4.5vh, 3rem);
      margin-bottom: 2.6rem;
    }

    .never li {
      padding-block: clamp(0.6rem, 1.5vh, 1rem);
      color: var(--ink);
      font-size: var(--t-h4);
      font-weight: 500;
      letter-spacing: -0.02em;
    }

    .promises {
      margin-top: clamp(2.2rem, 4.5vh, 3rem);
    }

    .promise {
      display: flex;
      flex-wrap: wrap;
      align-items: baseline;
      gap: 0.3rem 0.75rem;
      max-width: 68ch;
      padding-block: clamp(0.85rem, 1.9vh, 1.35rem);
    }

    .promise-word {
      min-width: 9ch;
      color: var(--brass);
      font-size: var(--t-lead);
      font-weight: 700;
      letter-spacing: -0.01em;
    }

    .promise-eq {
      color: var(--ink-soft);
      font-size: var(--t-body);
    }

    .promise-text {
      flex: 1 1 22ch;
      min-width: 0;
      color: var(--ink-soft);
      font-size: var(--t-body);
      line-height: 1.55;
    }

    .close-stack {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
    }

    .close-stack .statement {
      margin-bottom: 2.6rem;
    }

    .close-line {
      margin-top: 1.8rem;
      color: var(--ink);
      font-size: var(--t-body);
    }

    .close-note {
      margin-top: 0.7rem;
      color: var(--ink-soft);
      font-size: var(--t-small);
    }

    /* ══ SECTION 4, FOR YOU, SECTION 5, WHAT'S NEXT, AND SECTION 6, THE CLOSE ══
       For you and the close came from lab/section4-foryou on 2026-08-21.
       What's next came from lab/section5-next on 2026-08-23. All three ride
       the engine rules above, including .draft-section, .wrap, [data-reveal]
       and the motion gates. Their fy-, nx- and cl- rules stay local here. */

    /* The turn's own lines are one line each, which is what makes its
       rhythm. For you's lines are questions of 29 to 36 characters, so at
       the 17ch measure each wraps to two and the clay runs together before
       the lead. Two scoped moves: the block takes the proof row's width, so
       at desktop only the longest question wraps, and a gap between beats
       gives each question its own breath. Can's calls, 2026-08-21. */
    /* OFF FULL HEIGHT AND PULLED IN, Can's call 2026-08-26. It was the last
       section between two bands still pinned to 100vh, and the pin was the
       whole problem: it held 526px of content inside a 900px section, so 374px
       of slack sat above and below the content and read as distance. The runs
       either side measured 371px and 313px at 1440.

       HALF the band pad, not the whole of it. Joining the band system at
       --band-pad would have left the runs at 252px, which is a third off when
       Can asked for about half. At 0.5 they come out near 189px, which is 49%
       and 40% off. Written as a fraction of the shared value rather than as a
       number so it still tracks if --band-pad ever moves again.

       This is a deliberate exception to the one shared padding rule, and it is
       the only one. If a second section wants it, the rule is what should
       change, not the count of exceptions. */
    .draft-section.fy-section {
      min-height: 0;
      padding-block: calc(var(--band-pad) * 0.5);
      scroll-snap-align: none;
    }

    .fy-section .fy-icp { text-align: center; margin-inline: auto; }
    .fy-icp em { color: var(--brass); font-style: italic; }

    /* The section head, 2026-08-26. The four turn lines moved to the close
       and took the 8rem gap with them: that gap existed to hold the lead line
       clear of the entrepreneurs line, and there is no longer a lead line for
       it to hold anything clear of. What is left is an eyebrow over one line,
       which is the same head every other band on the page uses.

       The ICP line stays at the h4 step in the serif rather than dropping to
       an .h2. It is not a headline, it is the sentence that says who the
       quotes below are from, and at h2 it read as a second claim. */
    .fy-head { text-align: center; }
    .fy-head .eyebrow { margin-bottom: 1rem; }

    /* THE FOUR LINES, moved here from the testimonials section on 2026-08-26.
       Their TREATMENT moved with them, and that is the whole of this block.

       It is Can's pick of 2026-08-21, chosen in lab/section4-foryou: the
       roster headline's size (.s3-headline, value for value) rather than the
       statement's, the block centred, capped at the proof row's width so at
       desktop only the longest question wraps.

       The size is the part that matters. The bare .turn-line is --t-statement
       at a 17ch measure, which is right for a line standing alone and wrong
       for four stacked over two doors: moved without this block, every
       question broke across two lines and the section grew to 1220px. */
    .cl-section .turn-line {
      max-width: min(100%, var(--s3-max));
      margin-inline: auto;
      font-size: var(--t-h2);
      letter-spacing: -0.018em;
      line-height: 1.08;
      /* No word left alone on a line: "grow." and "them?" were, on Can's
         phone, 2026-09-03. Balance shares the words across the lines. */
      text-wrap: balance;
    }

    .cl-section .turn-lines {
      margin-bottom: clamp(2.6rem, 6vh, 4rem);
      text-align: center;
    }

    /* And a step down on a phone, the other half of the same fix. */
    @media (max-width: 560px) {
      .cl-section .turn-line { font-size: calc(var(--t-h2) * 0.86); }
    }

    .cl-section .turn-line--lead em { color: var(--brass); font-style: normal; }
    .cl-section .turn-line--quiet + .turn-line { margin-top: 0.42em; }

    /* The ICP line, lifted from body to the h4 step and set in ink, because
       at body size it vanished under the statement above it. */
    .fy-icp {
      max-width: min(100%, var(--s3-max));
      color: var(--ink);
      font-family: var(--serif);
      font-weight: 400;
      font-size: var(--t-h4);
      letter-spacing: 0;
      line-height: 1.25;
    }

    /* The proof row takes section 3's cap, not the prose shell, so the
       cards set to the same width the roster panel does one screen up.

       A SLIDER, Can's ask of 2026-09-08. Six cards now, and the row travels
       right to left on the provider strip's maths (section 4's "Also
       available on", for the same reasons): a second copy of the set sits
       behind the first in the track, the animation runs exactly one copy's
       worth, -50%, and the spacing is a margin on each card and not a gap
       on the row, so both copies measure the same and the seam never
       hitches. Two copies are enough here where the strip needs three: one
       set is two windows wide, so the copy behind the one being shifted
       always covers the window.

       Three cards fill the window at any width, sized off the window in
       container units, and the window clips hard at its edges: a card is
       either in the row or arriving. One card fills it on a phone.

       assets/fy-slider.js writes the second copy, turns the travel on
       (data-slider) and lets a hand throw the row. With the script absent
       the six sit in a row that scrolls sideways under the finger, and
       nothing moves on its own.

       The window carries room above and below and pulls it back with
       margins, so a hovered card's lift and its leaning shadow have space
       inside the clip. */
    .fy-proof {
      --fy-gap: clamp(1rem, 2vw, 1.5rem);
      --fy-room-top: 0.75rem;
      --fy-room-bottom: 4.5rem;
      width: min(100%, var(--s3-max));
      margin-inline: auto;
      /* Was 10rem, Can's slider pick of 2026-08-21. That gap was holding the
         cards clear of FOUR turn lines and the entrepreneurs line; the lines
         went to the close on 2026-08-26 and left an eyebrow over one sentence,
         so the gap comes back to the page's ordinary head to body step. */
      margin-top: calc(clamp(2.6rem, 6vh, 4rem) - var(--fy-room-top));
      margin-bottom: calc(-1 * var(--fy-room-bottom));
      padding-block: var(--fy-room-top) var(--fy-room-bottom);
      container-type: inline-size;
      overflow-x: auto;
      overflow-y: hidden;
      scrollbar-width: none;
      /* Sideways is ours, vertical stays the page's, so a touch drag over
         the row still scrolls the article. */
      touch-action: pan-y;
    }
    .fy-proof::-webkit-scrollbar { display: none; }
    .fy-proof[data-slider="on"] { overflow: hidden; cursor: grab; }
    /* The edges soften the way the strip's do, so a card leaving is not
       sliced through its words. Shorter than the strip's ten percent: at
       three cards to the window a long fade would dim the outer two. */
    .fy-proof[data-slider="on"] {
      mask-image: linear-gradient(to right, transparent, #000 4%, #000 96%, transparent);
      -webkit-mask-image: linear-gradient(to right, transparent, #000 4%, #000 96%, transparent);
    }
    .fy-proof[data-dragging="true"] { cursor: grabbing; }
    .fy-proof[data-dragging="true"] .fy-track { user-select: none; }

    .fy-track {
      display: flex;
      align-items: stretch;
      width: max-content;
      margin: 0;
      /* The lean's vanishing point lives here, not on the window:
         perspective reaches an element's children, and the cards are the
         track's. */
      perspective: 1100px;
    }
    .fy-proof[data-slider="on"] .fy-track {
      animation: fy-slide var(--fy-lap, 48s) linear infinite;
    }
    /* A hand over the row holds it still so a card can be read, the
       strip's rule. */
    .fy-proof[data-slider="on"] .fy-track:hover { animation-play-state: paused; }

    @keyframes fy-slide {
      from { transform: translateX(0); }
      to   { transform: translateX(-50%); }
    }

    /* Three to the window, each a third less two gaps, and the gap rides
       on the card as a margin (see above). */
    .fy-track .fy-card {
      flex: 0 0 auto;
      width: calc((100cqw - 2 * var(--fy-gap)) / 3);
      margin-right: var(--fy-gap);
    }
    /* 10rem was chosen on a desktop; on a phone it is a screen of nothing. */
    @media (max-width: 760px) {
      .fy-section .fy-proof { margin-top: calc(6rem - var(--fy-room-top)); }
      .fy-track .fy-card { width: 100cqw; }
    }

    /* Still, and back to a grid, three by two, one column on a phone. The
       copy is dropped rather than hidden, because with nothing moving it
       would read as every card printed twice. A row a reader cannot stop
       is exactly what this preference exists to switch off. */
    @media (prefers-reduced-motion: reduce) {
      .fy-proof, .fy-proof[data-slider="on"] {
        overflow: visible;
        cursor: auto;
        mask-image: none;
        -webkit-mask-image: none;
      }
      .fy-track, .fy-proof[data-slider="on"] .fy-track {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: var(--fy-gap);
        width: auto;
        animation: none;
        transform: none;
      }
      .fy-track .fy-card { width: auto; margin-right: 0; }
      .fy-track .fy-card[aria-hidden="true"] { display: none; }
    }
    html[data-reduced-motion="true"] .fy-proof {
      overflow: visible;
      cursor: auto;
      mask-image: none;
      -webkit-mask-image: none;
    }
    html[data-reduced-motion="true"] .fy-proof .fy-track {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: var(--fy-gap);
      width: auto;
      animation: none;
      transform: none;
    }
    html[data-reduced-motion="true"] .fy-track .fy-card { width: auto; margin-right: 0; }
    html[data-reduced-motion="true"] .fy-track .fy-card[aria-hidden="true"] { display: none; }
    @media (max-width: 760px) {
      html[data-reduced-motion="true"] .fy-proof .fy-track { grid-template-columns: minmax(0, 1fr); }
    }
    @media (prefers-reduced-motion: reduce) and (max-width: 760px) {
      .fy-track, .fy-proof[data-slider="on"] .fy-track { grid-template-columns: minmax(0, 1fr); }
    }

    /* The flag, Can's call 2026-08-21: pending RENDERS the cards as marked
       placeholders rather than hiding them, so the whole journey is on the
       page before the quotes exist. Dashed hairline and a mono caption at
       the top of each card (the .mono-label style at --t-micro, in clay), in
       CSS alone. ready is the finished look: solid hairline, no caption. The
       flip is this one attribute plus the copy. */
    .fy-section[data-proof="pending"] .fy-card { border-style: dashed; }
    .fy-section[data-proof="pending"] .fy-card::before {
      content: "Placeholder";
      display: block;
      margin-bottom: 1rem;
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.2em;
      text-transform: uppercase;
    }

    /* Section 3's card language: hairline, 16px radius, the .card padding
       step. A column, so the attribution pins to the foot whatever length the
       quote runs. No shadow, no stars, no faces.

       The fill came off 2026-08-26 on Can's call. It was --bone on a --paper
       section and read as a warm block cutting across an otherwise even page,
       which is the same complaint that took the ground off the integrations
       band the same day. The hairline stays: it is what makes them cards. */
    .fy-card {
      text-align: center;
      display: flex;
      flex-direction: column;
      min-height: clamp(250px, 24vw, 360px);
      /* Sides tighter than top and bottom, Can's call 2026-09-06: the quote
         was sitting in a narrow column with a band of nothing either side. */
      padding: clamp(1.6rem, 2.6vw, 2.2rem) clamp(1.2rem, 1.8vw, 1.6rem);
      border: 1px solid var(--hairline-paper);
      border-radius: 16px;
      background: var(--paper);
    }

    /* The quote in the display face at body size. It was --t-lead, a step up,
       which was right when the slot held three words and too loud once real
       length sample text went in on 2026-08-26: at that size a long quote
       filled the card and shouted over the attribution under it. Tracking and
       leading are unchanged. */
    /* The browser gives a blockquote 40px of margin a side and nothing here
       had taken it back, so every quote lost 80px of the card's width before
       the padding was counted. Found 2026-09-06 when Can framed the text. */
    .fy-quote { margin: 0; }

    .fy-quote p {
      margin: 0;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-body);
      font-weight: var(--display-weight);
      letter-spacing: -0.01em;
      line-height: 1.45;
    }

    /* The closing line, Can's call 2026-09-04. It sits inside the quote's own
       paragraph on a hard break: its own line, on the paragraph's own leading,
       with no gap. It was a second paragraph first and the space that opened
       between the two read as a pull quote inside a quote, which Can saw at
       once. There is no rule on .fy-coda by design. The class is a hook for
       the gate, nothing more: the break does the work. */

    /* Attribution in the sans at caption size, three stacked lines: the
       person, the business, what was built. Same step and leading as the
       roster's side column (.s3-val). */
    .fy-who {
      display: grid;
      justify-items: center;
      gap: 2px;
      margin-top: auto;
      padding-top: 1.6rem;
      font-size: var(--t-small);
      line-height: 1.45;
    }

    /* The name is signed, not typed: Alex Brush through --script, Can's pick
       on 2026-09-04 from twenty one faces in lab/section4-names, self-hosted
       in assets/brand/fonts.

       It replaces Mrs Saint Delafield, which was picked on the same rule in
       August and which Can could not read on the card. The face changed, the
       rule did not, and neither did the tie to the roster's two pen notes:
       .nudge__word reads the same token, so both are in this hand.

       1.51 body sizes is Can's number, not the measured one. Alex Brush
       measures 2.12, the factor that would draw its ink at the 1.55 body
       sizes every face in the strip was normalised to; he took it to 0.71 of
       that in the lab, which is 1.51, because at full ink height the wider
       face crowded the card and broke the longest name over two lines. The
       measurement decides how faces COMPARE. What ships is a judgement on
       top of it. */
    .fy-name {
      margin-bottom: 0.45rem;
      color: var(--ink);
      font-family: var(--script);
      font-size: calc(var(--t-body) * 1.51);
      font-weight: 400;
      letter-spacing: 0;
      line-height: 1.05;
    }

    .fy-biz { color: var(--ink-soft); }
    .fy-built { color: var(--clay); }

    /* The five steps and their horizontal line form one desktop grid. */
    /* Off full height on 2026-08-26. Can measured the run from the button at
       the foot of this section down to the security eyebrow against the run
       from the security cards down to the close and asked for the two to
       match. Padding alone could not do it: while this section was held to
       100vh with its content centred, every pixel left over after the content
       landed in that first gap, so the two runs agreed at a 900px window and
       were 385 against 184 at a 1500px one, which is the window he was
       looking at. Content height is what makes them agree everywhere.

       The walk is unaffected: assets/whats-next.js triggers off the rail's
       own position in the viewport (innerHeight * 0.8), never off the
       section's height. Verified by qa/site-section5-capture.mjs. */
    .draft-section.nx-section {
      position: relative;
      display: grid;
      align-items: center;
      /* Explicitly 0, not merely absent: the section also carries
         .draft-section, which sets 100vh, so dropping the declaration here
         just falls back to that one. */
      min-height: 0;
      padding-block: var(--band-pad);
      isolation: isolate;
      background: var(--paper);
      color: var(--ink);
    }

    /* Centred head, Can's call on 2026-08-23: the eyebrow and the headline sit
       on the centre line of the section, over the centred timeline. The .h2
       role keeps its 22ch measure, so it is centred by its margins, not by
       its text alone. */
    .nx-section .sec-head { text-align: center; }
    .nx-section .sec-head .h2 { margin-inline: auto; }
    /* No snap on this section, Can's call: the reader scrolls through it at
       their own pace so the walk can be watched, and proximity snapping was
       pulling the section to the top before the line had finished. */
    .nx-section { scroll-snap-align: none; }
    .nx-section .h2 { text-wrap: balance; }
    .nx-section .h2 em { color: var(--brass); font-style: normal; }

    .nx-section .nx-stage {
      --nx-t: 1;
      display: grid;
      grid-template-columns: repeat(5, minmax(0, 1fr));
      grid-template-rows: auto auto auto;
      width: 100%;
      min-width: 0;
      container-type: inline-size;
      isolation: isolate;
    }

    .nx-section .nx-step {
      min-width: 0;
      padding-inline: clamp(0.45rem, 0.8vw, 0.8rem);
      text-align: center;
    }

    .nx-section .nx-step--01 {
      grid-column: 1;
      grid-row: 1;
      align-self: end;
      padding-bottom: clamp(0.35rem, 0.55vw, 0.55rem);
    }

    .nx-section .nx-step--02 {
      grid-column: 2;
      grid-row: 3;
      align-self: start;
      padding-top: clamp(0.35rem, 0.55vw, 0.55rem);
    }

    .nx-section .nx-step--03 {
      grid-column: 3;
      grid-row: 1;
      align-self: end;
      padding-bottom: clamp(0.35rem, 0.55vw, 0.55rem);
    }

    .nx-section .nx-step--04 {
      grid-column: 4;
      grid-row: 3;
      align-self: start;
      padding-top: clamp(0.35rem, 0.55vw, 0.55rem);
    }

    .nx-section .nx-step--05 {
      grid-column: 5;
      grid-row: 1;
      align-self: end;
      padding-bottom: clamp(0.35rem, 0.55vw, 0.55rem);
    }

    .nx-section .nx-step__copy {
      width: min(100%, 26ch);
      min-width: 0;
      max-width: 26ch;
      margin-inline: auto;
      text-align: center;
    }

    .nx-section .nx-mobile-rail { display: none; }

    /* ── Step icons ───────────────────────────────────────────────────
       Off unless data-step-icons is flipped, which only the localhost lab
       tools do. Eight marks per step that relocate as the walk passes:
       four ruled rows, four bars, a folded sheet, the vertices of a box,
       a closed ring. Nothing is added and nothing is taken away, and the
       first mark stays brass, so the spark is findable in the last form.
       Round one of lab/section5-icons, Can's pick 2026-08-26. They ride
       --nx-t; there is no second timeline. */
    .nx-section .nx-icon { display: none; }

    .nx-section .nx-icon-line {
      fill: none;
      stroke: var(--hairline-paper);
      stroke-width: 1.3;
      stroke-linecap: round;
      stroke-linejoin: round;
      vector-effect: non-scaling-stroke;
    }

    .nx-section .nx-icon-mark { fill: var(--ink); opacity: 0.5; }
    .nx-section .nx-icon-mark--seed { fill: var(--brass); opacity: 1; }

    html[data-step-icons="on"] .nx-section .nx-icon {
      display: block;
      width: clamp(46px, 4.2vw, 64px);
      height: auto;
      margin-inline: auto;
      overflow: visible;
    }

    @media (min-width: 900px) {
      /* Flex only so the icon can be ordered past the copy. The steps above
         the rail want it below their copy and the steps below want it above,
         because the icon is the thing that should sit nearest the line: the
         tick out of each dot then points straight at it. */
      html[data-step-icons="on"] .nx-section .nx-step {
        display: flex;
        flex-direction: column;
      }

      html[data-step-icons="on"] .nx-section .nx-step--01 .nx-icon,
      html[data-step-icons="on"] .nx-section .nx-step--03 .nx-icon,
      html[data-step-icons="on"] .nx-section .nx-step--05 .nx-icon {
        order: 1;
        margin-top: 1rem;
      }

      html[data-step-icons="on"] .nx-section .nx-step--02 .nx-icon,
      html[data-step-icons="on"] .nx-section .nx-step--04 .nx-icon {
        margin-bottom: 1rem;
      }
    }

    .nx-section .nx-step-meta {
      display: flex;
      flex-wrap: nowrap;
      justify-content: center;
      gap: 0.25em 0.7em;
      align-items: baseline;
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.2em;
      line-height: 1.45;
      white-space: nowrap;
      text-transform: uppercase;
    }

    .nx-section .nx-step-index,
    .nx-section .nx-step-stamp { display: inline-block; }

    .nx-section .nx-step-title {
      max-width: 15ch;
      margin-top: 0.7rem;
      margin-inline: auto;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-h4);
      font-weight: var(--display-weight);
      letter-spacing: -0.015em;
      line-height: 1.12;
      text-wrap: balance;
    }

    .nx-section .nx-step-line {
      max-width: 26ch;
      margin-top: 0.7rem;
      margin-inline: auto;
      color: var(--ink-soft);
      font-family: var(--sans);
      font-size: var(--t-small);
      line-height: 1.55;
    }

    .nx-section .nx-desktop-curve {
      display: block;
      grid-column: 1 / -1;
      grid-row: 2;
      align-self: center;
      width: 100%;
      height: auto;
      overflow: visible;
      shape-rendering: geometricPrecision;
    }

    .nx-section .nx-rail,
    .nx-section .nx-trail,
    .nx-section .nx-tick,
    .nx-section .nx-fan {
      fill: none;
      stroke-linecap: butt;
      stroke-linejoin: miter;
    }

    .nx-section .nx-tick,
    .nx-section .nx-fan { vector-effect: non-scaling-stroke; }

    .nx-section .nx-rail,
    .nx-section .nx-tick,
    .nx-section .nx-fan {
      stroke: var(--hairline-paper);
      stroke-width: 1;
    }

    .nx-section .nx-fan { transition: none; }
    .nx-section .nx-fan--out { opacity: 1; }

    .nx-section .nx-fan-tip {
      fill: var(--hairline-paper);
      pointer-events: none;
      transition: none;
    }

    .nx-section .nx-stage[data-walk="running"] .nx-fan,
    .nx-section .nx-stage[data-walk="done"] .nx-fan {
      transition: stroke 450ms var(--ease);
    }

    .nx-section .nx-stage[data-walk="running"] .nx-fan-tip,
    .nx-section .nx-stage[data-walk="done"] .nx-fan-tip {
      transition: fill 450ms var(--ease);
    }

    .nx-section .nx-stage[data-walk="done"] .nx-fan { stroke: var(--brass); }
    .nx-section .nx-stage[data-walk="done"] .nx-fan-tip { fill: var(--brass); }

    .nx-section .nx-trail {
      stroke: var(--brass);
      stroke-width: 1;
      stroke-dasharray: 1;
      stroke-dashoffset: calc(1 - var(--nx-t, 1));
      opacity: 1;
    }

    .nx-section .nx-dot {
      fill: var(--paper);
      stroke: var(--hairline-paper);
      stroke-width: 1;
      vector-effect: non-scaling-stroke;
      transition: fill 300ms var(--ease), stroke 300ms var(--ease);
    }

    .nx-section .nx-dot[data-lit="1"] {
      fill: var(--brass);
      stroke: var(--brass);
    }

    .nx-section .nx-tick { transition: stroke 300ms var(--ease); }
    .nx-section .nx-tick[data-lit="1"] { stroke: var(--brass); }

    .nx-section .nx-halo {
      fill: var(--brass);
      opacity: 0.35;
      pointer-events: none;
      transition: none;
    }

    .nx-section .nx-spark {
      fill: var(--brass-bright);
      opacity: 1;
      pointer-events: none;
    }

    :root:not([data-theme="dark"]) .nx-section .nx-spark { fill: var(--brass); }

    .nx-section .nx-stage[data-walk="running"] .nx-halo,
    .nx-section .nx-stage[data-walk="done"] .nx-halo {
      transition: opacity 450ms var(--ease);
    }

    .nx-section .nx-stage[data-walk="done"] .nx-halo { opacity: 0; }

    .nx-section .nx-stage[data-walk="done"] .nx-spark { opacity: 0; }

    /* Option D keeps two equal price cells in one quiet container. */
    .nx-section .nx-money {
      width: 100%;
      margin-top: clamp(3.5rem, 7vh, 5.5rem);
      text-align: center;
    }

    .nx-section .nx-money__heading {
      color: var(--ink-soft);
      font-family: var(--sans);
      font-size: var(--t-small);
      font-weight: 400;
      letter-spacing: 0;
      line-height: 1.45;
    }

    .nx-section .nx-money .pr {
      display: inline-flex;
      gap: 0.5rem;
      align-items: baseline;
      justify-content: center;
      min-width: 0;
    }

    .nx-section .nx-money .pr-figure {
      display: inline-block;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-lead);
      font-weight: var(--display-weight);
      letter-spacing: -0.015em;
      line-height: 1.1;
    }

    /* The early bird, 2026-09-06. The struck figure is the display face at
       under three quarters of the live one, in clay, so the live figure stays
       the loud one; the line through it is drawn, not text-decoration, so it
       is one clean brass stroke with a slight hand tilt on every face. */
    .nx-section .nx-money .pr-was {
      display: inline-block;
      position: relative;
      color: var(--clay);
      font-family: var(--display);
      font-size: calc(var(--t-lead) * 0.72);
      font-weight: var(--display-weight);
      letter-spacing: -0.015em;
      line-height: 1.1;
      text-decoration: none;
    }

    .nx-section .nx-money .pr-was::after {
      content: "";
      position: absolute;
      inset-inline: -0.08em;
      top: 52%;
      height: 0.07em;
      border-radius: 1px;
      background: var(--brass);
      transform: rotate(-6deg);
    }

    .nx-section .nx-money .pr-was__label {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }

    .nx-section .nx-money .pr-tag {
      color: var(--brass);
      font-family: var(--sans);
      font-size: var(--t-micro);
      font-weight: 500;
      letter-spacing: 0.06em;
      line-height: 1.4;
      text-transform: uppercase;
    }

    .nx-section .nx-money .pr-unit {
      display: inline-block;
      color: var(--clay);
      font-family: var(--sans);
      font-size: var(--t-small);
      font-weight: 400;
      letter-spacing: 0;
      line-height: 1.4;
      white-space: nowrap;
    }

    .nx-section .nx-money__note {
      margin-top: 0.75rem;
      color: var(--ink-soft);
      font-family: var(--sans);
      font-size: var(--t-micro);
      line-height: 1.5;
      text-align: center;
    }

    .nx-section .nx-money__plaque {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      width: 100%;
      max-width: 40rem;
      margin-inline: auto;
      padding: 1rem 1.25rem;
      border: 1px solid var(--hairline-paper);
      border-radius: 16px;
      background: var(--bone);
    }

    .nx-section .nx-money__plaque-column {
      display: grid;
      gap: 0.35rem;
      align-content: center;
      justify-items: center;
      min-width: 0;
      padding-inline: 1rem;
      text-align: center;
    }

    .nx-section .nx-money__plaque-column + .nx-money__plaque-column {
      border-inline-start: 1px solid var(--hairline-paper);
    }

    @media (max-width: 899px) {
      .nx-section {
        min-height: auto;
        padding-block: clamp(4rem, 9vh, 5.5rem);
      }

      .nx-section .sec-head { margin-bottom: clamp(3rem, 8vh, 4.5rem); }
      .nx-section .nx-stage { display: block; }

      /* A fan needs width the 48px rail column does not have, so mobile keeps one straight vertical line. */
      .nx-section .nx-desktop-curve { display: none; }

      .nx-section .nx-step {
        --nx-mobile-dot-centre: calc(0.32rem + 4.5px);
        --nx-mobile-row-gap: clamp(3.5rem, 12vw, 5rem);
        position: relative;
        display: grid;
        grid-template-columns: 48px minmax(0, 1fr);
        grid-column: 1 !important;
        grid-row: auto !important;
        gap: clamp(1rem, 5vw, 1.5rem);
        align-items: stretch;
        min-width: 0;
        margin: 0;
        padding: 0 0 var(--nx-mobile-row-gap);
        text-align: left;
      }

      .nx-section .nx-step--05 { padding-bottom: 0; }

      .nx-section .nx-mobile-rail {
        position: relative;
        display: block;
        grid-column: 1;
        align-self: stretch;
        width: 48px;
        min-height: 100%;
      }

      .nx-section .nx-step__copy {
        grid-column: 2;
        width: auto;
        min-width: 0;
        max-width: none;
        margin-inline: 0;
        text-align: left;
      }

      .nx-section .nx-step-meta { justify-content: flex-start; }

      /* Mobile keeps the two column grid, so the icon goes in the copy
         column above the copy and the rail is told to span both rows.
         Without that the rail segment would stop at the icon's row. */
      html[data-step-icons="on"] .nx-section .nx-mobile-rail { grid-row: 1 / -1; }

      html[data-step-icons="on"] .nx-section .nx-icon {
        grid-column: 2;
        margin: 0 0 0.75rem;
      }

      .nx-section .nx-mobile-dot {
        position: absolute;
        z-index: 1;
        top: 0.32rem;
        left: 50%;
        display: block;
        width: 9px;
        height: 9px;
        border: 1px solid var(--hairline-paper);
        border-radius: 50%;
        background: var(--paper);
        translate: -50% 0;
        transition: background-color 220ms var(--ease), border-color 220ms var(--ease);
      }

      .nx-section .nx-mobile-dot[data-lit="1"] {
        border-color: var(--brass);
        background: var(--brass);
      }

      .nx-section .nx-mobile-segment {
        position: absolute;
        top: var(--nx-mobile-dot-centre);
        bottom: calc((var(--nx-mobile-row-gap) + var(--nx-mobile-dot-centre)) * -1);
        left: 50%;
        display: block;
        width: 1px;
        /* Fills top down with the scroll, --nx-seg 0 to 1 from whats-next.js.
           A gradient rather than a transition, because the value moves every
           frame and a transition would only ever be chasing it. */
        background: linear-gradient(to bottom, var(--brass) calc(var(--nx-seg, 0) * 100%), var(--hairline-paper) 0);
        translate: -50% 0;
      }

      .nx-section .nx-mobile-segment[data-lit="1"] { background: var(--brass); }
      .nx-section .nx-step-title {
        max-width: 18ch;
        margin-inline: 0;
      }

      .nx-section .nx-step-line {
        max-width: 34ch;
        margin-inline: 0;
      }
      .nx-section .nx-money { margin-top: clamp(4rem, 11vh, 6rem); }
    }

    @media (max-width: 599px) {
      .nx-section .nx-money__plaque { grid-template-columns: minmax(0, 1fr); }
      .nx-section .nx-money__plaque-column { padding-inline: 0; }

      .nx-section .nx-money__plaque-column + .nx-money__plaque-column {
        margin-top: 1rem;
        padding-top: 1rem;
        border-block-start: 1px solid var(--hairline-paper);
        border-inline-start: 0;
      }
    }

    html.no-js .nx-section .nx-stage,
    html[data-reduced-motion="true"] .nx-section .nx-stage { --nx-t: 1 !important; }

    html.no-js .nx-section .nx-fan,
    html[data-reduced-motion="true"] .nx-section .nx-fan {
      stroke: var(--brass);
      transition: none !important;
    }

    html.no-js .nx-section .nx-fan-tip,
    html[data-reduced-motion="true"] .nx-section .nx-fan-tip {
      fill: var(--brass);
      transition: none !important;
    }

    html.no-js .nx-section .nx-halo,
    html.no-js .nx-section .nx-spark,
    html[data-reduced-motion="true"] .nx-section .nx-halo,
    html[data-reduced-motion="true"] .nx-section .nx-spark { opacity: 0; }

    html.no-js .nx-section .nx-halo,
    html[data-reduced-motion="true"] .nx-section .nx-halo { transition: none !important; }

    html.no-js .nx-section .nx-dot,
    html[data-reduced-motion="true"] .nx-section .nx-dot {
      fill: var(--brass);
      stroke: var(--brass);
    }

    html.no-js .nx-section .nx-tick,
    html[data-reduced-motion="true"] .nx-section .nx-tick {
      stroke: var(--brass);
    }

    html[data-reduced-motion="true"] .nx-section .nx-tick { transition: none !important; }

    html.no-js .nx-section .nx-mobile-dot,
    html[data-reduced-motion="true"] .nx-section .nx-mobile-dot {
      border-color: var(--brass);
      background: var(--brass);
    }

    html.no-js .nx-section .nx-mobile-segment,
    html[data-reduced-motion="true"] .nx-section .nx-mobile-segment { background: var(--brass); }

    html[data-reduced-motion="true"] .nx-section .nx-dot,
    html[data-reduced-motion="true"] .nx-section .nx-mobile-dot,
    html[data-reduced-motion="true"] .nx-section .nx-mobile-segment { transition: none; }

    @media (prefers-reduced-motion: reduce) {
      .nx-section .nx-stage { --nx-t: 1 !important; }

      .nx-section .nx-fan {
        stroke: var(--brass);
        transition: none !important;
      }

      .nx-section .nx-fan-tip {
        fill: var(--brass);
        transition: none !important;
      }

      .nx-section .nx-halo,
      .nx-section .nx-spark { opacity: 0; }

      .nx-section .nx-halo { transition: none !important; }

      .nx-section .nx-dot {
        fill: var(--brass);
        stroke: var(--brass);
      }

      .nx-section .nx-tick {
        stroke: var(--brass);
        transition: none !important;
      }

      .nx-section .nx-mobile-dot {
        border-color: var(--brass);
        background: var(--brass);
      }

      .nx-section .nx-dot,
      .nx-section .nx-mobile-dot,
      .nx-section .nx-mobile-segment { transition: none; }

      .nx-section .nx-mobile-segment { background: var(--brass); }
    }

    /* The close: statement, then two doors of deliberately unequal weight,
       then the note. cl- rather than close-: the .close-stack, .close-line
       and .close-note rules above describe the retired single stack and are
       left with it rather than given new meanings. */
    .cl-section .statement { margin-bottom: 2.6rem; }

    /* Same cap as the proof row, so the two sections share one edge. The
       primary column is wider by design: the grid is the first thing that
       says these are not twins, before any colour does. Top aligned, not
       stretched, so the smaller door is also the shorter one. */
    /* Not a whole screen, and not snapped, Can's call 2026-08-26. The close
       is the shortest thing on the page now: a line, two doors, done. */
    .draft-section.cl-section {
      min-height: 0;
      padding-block: var(--band-pad);
      scroll-snap-align: none;
    }

    /* stretch, not start, Can's call 2026-08-26: the secondary door used to
       stop short and the pair read as a card with a note beside it rather
       than as two doors. Same height now, the weight still carried by the
       primary's fill and its light.

       Three equal columns since 2026-08-27, Can's call. The primary had 1.6
       of the width when there were two doors; with three, the cards are
       drawn alike and the fill does the telling. */
    .cl-doors {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: clamp(1rem, 2vw, 1.5rem);
      align-items: stretch;
      width: min(100%, var(--s3-max));
      margin-inline: auto;
      text-align: left;
    }

    /* One shape for all three, Can's call 2026-08-27: the same padding, a
       column so every button is pushed to the foot with an auto margin and
       the three sit on one line whatever the copy above them does. */
    .cl-door {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      padding: clamp(24px, 2.6vw, 36px) clamp(22px, 2.4vw, 32px);
      border: 1px solid var(--hairline-paper);
      border-radius: 16px;
    }

    /* The primary carries the weight: bone fill on paper, the resting card
       shadow and the only brass button in the close. */
    .cl-door--primary {
      background: var(--bone);
      box-shadow: var(--draft-shadow-rest);
    }

    /* The secondaries are hairline and air: no fill, the ghost pill. */
    .cl-door--secondary { background: transparent; }

    .cl-door .btn { margin-top: auto; }

    /* Claude's own mark, in Claude's own colour. It was masked into page ink
       when this door was built on 2026-08-26 and Can asked for the colour
       back the same day. The file is one of the two provider marks that ship
       with a vendor colour in them (#d97757), so it can simply be an <img>;
       the six flat ones in the section 3 strip still need the mask, which is
       what assets/providers/README.md is describing. Decorative, so the
       heading beside it carries the meaning and the alt stays empty.

       Inside the heading since 2026-08-27, not above it. Sat above, it pushed
       the lesson's heading a mark and a margin below the other two, and the
       three doors are meant to read as one row. */
    .cl-mark {
      display: inline-block;
      width: 1em;
      height: 1em;
      margin-right: 0.3em;
      vertical-align: -0.12em;
      object-fit: contain;
    }

    /* The quiet twin of .btn. The primary door owns the brass fill and has to
       keep owning it, so the second door gets the same pill drawn as a
       hairline: present enough to press, never competing for the eye. */
    .btn--ghost {
      background: transparent;
      border: 1px solid var(--brass-hair);
      box-shadow: none;
      color: var(--ink);
      transition: border-color 450ms var(--ease), background-color 450ms var(--ease), color 450ms var(--ease);
    }

    .btn--ghost:hover {
      border-color: var(--brass);
      background: var(--brass-wash);
      color: var(--ink);
    }

    /* Balanced for the same reason as the paragraphs below: at 390 the
       secondary heading left "one" alone on its second line. */
    .cl-door h3 {
      color: var(--ink);
      font-family: var(--display);
      font-weight: var(--display-weight);
      text-wrap: balance;
    }

    /* One size for all three since 2026-08-27. The primary used to sit at
       h3 and the secondary two steps under it; alike cards want alike
       headings, and a third of the row does not hold h3 anyway. Between the
       two old sizes: a step over lead, well under h3. */
    .cl-door h3 {
      max-width: 18ch;
      font-size: calc(var(--t-body) * 1.5);
      letter-spacing: -0.012em;
      line-height: 1.2;
    }

    /* .s3-detail, value for value. Balanced although the page balances
       centred copy only: the sentence that sells the form is a line and a
       bit at 1366 and left "a pitch." alone on its last line; pretty did not
       catch a two-word orphan, balance splits it into two even lines. */
    .cl-door p {
      max-width: 34ch;
      margin-top: 14px;
      margin-bottom: 1.6rem;
      color: var(--ink-soft);
      font-size: var(--t-body);
      font-weight: 300;
      line-height: 1.6;
      text-wrap: balance;
    }

    /* The price line. "Free" on the first door and the hour's price on the
       second, the same slot so the two read against each other. Colour,
       upright, never italic: the emphasis rule in tokens.css. It sits tight
       under its paragraph and keeps the paragraph's bottom margin, which is
       what holds the button off it before the auto margin takes over.

       Written as .cl-door .cl-price on purpose: it is a p inside a door, and
       .cl-door p above outranks a lone class. The old .cl-free lost this
       fight, which is why "Free." was never actually brass. */
    .cl-door .cl-price {
      margin-top: -0.7rem;
      color: var(--brass);
      font-size: var(--t-small);
      font-weight: 500;
    }

    /* A text link, not a button. Hairline underline in brass-hair at rest,
       brass on hover. It stays at body size so it is findable without
       competing with the button. */
    .cl-link {
      display: inline-block;
      margin-top: 1.2rem;
      color: var(--ink);
      font-size: var(--t-body);
      text-decoration: underline;
      text-decoration-color: var(--brass-hair);
      text-decoration-thickness: 1px;
      text-underline-offset: 0.22em;
      transition: color 450ms var(--ease), text-decoration-color 450ms var(--ease);
    }
    .cl-link:hover { color: var(--brass-bright); text-decoration-color: currentColor; }

    /* .close-note's values, with .close-line's 1.8rem slot above it now that
       it follows the doors rather than a line. */
    .cl-note {
      max-width: 52ch;
      margin-top: 1.8rem;
      margin-inline: auto;
      color: var(--ink-soft);
      font-size: var(--t-small);
      text-wrap: balance;
    }

    /* ── The light on the primary door ───────────────────────────────────
       Ported from lab/section5-cards/light.html, variant l-live, on Can's
       pick of 2026-08-23 with the lab's knobs at Lap 14s and Edge 2px. At
       rest, one spark walks the door's rounded edge, one lap every fourteen
       seconds. Two sparks a cycle is a fairground; one every fourteen
       seconds is a watch. Under a mouse or a pen the spark dims to the
       hairline and the card's own hover, "The card under the hand" below,
       takes over: the lean and the drawn line, the same as its two
       neighbours.

       THE POOL AND THE RING WENT on 2026-09-06. Until then a brass ring and
       a pool of light followed the hand to the nearest edge, off four
       pointer properties assets/close-light.js wrote on the door. Can saw
       the pool on paper and did not want colour behind the door, and the
       ring had nothing left to do once the drawn line took the edge. The
       script went with them.

       The secondary door is never lit: the light is what says which door
       is which, which matters most on the dark ground, where bone is a
       three percent step off paper and the fill does no work.

       HOW. The spark is the card's own border: a conic gradient clipped to
       the border box under a bone fill clipped to the padding box, so the
       conic shows only in the 2px the border occupies and follows the 16px
       radius. Its three colour stops are registered properties so they can
       transition to the hairline colour under the hand.

       LIGHT THEME. brass-bright is the right hot on the dark ground and the
       wrong one on paper, where it is lighter than the brass round it and
       vanishes. When the root is not dark the hot stop deepens toward the
       ink. Not a palette remap: every colour is still a token.

       MOTION. html[data-reduced-motion="true"] above kills the animation
       and the transitions, so the spark parks at its start. */
    @property --cl-spark-angle { syntax: "<angle>"; inherits: false; initial-value: 0turn; }
    @property --cl-live-c1 { syntax: "<color>"; inherits: false; initial-value: transparent; }
    @property --cl-live-c2 { syntax: "<color>"; inherits: false; initial-value: transparent; }
    @property --cl-live-c3 { syntax: "<color>"; inherits: false; initial-value: transparent; }

    .cl-section .cl-door--primary {
      position: relative;
      /* The lab's knobs, baked: Lap 14s, Edge 2px. */
      --cl-lap: 14s;
      --cl-edge: 2px;
      --cl-hot: var(--brass-bright);
      --cl-spark-angle: 0turn;
      --cl-live-c1: var(--brass-hair);
      --cl-live-c2: var(--brass);
      --cl-live-c3: var(--cl-hot);
      border-width: var(--cl-edge);
      border-color: transparent;
      background-color: transparent;
      background-image:
        linear-gradient(var(--bone), var(--bone)),
        conic-gradient(from var(--cl-spark-angle),
          var(--hairline-paper) 0%,
          var(--hairline-paper) 68%,
          var(--cl-live-c1) 76%,
          var(--cl-live-c2) 80.5%,
          var(--cl-live-c3) 82%,
          var(--cl-live-c2) 83.5%,
          var(--cl-live-c1) 88%,
          var(--hairline-paper) 96%,
          var(--hairline-paper) 100%);
      background-origin: border-box;
      background-clip: padding-box, border-box;
      animation: cl-spark-turn var(--cl-lap) linear infinite;
      /* The three colours, then the lean and the shadow the block below
         drives; the list is one so neither side loses the other's. */
      transition:
        --cl-live-c1 750ms var(--ease),
        --cl-live-c2 750ms var(--ease),
        --cl-live-c3 750ms var(--ease),
        transform 480ms var(--ease),
        box-shadow 480ms var(--ease);
    }

    @keyframes cl-spark-turn { to { --cl-spark-angle: 1turn; } }

    /* Under the hand the spark goes to hairline so the drawn line is the
       only light on the edge. In over 320ms, out over the 750ms above; the
       lean follows at 160ms like every other card. */
    .cl-section .cl-door--primary:hover {
      --cl-live-c1: var(--hairline-paper);
      --cl-live-c2: var(--hairline-paper);
      --cl-live-c3: var(--hairline-paper);
      transition-duration: 320ms, 320ms, 320ms, 160ms, 480ms;
    }

    /* Light theme. See the note at the top of this block. */
    :root:not([data-theme="dark"]) .cl-section .cl-door--primary {
      --cl-hot: color-mix(in srgb, var(--brass) 78%, var(--ink));
    }

    /* ── The card under the hand ─────────────────────────────────────────
       Can's pick of 2026-09-06 from lab/card-hover, Trace: under a mouse or
       a pen the card leans toward the pointer in perspective, lifts four
       pixels, and a brass line draws itself clockwise round its edge from
       the top and holds until the hand leaves. Nothing else follows the
       pointer: no pool, no gleam, no colour on the glass. Both rows carry
       it, the three testimonial cards and the three doors, so the page has
       one hover and not two.

       HOW. assets/card-hover.js writes --nx and --ny on the card, where the
       pointer lies from the centre, -1 to 1, and prepends the .fx-draw
       span: a conic gradient from the top, masked to a 1.5px ring, whose
       end angle is a registered property so it can transition. The lean is
       one transform reading the two numbers, and the shadow leans with it.
       The glass brightens a step under the hand the way the pill does.
       With the script absent the card lifts and the line does not draw.

       The lab's Intensity knob at 1 shipped first, 5 and 6 degrees of lean
       on both rows. Can cut it on 2026-09-08 off the live site, where a
       card that is also sliding read its lean as distortion: the doors keep
       half, the testimonials a fifth, as --fx-lean on each row, a share of
       the lab's angles. The shadow's drift scales with it; the lift, the
       line and the glass do not. Still 900ms for the line. The other
       seventeen treatments stay in the lab.

       TOUCH. A finger scrolls, not points: the script ignores it, and under
       (hover: none) a tap's hover is the lift alone.

       MOTION. Under reduced motion nothing leans and the line arrives
       without drawing. */
    @property --fx-draw { syntax: "<angle>"; inherits: false; initial-value: 0deg; }

    /* The proof row's vanishing point sits on its track, with the slider
       rules: perspective reaches an element's children, and the cards are
       the track's now, not the row's. */
    .cl-doors { perspective: 1100px; }

    .fy-card,
    .cl-door {
      --nx: 0;
      --ny: 0;
      transition: transform 480ms var(--ease), box-shadow 480ms var(--ease);
    }

    /* How far each row leans, as a share of the lab's 5 and 6 degrees. */
    .cl-door { --fx-lean: 0.5; }
    .fy-card { --fx-lean: 0.2; }

    .fy-card:hover,
    .cl-door:hover {
      transition-duration: 160ms, 480ms;
      transform:
        translateY(-4px)
        rotateX(calc(var(--ny) * -5deg * var(--fx-lean, 1)))
        rotateY(calc(var(--nx) * 6deg * var(--fx-lean, 1)))
        scale(1.014);
      box-shadow:
        calc(var(--nx) * -14px * var(--fx-lean, 1))
        calc(var(--ny) * -10px * var(--fx-lean, 1) + 30px)
        60px -24px
        rgba(0, 0, 0, calc(var(--g-shadow, 0.55) * 1.6));
    }

    .fy-card:hover::after,
    .cl-door--secondary:hover::after {
      background: color-mix(in srgb, var(--g-tint) 60%, rgba(231, 231, 231, 0.16));
    }
    :root[data-theme="dark"] .fy-card:hover::after,
    :root[data-theme="dark"] .cl-door--secondary:hover::after {
      background: color-mix(in srgb, var(--g-tint) 40%, rgba(255, 255, 255, 0.06));
    }

    /* The line. Two classes deep on purpose: glass.css sets every child of
       a card relative at z-index 2, and this has to sit under the tint. */
    .fy-card .fx-draw,
    .cl-door .fx-draw {
      --fx-draw: 0deg;
      position: absolute;
      inset: -1px;
      z-index: 1;
      padding: 1.5px;
      border-radius: inherit;
      background: conic-gradient(from -90deg,
        var(--brass) 0deg, var(--brass-bright) var(--fx-draw), transparent var(--fx-draw));
      -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
      -webkit-mask-composite: xor;
      mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
      mask-composite: exclude;
      opacity: 0;
      pointer-events: none;
      transition: opacity 600ms var(--ease) 100ms, --fx-draw 0ms 700ms;
    }
    .fy-card:hover .fx-draw,
    .cl-door:hover .fx-draw {
      --fx-draw: 360deg;
      opacity: 1;
      transition: opacity 300ms var(--ease), --fx-draw 900ms cubic-bezier(0.2, 0.7, 0.2, 1);
    }

    html[data-reduced-motion="true"] .fy-card:hover,
    html[data-reduced-motion="true"] .cl-door:hover { transform: none; }
    html[data-reduced-motion="true"] .fy-card .fx-draw,
    html[data-reduced-motion="true"] .cl-door .fx-draw { transition: opacity 300ms var(--ease); }
    @media (prefers-reduced-motion: reduce) {
      .fy-card:hover, .cl-door:hover { transform: none; }
      .fy-card .fx-draw, .cl-door .fx-draw { transition: opacity 300ms var(--ease); }
    }
    @media (hover: none) {
      .fy-card:hover, .cl-door:hover { transform: translateY(-4px); }
    }

    /* Three across from 1000px up, stacked under it. Two across at tablet
       widths would leave one door alone on a second row, which reads as an
       afterthought, and the primary is not one. */
    @media (max-width: 1000px) {
      .cl-doors { grid-template-columns: minmax(0, 1fr); }
    }

    .phyllo {
      position: absolute;
      z-index: 0;
      inset: 0;
      display: block;
      width: 100%;
      height: 100%;
      pointer-events: none;
    }

    /* Content is visible by default and stays that way. Nothing on the page
       animates in on scroll any more: the reveal that used to run here snapped
       copy the reader could already see to zero and faded it back, one child
       after another, which read as a wink on every section past the deck
       (Can, 2026-09-13). The [data-reveal] marks stay in the markup but are
       inert. The one exception is the close statement, whose four lines fade
       in one after the other; the script parks them only once it has confirmed
       it can run the fade, so no JS, reduced motion and print all see the
       lines as plain copy. */
    [data-reveal] {
      opacity: 1;
      transform: none;
    }

    html.reveal-armed .turn-lines[data-reveal="lines"] > .turn-line {
      opacity: 0;
    }

    html[data-reduced-motion="true"] {
      scroll-behavior: auto;
      scroll-snap-type: none;
    }

    html[data-reduced-motion="true"] .draft-section {
      scroll-snap-align: none;
    }

    html[data-reduced-motion="true"] *,
    html[data-reduced-motion="true"] *::before,
    html[data-reduced-motion="true"] *::after {
      scroll-behavior: auto !important;
      animation: none !important;
      transition: none !important;
    }

    /* The squeezed band: two columns, but not enough width to give the copy
       column its head. It used to end at 1040px. The 2026-08-19 H1 tops out at
       102.4px, and its longest authored line — "Your first AI" — sets 496px at
       that size, so the base rule needs a copy column of about 516px before it
       is safe. That does not arrive until roughly 1200px; between 1041 and
       1160 the line wrapped and the hero silently became FOUR lines with "AI"
       stranded on its own. The band now runs to 1199px for that reason, and
       the size inside it is set off the column it has to fit rather than off
       the old 4.45rem ceiling. */
    @media (max-width: 1199px) and (min-width: 901px) {
      .sphere-uniform-inner {
        grid-template-columns: minmax(410px, 1fr) minmax(0, 1fr);
        padding-left: 48px;
      }

      /* No headline size of its own any more. It had one, 7vw, which at
         1199 stepped the headline UP from 67px to 84px as the window got
         narrower; Can caught it 2026-09-03. The base rule's 5.6vw with its
         4rem floor runs straight through this band: 67px at 1199, on the
         64px floor from 1143 down, and the floor carries into the stacked
         layout below 900, so the size never rises as the screen shrinks. */

      /* Smaller and further right since 2026-09-03, same reason as the band
         above: at 1024 the old 760px stage sat over the whole headline. */
      .sphere-uniform-stage {
        width: calc(clamp(440px, 52vw, 620px) * var(--hero-stage-scale));
        transform: translate(34%, -50%);
      }
    }

    @media (max-width: 900px) {
      .sphere-uniform-header {
        padding-inline: var(--page-inset);
      }

      /* Stacked, sphere FIRST, Can 2026-09-03. It was copy over sphere with
         the inner held to a full screen, which on a tall phone window put
         the headline a screen down and the sphere a screen below that.
         Content height now, no min-height, and the sphere is a fixed row
         above the copy so the page opens on the mark. */
      /* Content height, not a screen: in a tall phone window the old
         min-height left most of a screen empty under the lede. */
      .sphere-uniform-hero {
        min-height: 0;
      }

      /* The sphere row is the sphere's own height, so nothing is clipped
         flat. The stage below is 1.06 times the clamp; the field fills
         about 92% of the stage box. */
      /* Air around the copy, Can 2026-09-03: "too squeezed". The row gap
         is the space between sphere and headline; the bottom padding is
         the space under the lede before the next section. */
      .sphere-uniform-inner {
        grid-template-columns: 1fr;
        /* The row is the whole stage plus air, so the sphere's rim is never
           cut flat at the top or bottom. Can's phone, 2026-09-03. */
        grid-template-rows: calc(clamp(340px, 90vw, 600px) * 1.06 + 40px) auto;
        gap: 28px;
        min-height: 0;
        padding: 100px var(--page-inset) 80px;
      }

      .sphere-uniform-copy {
        max-width: 680px;
        padding-top: 0;
      }

      .sphere-uniform-visual {
        order: -1;
        /* Bleeds the stage back out past whatever the inner is inset by, so
           this has to track the same token the inner's padding uses. */
        width: calc(100% + (2 * var(--page-inset)));
        margin-left: calc(-1 * var(--page-inset));
        overflow: hidden;
      }

      /* Centred in its row. translate is set here rather than through
         --hero-offset-x because the tune panel writes that property inline
         on the element, which would beat any media query. */
      /* Centred, Can 2026-09-03: "it needs to be in the center". The push
         off the right edge is a two column thing; stacked, the sphere sits
         in the middle of its row and shows whole. */
      .sphere-uniform-stage {
        right: 50%;
        width: calc(clamp(340px, 90vw, 600px) * var(--hero-stage-scale));
        translate: 0 0;
        transform: translate(50%, -50%);
      }

      .sphere-uniform-anchor {
        width: 250px;
      }
    }

    @media (max-width: 899px) {
      html {
        scroll-snap-type: none;
      }

      /* Half the band on a phone, Can 2026-09-03: the desktop value put a
         run of 224px between every pair of bands, which on a 390 screen is
         over a quarter of it with nothing in it. The token is what moves,
         so all five bands tighten together and the rhythm holds. */
      :root { --band-pad: clamp(3.25rem, 7vh, 5rem); }

      .draft-section {
        min-height: auto;
        padding-block: clamp(3rem, 6vh, 4rem);
        scroll-snap-align: none;
      }
    }

    @media (max-width: 760px) {
      .cards {
        grid-template-columns: minmax(0, 1fr);
      }

      .ledger-head {
        display: none;
      }

      .ledger-row {
        grid-template-columns: minmax(0, 1fr);
        gap: 0.45rem;
      }

      .ledger-part .mono-label {
        display: block;
      }

      .promise {
        gap: 0.2rem 0.6rem;
      }

      .promise-word {
        min-width: 0;
      }

      .promise-text {
        flex: 1 1 100%;
      }
    }

    @media (max-width: 560px) {
      .sphere-uniform-header {
        grid-template-columns: 1fr;
        padding: 24px 22px 0;
      }

      .sphere-uniform-header__balance {
        display: none;
      }

      .aita-wordmark {
        font-size: 23px;
      }

      .sphere-uniform-inner {
        grid-template-rows: calc(clamp(300px, 90vw, 480px) * 1.06 + 40px) auto;
        gap: 18px;
        padding: 80px 22px 64px;
      }

      .sphere-uniform-title {
        font-size: clamp(3rem, 13vw, 3.35rem);
        letter-spacing: -0.04em;
        line-height: 0.96;
      }

      .sphere-uniform-lede {
        max-width: 39ch;
        margin-top: 1.5rem;
        font-size: 16px;
        line-height: 1.62;
      }

      .sphere-uniform-stage {
        width: calc(clamp(300px, 90vw, 480px) * var(--hero-stage-scale));
      }

      .sphere-uniform-anchor {
        width: 210px;
      }

      .lab-tools {
        --lab-inset: 12px;
      }
    }

    @media (prefers-reduced-motion: reduce) {
      html {
        scroll-behavior: auto;
        scroll-snap-type: none;
      }

      .draft-section {
        scroll-snap-align: none;
      }

      *,
      *::before,
      *::after {
        scroll-behavior: auto !important;
        animation: none !important;
        transition: none !important;
      }

      .card:hover,
      .btn:hover {
        translate: none;
      }
    }

    @media print {
      html {
        scroll-snap-type: none;
      }

      .phyllo {
        display: none;
      }

      .sphere-uniform-hero,
      .draft-section {
        min-height: 0;
        padding-block: 1.5rem;
        break-inside: avoid;
        scroll-snap-align: none;
      }

      [data-reveal],
      html.reveal-armed .turn-lines[data-reveal="lines"] > .turn-line {
        opacity: 1 !important;
        transform: none !important;
      }
    }

    /* ══ SECTION 2 · THE FOUR-BEAT FLOW ═══════════════════════════════════
       Ported from lab/section2b on 2026-08-19, replacing the single-beat
       analysis section that shipped before it. Everything is s2- namespaced so
       it can never collide with the draft sections it sits between — the
       lab's bare .copy / .eyebrow / .line / .w would all have collided.

       500vh of scroll driving one sticky pane. Nothing inside it scrolls
       visually: the stage and the copy hold still and the scroll position is
       only the clock. Beats: the noise → the analysis → the hires → the
       arrangement.

       Deliberately NOT a .draft-section: scroll-snap-align on a block this
       tall would fight the sticky pane every time the reader crosses it. */
    .s2-flow {
      /* 500vh, down from 780 (and 950 before that). Founder call, 2026-08-20:
         the section was asking for nearly seven screens of scroll and every
         beat wanted to move sooner. Everything downstream reads a normalised
         0..1 progress, so this one number is the whole tempo — cutting it
         speeds all four beats by the same proportion and changes nothing
         about their staging. Actual travel is height minus the 100svh pin:
         400vh, down from 680. */
      height: 500vh;
      /* --paper since 2026-08-26, Can's call: every section on the page now
         shares one ground in each theme, so nothing alternates warm and cool
         as the reader scrolls. This was --bone, and it and the integrations
         wall were the only two that were. */
      background: var(--paper);
      /* The pill ground on the canvas, which has to match the section it is
         drawn on. Declared here rather than assumed by the script; it now
         agrees with the lab, which always ran this engine on --paper. */
      --s2-ground: var(--paper);
      isolation: isolate;
    }

    .s2-flow__pin {
      position: sticky;
      top: 0;
      display: grid;
      /* The text side gains a little on the stage: Satoshi at 300 sets
         noticeably wider than Gambetta did at the same size, and an authored
         line break is pointless if the line it makes then wraps anyway. */
      /* The copy pane is a fixed share of the SCREEN, not a share of what is
         left after the stage. Founder call, 2026-08-20: the block was reading
         as centred — it started at exactly 50% and its longest line stopped
         84% across, with a quarter of the screen empty to the right of it.

         Two fr columns could not fix that. The headline is sized off this
         column in cqw, so widening the column to push the ink right made the
         type bigger and pulled the START left; narrowing it moved the start
         right and shrank the type. Pinning the column to 38.5vw and flushing
         it against the right inset fixes both ends: the pane runs from 51.5%
         to 90% at every width, and the type is compensated back to the size it
         was approved at by the cqw multipliers below.

         Left of it, the stage now starts at the page inset like everything
         else instead of nearly touching the edge. */
      /* Locked past 3000px the way the hero is, Can's ruling of 2026-09-06
         on an ultra wide screen: the content sits centred in a 2400 shell
         and the copy pane stops at its share of that shell (924px is 38.5%
         of 2400), so the cqw type it carries stops with it. Under 3000px
         both numbers resolve to what they were. The pin itself stays full
         bleed, since it is the ground. */
      grid-template-columns: minmax(0, 1fr) min(38.5vw, 924px);
      gap: clamp(24px, 4vw, 64px);
      height: 100svh;
      overflow: hidden;
      padding-inline: max(var(--page-inset), calc((100% - 2400px) / 2));
      /* THIS is the ground the reader actually sees for section 2, not the
         .s2-flow background below it. The pin is sticky and 100svh, so it
         covers the section for the whole 500vh of scroll and the section's
         own background is never visible. Changing .s2-flow alone on
         2026-08-26 looked like it had done nothing for exactly that reason.
         --paper since then, so section 2 matches the roster below it. */
      background: var(--paper);
    }

    .s2-flow__stage {
      position: relative;
      min-width: 0;
    }

    .s2-flow__stage canvas {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
    }

    /* ── Figures ─────────────────────────────────────────────────────────
       People are renders, information is canvas. Each figure is Can's
       character art at 512 square: a body PNG under, and for the owner the
       four traced expressions stacked on it with the vector face on top.
       Position, scale and opacity are driven from section2b.js on the same
       clock as the particles. isolation keeps the hires' multiply tint
       inside its own figure rather than letting it reach the canvas. */
    .s2-figure {
      position: absolute;
      top: 0;
      left: 0;
      isolation: isolate;
      will-change: transform, opacity;
    }

    /* placeFigure writes a width and nothing else, so the square comes from
       here. The old SVG got its height from the viewBox aspect. */
    .s2-cast { aspect-ratio: 1; }

    .s2-cast__body { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

    /* The exact frames and the vector head carry inline percentages written
       in the smile's head frame, so they resolve against this box. */
    .s2-cast__exact { position: absolute; display: block; pointer-events: none; }
    .s2-cast__vec { position: absolute; display: block; overflow: visible; pointer-events: none; }

    /* The hires wear a clay wash: one flat colour multiplied through the
       render's own alpha, so they read as staff rather than as the client.
       Can chose the clay against both grounds, so it stays a literal rather
       than following --clay, which moves with the theme.

       Neutral since 2026-08-31 with the copper palette. It was #8A8171, the
       old warm clay, picked when both grounds were warm; against a grey page
       it was the one thing on the section still reading brown. Same lightness
       and the same job, temperature removed. */
    .s2-cast__tint {
      position: absolute;
      inset: 0;
      pointer-events: none;
      background: #7F7E7A;
      opacity: 0.6;
      mix-blend-mode: multiply;
      -webkit-mask-image: url("/assets/characters/smile-512.webp");
      mask-image: url("/assets/characters/smile-512.webp");
      -webkit-mask-size: contain;
      mask-size: contain;
      -webkit-mask-repeat: no-repeat;
      mask-repeat: no-repeat;
      -webkit-mask-position: center;
      mask-position: center;
    }

    /* ── The copy pane ───────────────────────────────────────────────────
       The founder's hard constraint: the text never travels. Four blocks
       stacked in the same grid cell, one visible at a time, cross-fading
       as the scroll crosses a beat boundary. The 14px lift on entry is a
       settle-into-place, not a scroll — nothing ever leaves upward. */
    .s2-flow__text {
      display: grid;
      align-content: center;
      min-width: 0;
      padding-block: 8vh;
      /* The headline is sized off this column, not the viewport. A vw-based
         clamp caps out and then the type stays small however much room the
         screen gains — on a wide monitor the copy sat in a corner of its own
         pane. In cqw it keeps the same proportion of the column at every
         width, which is what makes it read as set rather than as leftover. */
      container-type: inline-size;
    }

    .s2-copy-stack { display: grid; }

    .s2-copy {
      grid-area: 1 / 1;
      /* Set against the right rail, 2026-08-20. Founder call: "push that
         container to the right side."

         Left-aligned, it could not be pushed. The block starts where its
         column starts, and the column has to be wide enough for the longest
         authored line in the set — "Then we build your AI employees." — or
         that line wraps and the beat reads four lines deep. Those two pull
         opposite ways: narrow the column to move the start right, and beat 3
         breaks; widen it to hold beat 3, and the start slides back to the
         middle. Beat 1, whose lines are the shortest in the set, then stops a
         fifth of the screen short of the right edge whatever you do.

         Anchoring the other end settles all of it. Every line in every beat
         now finishes on the same rail as the roster and the header below, the
         ragged edge falls on the left where the stage's own air is, and the
         short beats sit further right than the long ones instead of leaving a
         hole. Nothing travels between beats: the anchor is the same edge for
         all four. */
      text-align: right;
      /* No measure cap. A paragraph needs one; two authored lines do not, and
         a fixed 40rem box was quietly overriding the column-width sizing —
         the type grew, the box it had to fit in did not, and the lines
         wrapped on wide screens while looking small. The grid column is the
         measure now, which is the same thing the cqw sizing reads. */
      max-width: none;
      opacity: 0;
      transform: translateY(14px);
      transition: opacity 560ms var(--ease), transform 560ms var(--ease);
      pointer-events: none;
    }

    .s2-copy[data-active="true"] {
      opacity: 1;
      transform: none;
      pointer-events: auto;
    }

    /* Set against the headline rather than with it: the sans in caps reads
       as a label on the display sentence, which is the job. Tracked open —
       caps set solid at this size close up and stop scanning as a word. It
       names the beat and nothing else.

       Founder pick, 2026-08-19, chosen in lab/hero-copy against the longest
       label the section has to carry ("The arrangement"). Gambetta was tested
       in caps and in sentence case and rejected: the face stays Satoshi. What
       changed is scale — the eyebrow was a 15px caption and is now a peer of
       the headline it introduces, at roughly double the size and tracked to
       0.300em to keep the caps scanning as a word rather than a block.

       The size is expressed in the SAME UNIT as the headline, not in px. That
       matters more than it looks. The founder tuned this at 29.5px against a
       67.2px H2 on a 2586px screen — a ratio of 0.439. The H2 sizes off cqw,
       so it shrinks with the text column; a px eyebrow does not. Pinned at
       29.5px the eyebrow held while the headline fell away beneath it, and by
       1440px it was 33px against a 39.9px H2 — 83% of the headline it is meant
       to introduce, which is a different design from the one that was
       approved.

       5.4cqw x 0.439 = 2.37cqw reproduces the approved proportion at every
       width, including exactly the 33px the founder saw at 2586px. The ceiling
       is the H2's own 6rem ceiling scaled by the same ratio. The floor keeps
       the label readable on the stacked narrow stage, where the H2 hits its
       own floor too.

       2026-08-20: the copy column narrowed from ~1.08fr of the leftovers to a
       flat 38.5vw so the pane sits against the right inset (see the pin), and
       the type came down with the founder's approval to buy the room the right
       rail needs — 5.58cqw for the H2, 2.45 here. The ratio is still 0.439
       exactly; only the scale moved.

       It stays inside the same calc() as the headline so --s2-copy-scale moves
       both together. */
    .s2-eyebrow {
      display: block;
      margin-bottom: 1.1rem;
      color: var(--brass);
      font-family: var(--sans);
      font-size: calc(clamp(12px, 2.45cqw, 2.63rem) * var(--s2-copy-scale, 1));
      font-weight: 500;
      letter-spacing: 0.300em;
      text-transform: uppercase;
    }

    .s2-copy h2 {
      margin: 0;
      font-family: var(--display);
      /* Copy ratified 2026-08-19. 5.4cqw was originally tuned so the longest
         line then in the set held one line; the ratified copy is longer, so
         three of the four second lines now wrap to two visual lines and each
         beat reads as a three-line block. That is accepted, not accidental:
         holding every line unwrapped needs 4.5cqw, which drops the headline
         from 40px to 33px at 1440 AND breaks the founder-approved 0.439
         eyebrow:H2 ratio, since the eyebrow is sized off the same container.
         Measured across 390-1920px. If the copy grows again, re-measure with
         distinct word-span tops - getClientRects lies here, because the engine
         wraps every word in its own span.

         2026-08-20: reads 5.58cqw, not 5.4, off a column that is now a flat
         38.5vw instead of ~52% of the screen — so the rendered size comes DOWN
         by 13-18%, to 51px at 2120 and 62px at 2560. Founder call, made to buy
         the room the right rail needs: at the approved size the longest first
         line does not fit a column narrow enough to sit against the rail, and
         beat 3 reads four lines deep.

         5.58 is the largest multiplier that keeps every first line unwrapped,
         with ~10% to spare at 1366, 1440, 1920, 2120 and 2560. Re-measure with
         qa/s2-line-probe.mjs before moving it — and mind that the second lines
         are MEANT to wrap to two; it is the first lines this number protects. */
      font-size: calc(clamp(1.35rem, 5.58cqw, 6rem) * var(--s2-copy-scale, 1));
      font-weight: var(--display-weight);
      letter-spacing: 0.004em;
      line-height: 1.18;
      text-wrap: balance;
    }

    /* One accent word per headline, in brass and upright. Founder rule,
       2026-08-18: emphasis is either colour or italic, never both on the same
       text. Colour marks a word; italic is reserved for a whole line spoken
       in a different voice, and that line is never coloured. */
    .s2-copy h2 em {
      color: var(--brass);
      font-style: normal;
    }

    /* Each sentence gets its own line, authored rather than wrapped. The
       second line steps down in weight: the first is the claim everyone
       already makes, the second is the part nobody says. */
    .s2-copy h2 .s2-line { display: block; }
    .s2-copy h2 .s2-line + .s2-line { color: var(--ink-soft); }

    /* The headline resolves word by word rather than as a block: each word
       lifts, sharpens and lands, left to right. section2b.js wraps the words
       and sets --i; the delay is the only thing that differs between them. */
    .s2-copy h2 .s2-w {
      display: inline-block;
      opacity: 0;
      transform: translateY(0.24em);
      filter: blur(3px);
      transition:
        opacity 640ms var(--ease),
        transform 640ms var(--ease),
        filter 640ms var(--ease);
    }

    .s2-copy[data-active="true"] h2 .s2-w {
      opacity: 1;
      transform: none;
      filter: none;
      transition-delay: calc(var(--i) * 58ms);
    }

    /* ── Lab chrome, localhost only ──────────────────────────────────────
       section2b.js mounts the beat readout and the size stepper only when
       the hostname is local, matching the hero's tune panel. These rules are
       inert on the shipping page because the elements never exist there. */
    .s2-hud,
    .s2-hud__size {
      position: fixed;
      bottom: 14px;
      z-index: 10;
      padding: 7px 12px;
      border: 1px solid var(--hairline-paper);
      border-radius: 6px;
      background: color-mix(in srgb, var(--paper) 82%, transparent);
      color: var(--ink-soft);
      font-family: var(--mono);
      font-size: 11px;
      letter-spacing: 0.04em;
      backdrop-filter: blur(6px);
      pointer-events: none;
    }

    /* Bottom RIGHT, not bottom left as in the lab: on this page the hero's
       own localhost chrome (Light / Tune / Lines) owns the bottom left, and
       the lab's position landed the beat readout straight on top of it.
       Stacked vertically rather than side by side, because the readout's
       width changes with the beat label and anything parked beside it has to
       be positioned off a width that moves. */
    .s2-hud { right: 16px; }

    .s2-hud__size {
      right: 16px;
      bottom: 52px;
      border-color: var(--brass-hair);
      color: var(--brass);
      letter-spacing: 0.14em;
      text-transform: uppercase;
    }

    @media (max-width: 900px) {
      .s2-flow__pin {
        grid-template-columns: minmax(0, 1fr);
        /* Capped, Can 2026-09-03: in a tall phone window 52svh gave the
           stage a thousand pixels and the figures sat in the top half of an
           empty room. A real phone never reaches the cap. */
        grid-template-rows: min(52svh, 440px) auto;
        /* Centred in the pinned screen, Can 2026-09-03. The pin is a full
           screen for the whole scroll, and stage plus copy do not fill a
           tall one; top aligned, the leftover was a hole under the copy.
           Centred, it is air above and below, which reads as composition.
           A real phone has little leftover either way. */
        align-content: center;
        /* dvh, not svh: on a phone the pin is the screen as it IS, growing
           when the browser's bars retract, so the centring holds instead of
           leaving a band of empty ground under the frame. The top padding
           keeps the fixed header out of the centring, so the scene is not
           tucked under it. Can's phone, 2026-09-03. */
        height: 100dvh;
        padding-top: 84px;
        gap: 0;
        /* Stacked, so there is no right rail to sit against and the page
           inset would only cost the stage width it needs. This is the value
           the section shipped with before the inset pass. */
        padding-inline: clamp(20px, 4vw, 56px);
      }

      .s2-flow__stage { border-bottom: 1px solid var(--hairline-paper); }
      .s2-flow__text { align-content: start; padding-block: 4vh; }
      /* Stacked, the copy has the full width and no rail to sit against, so
         the right alignment that carries the desktop composition would just
         read as an odd ragged left margin. */
      .s2-copy { text-align: left; }
      .s2-copy h2 {
        /* The stacked layout gives the copy the full width, so the column
           measure that governs above would set it far too large here. */
        font-size: calc(clamp(1.5rem, 5.6vw, 1.9rem) * var(--s2-copy-scale, 1));
        margin-bottom: 1rem;
      }
    }

    /* Reduced motion gets the finished picture, not a broken one: the stage
       renders the final arrangement and all four copies read in normal flow.
       section2b.js stamps data-s2-static on the root under ?reduced-motion=1
       and when the site's own flag is set, so the two selector sets below
       must stay identical. */
    @media (prefers-reduced-motion: reduce) {
      .s2-copy h2 .s2-w { opacity: 1; transform: none; filter: none; transition: none; }
      .s2-copy { position: static; opacity: 1; transform: none; transition: none; pointer-events: auto; }
      .s2-copy + .s2-copy { margin-top: 3.5rem; }
      .s2-copy-stack { display: block; }
      .s2-flow { height: auto; }
      .s2-flow__pin { position: static; height: auto; min-height: 100svh; }
      .s2-flow__stage { min-height: 56svh; }
    }

    /* No JavaScript: the same finished picture. Without this the section
       would ship three of its four blocks at opacity 0 permanently, because
       nothing is left to set data-active on them — two thirds of the
       section's copy unreadable rather than merely unanimated. The stage is
       canvas and cannot draw at all, so it is removed instead of left as a
       blank hole, and the four blocks simply read down the page. */
    :root.no-js .s2-flow { height: auto; }
    :root.no-js .s2-flow__pin { position: static; display: block; height: auto; min-height: 0; padding-block: 10vh; }
    :root.no-js .s2-flow__stage { display: none; }
    :root.no-js .s2-copy-stack { display: block; }
    :root.no-js .s2-copy { position: static; opacity: 1; transform: none; transition: none; pointer-events: auto; }
    :root.no-js .s2-copy + .s2-copy { margin-top: 3.5rem; }

    :root[data-s2-static] .s2-copy h2 .s2-w { opacity: 1; transform: none; filter: none; transition: none; }
    :root[data-s2-static] .s2-copy { position: static; opacity: 1; transform: none; transition: none; pointer-events: auto; }
    :root[data-s2-static] .s2-copy + .s2-copy { margin-top: 3.5rem; }
    :root[data-s2-static] .s2-copy-stack { display: block; }
    :root[data-s2-static] .s2-flow { height: auto; }
    :root[data-s2-static] .s2-flow__pin { position: static; height: auto; min-height: 100svh; }
    :root[data-s2-static] .s2-flow__stage { min-height: 56svh; }

    @media print {
      .s2-flow { height: auto; }
      .s2-flow__pin { position: static; height: auto; min-height: 0; break-inside: avoid; }
      .s2-flow__stage { display: none; }
      .s2-flow__text { padding-block: 1.5rem; }
      .s2-copy-stack { display: block; }
      .s2-copy { position: static; opacity: 1; transform: none; }
      .s2-copy + .s2-copy { margin-top: 1.5rem; }
      .s2-copy h2 .s2-w { opacity: 1; transform: none; filter: none; }
    }

    /* ══ SECTION 3 · THE ROSTER ═══════════════════════════════════════════
       Ported from lab/section3-jobs on 2026-08-19 after Can approved the lab,
       and placed directly under section 2 on his instruction. It retired the
       old SECTION 4 — WHAT IT DOES, four task cards under the same eyebrow
       that said the weaker version of the same thing.

       Everything is s3- namespaced, on the same reasoning as s2-: the lab's
       bare .rail / .main / .side / .mark / .tag / .fig would all have collided
       with the draft-section system this sits between.

       Deliberately NOT a .draft-section. The picker is interactive and taller
       than a viewport on a laptop, so a 100vh min-height plus scroll-snap
       would trap the reader mid-agent. */
    /* A full screen tall, founder call 2026-08-20: "it should be like a full
       screen height, vertical height."

       min-height rather than height, and align-content:center rather than a
       fixed box, because the section is HONESTLY taller than one screen on a
       laptop: the tallest agent panel alone runs 595-818px depending on the
       card width, and the head and foot want another 290. So this guarantees
       the section is never SHORTER than a screen, and centres it in the space
       when there is room, which is what a 2560x1400 monitor gets. Below that
       it still scrolls, which is the behaviour this section was built for on
       purpose: the picker is interactive, and trapping a reader mid-agent in a
       100vh box is worse than letting the section run long.

       The padding came down from 9vh to 6vh in the same pass, which is 71px
       back at 1180 and 78px at 880. */
    .s3-team {
      position: relative;
      isolation: isolate;
      display: grid;
      align-content: center;
      min-height: 100svh;
      padding-block: clamp(3rem, 6vh, 5.5rem);
      /* Paper under a bone card, mirroring the lab. The other way round looks
         flat in dark, where --paper is DARKER than --bone and the card sinks
         into the section instead of lifting off it. It also breaks the bone
         that section 2 ends on. */
      background: var(--paper);
    }

    /* Inset to inset, no cap. Founder call 2026-08-20: "I would like to have
       full width for the third section, definitely", the way section 2 already
       runs.

       It was on --shell, which caps at 1560px. Below about 1944px wide the cap
       never bites and the card already sat at the 10% frame; above it the card
       stopped growing while the screen kept going, so on a 2560 screen the
       picker sat 19.5% in on each side against section 2's 10%. Measured, not
       guessed: qa/inset-measure.mjs.

       A cap is a measure, and a measure is for prose. This is a rail and a
       panel read across, so the cap was only margin. The extra width also pays
       for the type scale: everything inside the card just went up a step. */
    .s3-inner {
      width: min(var(--shell-full), var(--s3-max));
      /* Load bearing, and the reason is not obvious. .s3-team is a grid, and a
         grid item's automatic minimum size is min-width:auto, which resolves to
         the item's min-content and can OVERRIDE a specified width. The marquee
         inside this block sets width:max-content, which makes its min-content
         contribution its full ~4700px, so the grid column blew out to 4700 and
         this 1080px block was centred inside it: the whole roster card sat at
         x1856 in a 1440px viewport, entirely off screen to the right. Caught by
         qa/site-section3-capture.mjs, which could no longer click the rail.

         min-width:0 opts the item out of that automatic minimum, so the
         specified width is what it gets and the marquee stays clipped inside
         its own overflow:hidden window where it belongs. */
      min-width: 0;
      margin-inline: auto;
    }

    /* Centred since 2026-08-20. The picker below keeps its own left alignment,
       because a rail and a panel are read across, not down the middle. */
    /* Founder call 2026-08-20: the head sat too close to the card. Nearly
       doubled, and paid for by the eyebrow coming out and the lede dropping
       from three sentences to one, so the section is shorter overall. */
    .s3-head {
      margin-bottom: clamp(3.5rem, 7vh, 6rem);
      text-align: center;
    }
    .s3-headline,
    .s3-lede {
      margin-inline: auto;
      text-wrap: balance;
    }
    .s3-eyebrow {
      display: block;
      margin-bottom: 1rem;
      color: var(--brass);
      font-family: var(--mono);
      font-size: var(--t-small);
      letter-spacing: 0.16em;
      text-transform: uppercase;
    }
    /* --display is Satoshi and --display-weight is 300: the type decision in
       tokens.css, not a local choice. */
    /* The head sets to the card's own width, not to a ch measure. Founder
       call 2026-08-20: the card was wider than the copy above it, and the two
       blocks reading at different widths made the head look adrift. The
       headline carries an authored <br> so it holds two lines either way; the
       lede simply wraps to the same edges the card has. */
    .s3-headline {
      max-width: 100%;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-h2);
      font-weight: var(--display-weight);
      letter-spacing: -0.018em;
      line-height: 1.08;
    }
    /* Colour, not italic. The type decision in tokens.css reserves italic for
       a whole line spoken in a different voice; a single marked word is
       coloured and stays upright, which is what section 4 already does. */
    .s3-headline em { color: var(--brass); font-style: normal; }
    .s3-lede {
      max-width: 100%;
      margin-top: 1rem;
      color: var(--ink-soft);
      font-size: var(--t-lead);
      font-weight: 300;
      line-height: 1.55;
    }

    /* == section 3, the map view ========================================
       A second reading of the same seven hires, toggled against the detail
       view. Everything here rides the same tokens as the rest of the page,
       so it inherits the dark palette without a single hard-coded colour. */
    /* ── the segmented pill ─────────────────────────────────────────────
       One pill, two or more options, and a thumb that slides to the pressed
       one. Can asked for a toggle switch rather than two separate pills,
       2026-08-21, and wants it again elsewhere, so the styling lives on
       generic classes: .seg on the group, .seg__opt on each button, with
       aria-pressed="true" marking the live one. The thumb is the group's
       ::before, sized to one cell of an equal-column grid and moved by
       translateX in whole widths, so it lands under any option without
       measuring anything. Two options by default; set --seg-n on the group
       for three or four. Section 3 keeps its .s3-views / .s3-view hooks for
       the script and the gates, and they carry no styling now. */
    .seg {
      position: relative;
      display: inline-grid;
      grid-auto-flow: column;
      grid-auto-columns: 1fr;
      padding: 3px;
      border: 1px solid var(--hairline-paper);
      border-radius: 999px;
      background: var(--seg-track);
      isolation: isolate;
    }
    .seg::before {
      content: '';
      position: absolute;
      top: 3px;
      left: 3px;
      z-index: 0;
      box-sizing: border-box;
      width: calc((100% - 6px) / var(--seg-n, 2));
      height: calc(100% - 6px);
      border: 1px solid var(--brass-hair);
      border-radius: 999px;
      background: var(--brass-wash);
      transition: transform 0.35s var(--ease);
    }
    .seg:has(.seg__opt:nth-child(2)[aria-pressed="true"])::before { transform: translateX(100%); }
    .seg:has(.seg__opt:nth-child(3)[aria-pressed="true"])::before { transform: translateX(200%); }
    .seg:has(.seg__opt:nth-child(4)[aria-pressed="true"])::before { transform: translateX(300%); }
    .seg__opt {
      position: relative;
      z-index: 1;
      padding: 0.5rem 1.2rem;
      border: 0;
      border-radius: 999px;
      background: transparent;
      color: var(--ink-soft);
      font-family: inherit;
      font-size: var(--t-small);
      text-align: center;
      cursor: pointer;
      transition: color 0.25s var(--ease);
    }
    .seg__opt:hover { color: var(--ink); }
    .seg__opt[aria-pressed="true"] { color: var(--brass); cursor: default; }
    @media (prefers-reduced-motion: reduce) { .seg::before { transition: none; } }

    .s3-views {
      display: grid;
      width: max-content;
      margin-inline: auto;
      margin-bottom: clamp(1.2rem, 2.5vh, 2rem);
    }

    /* ── the flip ──────────────────────────────────────────────────────
       Detail on the front, Map on the back, and the Detail / Map toggle turns
       the card over. Can's call 2026-08-21: flip the detail card so the map
       comes up as if it had been behind it.

       Nothing here engages until section3-map.js adds is-ready. Without it
       the picker flows in the page as it always did and the map keeps its
       hidden attribute, so the no-JS reading of the section is unchanged.
       With it, both faces are taken out of flow and the inner is given the
       active face's measured height, which tweens along with the turn so
       the card changes size as it changes side. The inner holds the 3D
       context and must stay free of overflow, opacity, filter and mask,
       any of which would flatten it; those all live on the faces.

       The map's breakout, wider than the shell, was a negative margin and
       becomes a left offset here, because a box taken out of flow cannot
       centre itself with margins. */
    .s3-flip { position: relative; perspective: 2600px; }
    .s3-flip__inner { position: relative; transform-style: preserve-3d; }
    .s3-flip.is-ready .s3-flip__inner { height: var(--s3-face-h, auto); }
    .s3-flip.is-ready .s3-picker,
    .s3-flip.is-ready .s3-map {
      position: absolute;
      top: 0;
      -webkit-backface-visibility: hidden;
      backface-visibility: hidden;
    }
    .s3-flip.is-ready .s3-picker { left: 0; width: 100%; }
    .s3-flip.is-ready .s3-map {
      left: calc((100% - var(--s3-map-w)) / 2);
      margin-inline: 0;
      transform: rotateY(180deg);
      /* No background of its own once the flip owns it. The map is taller
         than the picker, and while the inner tweens between the two heights
         the map's bottom overhangs the box and whatever has moved up under
         it. Its paper was the page's own colour, so the overhang was
         invisible until it crossed the powered by line and took a bite out
         of the words. The drawing itself ends well above the bottom of the
         face, so with nothing painted there the overhang is air. */
      background: transparent;
    }
    .s3-flip.is-map .s3-flip__inner { transform: rotateY(180deg); }
    .s3-flip.is-map .nudge--rail { opacity: 0; }

    /* The turn recedes as it goes. Under perspective the half of the card
       coming towards the viewer projects larger than its box, and on the way
       round it passed in front of the powered by line below and the toggle
       above, which Can caught on a screenshot. Pulling the card back 900px
       at the edge-on point keeps the projection inside its own lane for the
       whole turn, and reads as a card turned in the hand rather than swung
       at the reader.

       Keyframes rather than a transition, because a transition has no
       midpoint. Every keyframe lists the same two functions in the same
       order so the engine interpolates them one by one instead of falling
       back to matrix interpolation. Both ends match the resting transforms,
       so dropping the animation when the turn settles changes nothing on
       screen. Height still tweens as a transition alongside. */
    .s3-flip.is-flipping .s3-flip__inner { transition: height 0.9s var(--ease); }
    .s3-flip.is-flipping.is-map .s3-flip__inner { animation: s3-turn-over 0.9s linear both; }
    .s3-flip.is-flipping:not(.is-map) .s3-flip__inner { animation: s3-turn-back 0.9s linear both; }
    @keyframes s3-turn-over {
      from { transform: translateZ(0) rotateY(0deg); animation-timing-function: ease-in; }
      50%  { transform: translateZ(-900px) rotateY(90deg); animation-timing-function: ease-out; }
      to   { transform: translateZ(0) rotateY(180deg); }
    }
    @keyframes s3-turn-back {
      from { transform: translateZ(0) rotateY(180deg); animation-timing-function: ease-in; }
      50%  { transform: translateZ(-900px) rotateY(90deg); animation-timing-function: ease-out; }
      to   { transform: translateZ(0) rotateY(0deg); }
    }
    @media (prefers-reduced-motion: reduce) {
      .s3-flip.is-flipping .s3-flip__inner { transition: none; animation: none; }
    }

    /* No frame since 2026-08-21, on Can's call: the map sits on the section's
       own ground rather than in a bone box, so nothing reads as a container.
       It is also wider than .s3-inner on purpose, 30% over the 1020 it shipped
       at, and breaks out with a negative margin because the shell it lives in
       caps lower than that on most screens. The width is worked out from the
       viewport rather than the parent so the margin needs no self-reference. */
    .s3-map {
      --s3-map-w: min(1326px, 100vw - 2 * var(--page-inset));
      position: relative;
      width: var(--s3-map-w);
      margin-inline: calc((100% - var(--s3-map-w)) / 2);
      /* Grows sideways, not down: at 1326 wide the old 1 / 0.94 would stand
         1246px tall and run off the bottom of a 1080 screen. */
      aspect-ratio: 1 / 0.78;
      background: var(--paper);
      overflow: hidden;
    }
    @media (max-width: 860px) { .s3-map { aspect-ratio: 1 / 1.04; } }

    /* Flown in, the fan's lines run off the edge of the frame. With no border
       to stop them they ended in a hard cut, so the edges fade into the ground
       while a hire is in focus, and only then: in the overview the outer hubs
       sit close enough to the sides that an always-on fade would dim them.

       A mask on the drawing, not a paper-coloured shadow painted over it. The
       shadow was tried first and left a faint rectangle: a semi-transparent
       paper layer composited over paper rounds to 19,16,8 against 20,17,9,
       one level out, and on a ground this dark one level reads. Measured with
       a pixel probe, not guessed. The mask fades the lines themselves and
       paints nothing, so there is nothing to round. The fade width is a
       registered property so it can tween; where @property is unsupported
       the mask still applies, it just snaps. */
    @property --s3-fade { syntax: '<length>'; inherits: false; initial-value: 0px; }
    /* The field's labels follow the type ladder too. constellation.js holds
       the apparent label size still through the zoom, and reads the size it
       should hold from this property: registered as a length so the
       computed value arrives in resolved pixels rather than as the calc()
       text an unregistered custom property would hand back. Without
       @property the script falls back to its own fixed size. */
    @property --label-px { syntax: '<length>'; inherits: true; initial-value: 0px; }
    #s3-sky {
      --label-px: var(--t-small);
      display: block;
      width: 100%;
      height: 100%;
      touch-action: manipulation;
      --s3-fade: 0px;
      -webkit-mask-image:
        linear-gradient(to right, transparent, #000 var(--s3-fade), #000 calc(100% - var(--s3-fade)), transparent),
        linear-gradient(to bottom, transparent, #000 var(--s3-fade), #000 calc(100% - var(--s3-fade)), transparent);
      mask-image:
        linear-gradient(to right, transparent, #000 var(--s3-fade), #000 calc(100% - var(--s3-fade)), transparent),
        linear-gradient(to bottom, transparent, #000 var(--s3-fade), #000 calc(100% - var(--s3-fade)), transparent);
      -webkit-mask-composite: source-in;
      mask-composite: intersect;
      transition: --s3-fade 0.6s var(--ease);
    }
    #s3-sky.is-focused { --s3-fade: clamp(48px, 12vw, 160px); }

    .s3-back {
      position: absolute;
      left: 50%;
      bottom: 0.75rem;
      z-index: 2;
      transform: translateX(-50%);
      padding: 0.5rem 1.1rem;
      border: 1px solid var(--hairline-paper);
      border-radius: 999px;
      background: var(--paper);
      color: var(--ink);
      font-family: inherit;
      font-size: var(--t-small);
      cursor: pointer;
    }
    .s3-back:hover { border-color: var(--brass-hair); }

    /* The card that carries a hire's copy into the frame when you fly in. */
    .s3-card {
      position: absolute;
      left: 0;
      top: 0;
      z-index: 3;
      width: min(380px, 38%);
      padding: 20px 22px 22px;
      border: 1px solid var(--hairline-paper);
      border-radius: 16px;
      background: var(--paper);
      opacity: 0;
      visibility: hidden;
      pointer-events: none;
      transition: opacity 0.5s var(--ease), visibility 0s 0.5s;
    }
    .s3-card.is-on { opacity: 1; visibility: visible; transition: opacity 0.55s 0.45s var(--ease), visibility 0s; }
    /* On the page's type ladder, step for step with the detail view's side
       column: eyebrow at --t-micro like .s3-lbl, name at --t-h4 like the
       who-this-is block, relief at --t-small like .s3-val, chips like
       .s3-touch. It shipped on fixed pixels carried over from the lab, 9.5
       to 15, and next to the detail view it read as a different, smaller
       product. Can's call 2026-08-21. */
    .s3-card .card-role {
      display: block;
      margin-bottom: 8px;
      color: var(--brass);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.13em;
      text-transform: uppercase;
    }
    .s3-card .card-name {
      margin: 0;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-h4);
      font-weight: var(--display-weight);
      letter-spacing: -0.012em;
      line-height: 1.05;
    }
    .s3-card .card-pain { margin: 14px 0 0; max-width: 26ch; font-size: var(--t-body); font-weight: 500; line-height: 1.35; }
    .s3-card .card-relief { margin: 8px 0 0; max-width: 34ch; color: var(--ink-soft); font-size: var(--t-small); font-weight: 300; line-height: 1.45; }
    .s3-card .card-touch { display: flex; flex-wrap: wrap; gap: 5px; margin: 16px 0 0; }
    .s3-card .card-touch span {
      padding: 3px 8px;
      border: 1px solid var(--hairline-paper);
      border-radius: 7px;
      color: var(--ink-soft);
      font-size: var(--t-micro);
    }
    @media (max-width: 700px) { .s3-card { display: none; } }

    /* Field furniture. The same rules as lab/section3-constellation, in the
       same order, so the two can be diffed. */
    #s3-sky .link { fill: none; stroke: var(--clay); stroke-width: 1; opacity: 0.4; transition: opacity 0.55s var(--ease), stroke 0.55s var(--ease); }
    #s3-sky .link--trunk { opacity: 0.52; stroke-width: 1.15; }
    #s3-sky .tie { fill: none; stroke: var(--clay); stroke-width: 0.9; transition: opacity 0.55s var(--ease), stroke 0.55s var(--ease); }
    #s3-sky .tie--ring { opacity: 0.34; }
    #s3-sky .tie--mesh { opacity: 0.14; stroke-width: 0.75; }
    #s3-sky .tie--star2, #s3-sky .tie--star3 { opacity: 0.26; }
    #s3-sky .leader { stroke: var(--clay); stroke-width: 0.7; opacity: 0; transition: opacity 0.45s var(--ease); }
    #s3-sky .flow--out { fill: var(--brass); }
    #s3-sky .flow--in { fill: none; stroke: var(--brass); stroke-width: 1.3; }
    /* The hub discs are the ground's own colour so they punch a hole in the
       lines rather than sit on them as lighter coins. Paper, because the map
       runs frameless on the section's paper since 2026-08-21. The centre has
       no disc since 2026-09-12: the mark sits on the field inside a ring of
       brass seeds, see the core in constellation.js. */
    #s3-sky .seed { fill: var(--brass); }
    #s3-sky .core-mark path { fill: var(--brass); }
    /* The overview is one big button in seven parts, see wireSectors in
       constellation.js, so the whole field offers a hand. The lockup at the
       centre is the one spot that does nothing, and says so. */
    #s3-sky:not(.is-focused) { cursor: pointer; }
    #s3-sky .core { cursor: default; }
    #s3-sky .hub { cursor: pointer; }
    #s3-sky .hub:focus { outline: none; }
    #s3-sky .hub:focus-visible .hub-ring { stroke: var(--brass); stroke-width: 2.4; }
    #s3-sky .hub-hit { fill: transparent; }
    #s3-sky .hub-ring { fill: none; stroke: var(--brass-hair); stroke-width: 1.2; transition: stroke 0.4s var(--ease), stroke-width 0.4s var(--ease); }
    #s3-sky .hub-disc { fill: var(--paper); }
    #s3-sky .hub-face { opacity: 1; }
    #s3-sky .job { transition: opacity 0.55s var(--ease); }
    #s3-sky .job-dot { fill: var(--paper); stroke: var(--clay); stroke-width: 1.1; transition: fill 0.4s var(--ease), stroke 0.4s var(--ease); }
    #s3-sky .step { fill: var(--paper); stroke: var(--clay); stroke-width: 1; opacity: 0.8; transition: opacity 0.55s var(--ease), stroke 0.4s var(--ease); }
    #s3-sky .tool { opacity: 0; }
    #s3-sky .tool-brand path { fill: var(--clay); transition: fill 0.4s var(--ease); }
    #s3-sky .tool-glyph path { fill: none; stroke: var(--clay); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; transition: stroke 0.4s var(--ease); }
    #g-label { font-family: var(--sans); }
    #s3-sky .lbl { fill: var(--ink); pointer-events: none; transition: opacity 0.45s var(--ease); }
    #s3-sky .lbl--hub { font-weight: 500; }
    #s3-sky .lbl--role { fill: var(--clay); font-size: 0.74em; opacity: 0.82; }
    #s3-sky .lbl--job { fill: var(--ink); font-size: 1em; opacity: 0; }
    #s3-sky .lbl--tool { fill: var(--ink-soft); font-size: 0.95em; opacity: 0; }
    @media (max-width: 700px) { #s3-sky .lbl--role { display: none; } }

    #s3-sky.is-hovered:not(.is-focused) .link:not(.is-warm) { opacity: 0.11; }
    #s3-sky.is-hovered:not(.is-focused) .tie:not(.is-warm) { opacity: 0.07; }
    #s3-sky.is-hovered:not(.is-focused) .job:not(.is-warm) { opacity: 0.3; }
    #s3-sky.is-hovered:not(.is-focused) .step:not(.is-warm) { opacity: 0.22; }
    #s3-sky.is-hovered:not(.is-focused) .flow:not(.is-warm) { opacity: 0.12; }
    #s3-sky.is-hovered:not(.is-focused) .lbl--hub:not(.is-warm) { opacity: 0.34; }
    #s3-sky.is-hovered:not(.is-focused) .lbl--role:not(.is-warm) { opacity: 0.16; }
    #s3-sky.is-hovered .hub.is-warm .hub-ring { stroke: var(--brass); stroke-width: 1.6; }
    #s3-sky.is-hovered .link.is-warm { stroke: var(--brass); opacity: 0.62; }

    #s3-sky.is-focused .link.is-ghost { opacity: 0.07; }
    #s3-sky.is-focused .tie.is-ghost { opacity: 0.03; }
    #s3-sky.is-focused .job.is-ghost { opacity: 0.12; }
    #s3-sky.is-focused .step.is-ghost { opacity: 0.1; }
    #s3-sky.is-focused .hub.is-ghost { opacity: 0.16; }
    #s3-sky.is-focused .flow.is-ghost { opacity: 0.08; }
    #s3-sky.is-focused .lbl.is-ghost { opacity: 0; }
    #s3-sky.is-focused .link.is-live { stroke: var(--brass); opacity: 0.72; }
    #s3-sky.is-focused .link--trunk.is-live { stroke: var(--brass); opacity: 0.2; }
    #s3-sky.is-focused .tie.is-live { opacity: 0.14; stroke: var(--brass); stroke-width: 1; }
    #s3-sky.is-focused .job-dot { fill: var(--paper); }
    #s3-sky.is-focused .job.is-live .job-dot { stroke: var(--brass); stroke-width: 1.4; }
    #s3-sky.is-focused .step.is-live { stroke: var(--brass); opacity: 1; }
    #s3-sky.is-focused .tool.is-live { opacity: 1; }
    #s3-sky.is-focused .tool.is-live .tool-brand path { fill: var(--brass); }
    #s3-sky.is-focused .tool.is-live .tool-glyph path { stroke: var(--brass); }
    #s3-sky.is-focused .hub.is-live .hub-ring { stroke: var(--brass); stroke-width: 2.2; }
    #s3-sky.is-focused .lbl--job.is-live,
    #s3-sky.is-focused .lbl--tool.is-live { opacity: 1; }
    #s3-sky.is-focused .leader.is-live { opacity: 0.5; }
    #s3-sky.is-focused .lbl--hub.is-live,
    #s3-sky.is-focused .lbl--role.is-live { opacity: 0; }

    .s3-picker[hidden] { display: none; }
    .s3-picker {
      position: relative;
      display: grid;
      grid-template-columns: 268px minmax(0, 1fr);
      border: 1px solid var(--hairline-paper);
      border-radius: 22px;
      background: var(--bone);
      overflow: hidden;
    }

    /* ── the field in the card ────────────────────────────────────────────
       The app's gradient band, Can's ask of 2026-09-06, mounted by
       section3-field.js behind the rail and the panel and masked to the top
       of the card with the app's own numbers: solid to 110px, gone by 230px.
       The rail and the panels are positioned so they paint over it; the
       chosen rail row opens to 71%, the app's panel openness, so the band
       shows through it rather than stopping at a hole. */
    .s3-field {
      position: absolute;
      inset: 0;
      z-index: 0;
      pointer-events: none;
      /* THE FADE. The app's band is a header strip, solid to 110px and gone
         by 230px on a straight ramp, and Can saw the end of that ramp as a
         line across the card on 2026-09-06. A first cut let the fade run to
         520px and he read it as the whole card coloured in. So: the app's
         strip, solid to 96px, then an eased tail that is mostly gone by
         280px and finished by 400px, with --s3-band-floor left under the
         REST of the card, a whisper rather than nothing, so the strip does
         not end on colour against no colour. 0.04 ships. ?band=floor doubles
         it and ?band=sharp is the app's own ramp, for the side by side;
         section3-field.js stamps them on the root. */
      --s3-band-floor: 0.04;
      -webkit-mask-image: linear-gradient(to bottom,
        #000 0 96px,
        rgba(0, 0, 0, 0.6) 160px,
        rgba(0, 0, 0, 0.3) 220px,
        rgba(0, 0, 0, 0.12) 280px,
        rgba(0, 0, 0, 0.06) 340px,
        rgba(0, 0, 0, var(--s3-band-floor)) 400px,
        rgba(0, 0, 0, var(--s3-band-floor)) 100%);
      mask-image: linear-gradient(to bottom,
        #000 0 96px,
        rgba(0, 0, 0, 0.6) 160px,
        rgba(0, 0, 0, 0.3) 220px,
        rgba(0, 0, 0, 0.12) 280px,
        rgba(0, 0, 0, 0.06) 340px,
        rgba(0, 0, 0, var(--s3-band-floor)) 400px,
        rgba(0, 0, 0, var(--s3-band-floor)) 100%);
    }
    :root[data-s3-band="floor"] .s3-field { --s3-band-floor: 0.08; }
    :root[data-s3-band="sharp"] .s3-field {
      -webkit-mask-image: linear-gradient(to bottom, #000 0 110px, transparent 230px);
      mask-image: linear-gradient(to bottom, #000 0 110px, transparent 230px);
    }
    .s3-field canvas { display: block; width: 100%; height: 100%; }
    .s3-picker > .s3-rail,
    .s3-picker > .s3-agents { position: relative; }

    /* ── the disc under the map ──────────────────────────────────────────
       The same field on the back of the flip, Can's ask of 2026-09-06 with
       two circles drawn on the map: full inside the ring of hires, gone
       before the outer hubs. The drawing is a 1000 unit square fitted to
       the shorter side of the box, so closest-side percentages ARE viewBox
       units, two to one: the ring sits at about 175, the fan's outer hubs
       past 400. Can's third look, 2026-09-06: the colour belongs to the
       INNER circle, the ring itself. Full to 22% (110 units), through the
       hires at 35%, gone by 54% (270), an eased ramp. Short on purpose: a
       ramp is where a mask can band, and the field's own dark stops are set
       to the ground behind the map for the same reason (section3-field.js,
       DISC.ground). section3-field.js mounts it, and only once the map has
       been turned to. */
    .s3-map-field {
      position: absolute;
      inset: 0;
      z-index: 0;
      pointer-events: none;
      -webkit-mask-image: radial-gradient(circle closest-side at 50% 50%,
        #000 0 22%,
        rgba(0, 0, 0, 0.78) 31%,
        rgba(0, 0, 0, 0.42) 40%,
        rgba(0, 0, 0, 0.14) 48%,
        transparent 54%);
      mask-image: radial-gradient(circle closest-side at 50% 50%,
        #000 0 22%,
        rgba(0, 0, 0, 0.78) 31%,
        rgba(0, 0, 0, 0.42) 40%,
        rgba(0, 0, 0, 0.14) 48%,
        transparent 54%);
    }
    .s3-map-field canvas { display: block; width: 100%; height: 100%; }
    .s3-map > #s3-sky { position: relative; }
    .s3-picker.has-field .s3-rail button[aria-pressed="true"] {
      background: color-mix(in srgb, var(--paper) 71%, transparent);
    }

    /* ── the lift ─────────────────────────────────────────────────────────
       Can's second ask of 2026-09-06: the card sits ON the ground rather
       than in it. The tone does most of it on dark (the box under the card
       wears paper, see .ml-wrap). What is left here is a contact hair at
       the foot, one short plain drop, and a hair of light along the top
       edge on its own layer, because an inset shadow would sit under the
       canvas and never be seen.

       Short and uncoloured on purpose. A first cut reached 120px out in
       black, and on a ground this dark a blur that long has only a dozen
       shades to cross, so it crossed them in rings Can could count; a
       second cut tinted it copper and read as a glow. Within 22px the steps
       sit closer than the eye splits them. */
    .s3-picker {
      --s3-rim: rgba(255, 255, 255, 0.6);
      --s3-contact: rgba(22, 22, 22, 0.08);
      --s3-drop: rgba(22, 22, 22, 0.14);
      box-shadow:
        0 1px 2px var(--s3-contact),
        0 10px 22px -12px var(--s3-drop);
    }
    /* Dark carries a third layer and goes further into black: on paper the
       first plain cut read as no shadow at all at the edges (Can, the same
       evening). Still within 44px, so about three pixels a shade. */
    :root[data-theme="dark"] .s3-picker {
      --s3-rim: rgba(255, 255, 255, 0.07);
      --s3-contact: rgba(0, 0, 0, 0.7);
      --s3-drop: rgba(0, 0, 0, 0.8);
      box-shadow:
        0 1px 2px var(--s3-contact),
        0 8px 20px -4px rgba(0, 0, 0, 0.6),
        0 18px 44px -10px var(--s3-drop);
    }
    .s3-picker::after {
      content: "";
      position: absolute;
      inset: 0;
      z-index: 2;
      border-radius: inherit;
      box-shadow: inset 0 1px 0 var(--s3-rim);
      pointer-events: none;
    }

    /* ── rail ── */
    .s3-rail {
      border-right: 1px solid var(--hairline-paper);
      padding: 10px 9px 14px;
    }
    .s3-rail button {
      display: flex;
      align-items: center;
      gap: 11px;
      width: 100%;
      margin-bottom: 2px;
      padding: 10px 11px;
      border: 1px solid transparent;
      border-radius: 9px;
      background: transparent;
      color: var(--ink-soft);
      font-family: var(--sans);
      font-size: var(--t-small);
      line-height: 1.35;
      text-align: left;
      cursor: pointer;
      transition: border-color 180ms var(--ease), background 180ms var(--ease);
    }
    .s3-rail button:hover { border-color: var(--brass-hair); }
    .s3-rail button[aria-pressed="true"] {
      border-color: var(--brass-hair);
      background: var(--paper);
    }
    .s3-rl { display: block; min-width: 0; }
    .s3-rn { display: block; color: var(--ink); font-size: var(--t-small); }
    .s3-rr { display: block; margin-top: 2px; color: var(--clay); font-size: var(--t-micro); }

    /* ── the agent portrait ───────────────────────────────────────────────
       Replaced the seeded dot marks on 2026-08-19. Those were generated from
       the hero's own golden-angle field and were always a placeholder; Can's
       role-specific portraits are the real thing, and a face does the work a
       glyph was standing in for — the rail reads as people at a glance, which
       is the argument the whole section makes.

       Cut to transparent circles by qa/circle-agent-portraits.py, so each one
       carries its own cream ground and brass ring and sits on paper or soot
       without a plate. The 128px WebP is served at every size here: 5 to 6k
       each, and 2x for the largest use on the page.

       Ari is the rabbit, Milo the bear, Fin the elephant, Lumi the cat, Scout
       the eagle, Sol the panda and Nico the fox. The files are named for the
       AGENT, not the animal, so recasting one is a re-run of the cutter rather
       than an edit here — the assignment and the reasoning behind it live in
       that script's ROSTER. */
    .s3-face {
      display: block;
      flex: 0 0 auto;
      border-radius: 50%;
    }
    .s3-rail .s3-face {
      opacity: 0.88;
      transition: opacity 180ms var(--ease);
    }
    .s3-rail button:hover .s3-face,
    .s3-rail button[aria-pressed="true"] .s3-face { opacity: 1; }

    /* ── panels ──────────────────────────────────────────────────────────
       Every agent is in the DOM. Without the module they stack and read as a
       list, which is what keeps the section whole for a reader with no
       JavaScript and for anything that indexes the page. With it, one shows.
       display:none rather than opacity, so nothing is authored copy stranded
       at zero opacity — the failure qa/site-verify.mjs actually guards. */
    .s3-agent {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 236px;
    }
    .s3-agent + .s3-agent { border-top: 1px solid var(--hairline-paper); }
    :root.s3-ready .s3-agent { display: none; }
    :root.s3-ready .s3-agent.is-active { display: grid; }
    :root.s3-ready .s3-agent + .s3-agent { border-top: 0; }

    .s3-panel {
      position: relative;
      padding: clamp(24px, 2.8vw, 40px) clamp(22px, 2.6vw, 38px);
    }
    /* 20ch until 2026-08-20, when the card went to full width. A ch measure
       does not grow with the card, so the question stayed a ~550px column
       inside an 1100px panel and left a 500px hole between it and the side
       rail. These two are display lines, not prose: they can run long. At 46
       and 40 the longest question sets on ONE line from about 1600px up and
       falls back to two on a laptop, where the column is the limit anyway.
       .s3-detail below keeps its 48ch, because that one IS prose. */
    .s3-pain {
      max-width: 46ch;
      margin: 0;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-h3);
      font-weight: var(--display-weight);
      letter-spacing: -0.015em;
      line-height: 1.12;
    }
    .s3-relief {
      max-width: 40ch;
      margin-top: 14px;
      color: var(--brass-bright);
      font-family: var(--display);
      font-size: var(--t-h4);
      font-weight: var(--display-weight);
      letter-spacing: -0.012em;
      line-height: 1.18;
    }
    .s3-detail {
      max-width: 48ch;
      margin-top: 20px;
      color: var(--ink-soft);
      font-size: var(--t-body);
      font-weight: 300;
      line-height: 1.6;
    }

    /* Can's label, replacing "What they handle". The list is a sample of what
       gets built, not a feature list — everything is custom per client — and
       this agrees with the foot line instead of contradicting it. */
    .s3-does-lbl {
      margin: 22px 0 10px;
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.13em;
      text-transform: uppercase;
    }
    /* One short line each. The font-size lives on the list, not the item, or
       `ch` resolves against the inherited 16px and the measure blows out. */
    .s3-does {
      display: grid;
      gap: 7px;
      max-width: 54ch;
      margin: 0;
      padding: 0;
      font-size: var(--t-small);
      list-style: none;
    }
    .s3-does li {
      position: relative;
      padding-left: 16px;
      color: var(--ink-soft);
      line-height: 1.55;
    }
    .s3-does li::before {
      content: "—";
      position: absolute;
      left: 0;
      color: var(--brass);
    }

    /* ── the handoff chip ── */
    .s3-tag {
      display: inline-flex;
      align-items: center;
      gap: 5px;
      margin: 0 1px;
      padding: 1px 10px 1px 3px;
      border: 1px solid var(--brass-hair);
      border-radius: 999px;
      background: var(--brass-wash);
      color: var(--brass-bright);
      font-family: inherit;
      font-size: var(--t-micro);
      line-height: 1.3;
      vertical-align: -0.22em;
      white-space: nowrap;
      cursor: pointer;
      transition: border-color 180ms var(--ease), background 180ms var(--ease);
    }
    .s3-tag:hover {
      border-color: var(--brass-bright);
      background: color-mix(in srgb, var(--brass) 16%, transparent);
    }

    /* ── what the work costs ─────────────────────────────────────────────
       Deliberately not a hero number: it is evidence sitting under the list,
       not the thing the panel is about. Arithmetic the reader can check, with
       its assumption stated, is the only kind of number this site can publish
       while it has no clients to cite. */
    .s3-worth {
      margin-top: 22px;
      padding-top: 18px;
      border-top: 1px solid var(--hairline-paper);
    }
    .s3-worth-lbl {
      margin: 0 0 13px;
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.13em;
      text-transform: uppercase;
    }
    .s3-worth-grid { display: flex; flex-wrap: wrap; gap: 16px 44px; }
    .s3-fig {
      display: block;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-h4);
      font-weight: var(--display-weight);
      letter-spacing: -0.012em;
      line-height: 1.05;
    }
    .s3-fig--own { color: var(--brass-bright); }
    .s3-cap {
      display: block;
      max-width: 30ch;
      margin-top: 6px;
      color: var(--ink-soft);
      font-size: var(--t-small);
      line-height: 1.4;
    }
    .s3-worth-note {
      max-width: 56ch;
      margin-top: 15px;
      color: var(--clay);
      font-size: var(--t-small);
      line-height: 1.5;
    }

    /* ── side column ── */
    .s3-side {
      display: grid;
      align-content: start;
      gap: 16px;
      padding: clamp(22px, 2.4vw, 32px) 20px;
      border-left: 1px solid var(--hairline-paper);
    }
    .s3-lbl {
      display: block;
      margin-bottom: 6px;
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.13em;
      text-transform: uppercase;
    }
    .s3-val { color: var(--ink); font-size: var(--t-small); line-height: 1.45; }
    .s3-touch { display: flex; flex-wrap: wrap; gap: 5px; }
    .s3-touch span {
      padding: 3px 8px;
      border: 1px solid var(--hairline-paper);
      border-radius: 7px;
      color: var(--ink-soft);
      font-size: var(--t-micro);
    }

    /* ── who this is ─────────────────────────────────────────────────────
       Top of the side column, which is the placement Can picked. Kept out of
       the panel's top-left on purpose so the panel opens on the question
       rather than on a label saying whose panel you are reading. Rendered
       into both slots so the name cannot vanish when the side column drops
       out below 1120px. */
    .s3-who { display: none; align-items: center; gap: 10px; }
    .s3-who-text { display: block; }
    .s3-who-name {
      display: block;
      color: var(--brass-bright);
      font-family: var(--display);
      font-size: var(--t-body);
      font-weight: var(--display-weight);
      letter-spacing: 0.01em;
      line-height: 1.1;
    }
    .s3-who-role {
      display: block;
      margin-top: 4px;
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.14em;
      text-transform: uppercase;
    }
    /* Stacked, not beside: a 64px portrait plus the role on one line left
       "Personal assistant" wrapping in a 236px column. Going vertical gives
       the role the full width and lets the portrait get bigger, which is the
       one place on the page where the detail in it is worth seeing. */
    .s3-who--side {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      gap: 12px;
      padding-bottom: 16px;
      border-bottom: 1px solid var(--hairline-paper);
    }
    .s3-who--side .s3-face { width: 76px; height: 76px; }
    /* There used to be a second name block, .s3-who--top, that took over the
       panel's top right corner once the side column folded at 1120. Can,
       2026-09-04, from three screens: the portrait sat on top of the
       question ("Need a project manag[Milo]?") from 1120 all the way down to
       620. The rule now: when the side column goes, the name goes with it.
       The rail already shows the selected agent lit, and the question names
       the role in its first line. Do not put the photo back in the corner. */

    /* ── the nudges ────────────────────────────────────────────────────
       Two hand-written margin notes, from Can's mark-up on 2026-08-27. This
       section holds the only two controls on the page a reader has to find
       for themselves: the Detail / Map toggle, and the rail of seven names.
       Both look like labels until you click one, and a reader who never
       clicks reads the section as a single static card about Ari.

       They are drawn as pen, not as UI. Alex Brush through
       --script, the same face that signs the testimonial, and an open V
       arrowhead rather than a filled marker, because a filled triangle
       reads as a diagram and two strokes read as a hand. Ink is --ink-soft,
       pencil weight: brass is the interactive colour on this page and a
       brass note would compete with the thing it points at.

       They are decoration and nothing more. aria-hidden, pointer-events
       none, and invisible until nudge.js draws them, which is correct: with
       the module blocked neither control does anything, so there is nothing
       to point at.

       --u is the unit the whole note is measured in, so the arrow and the
       word grow together with the type scale. Every offset below is a
       multiple of it, derived from where the path actually starts and ends
       in its own viewBox; the working is in the two rules that place the
       words. */
    .nudge {
      --u: var(--t-body);
      position: absolute;
      z-index: 3;
      color: var(--ink-soft);
      font-size: var(--u);
      pointer-events: none;
      transition: opacity 0.45s var(--ease);
    }

    /* Set the moment the reader uses the control the note points at. The
       note has done its job and a note that stays is nagging. */
    .nudge.is-done { opacity: 0; }

    .nudge__word {
      position: absolute;
      /* Full ink, the same as the signature under the testimonials. The
         word was pencil weight like its arrow until 2026-09-06, when Can
         read it as a different face from the names: same Alex Brush, but
         at 74% on the dark ground its hairlines thinned to nothing. The
         arrow keeps the pencil. */
      color: var(--ink);
      font-family: var(--script);
      font-size: calc(var(--u) * 1.75);
      font-weight: 400;
      line-height: 1;
      white-space: nowrap;
      opacity: 0;
      translate: 0 0.3em;
      transition: opacity 0.5s var(--ease), translate 0.5s var(--ease);
    }
    .nudge.is-drawn .nudge__word { opacity: 1; translate: 0 0; }

    .nudge__arrow {
      display: block;
      /* The arrowhead barbs are plotted just past the foot of the viewBox,
         where the head sits; clipping them would cut the point off. */
      overflow: visible;
      fill: none;
      stroke: currentColor;
      stroke-width: 1.7;
      stroke-linecap: round;
      stroke-linejoin: round;
    }

    /* Drawn on, not faded in: the note should look written while you watch.
       Three strokes in the order a hand makes them. The tail runs first,
       from the word down to the point. Then the two barbs, each one a
       separate path starting AT the point and running outward, a beat
       apart. They were one path through the point on 2026-08-27 and Can
       caught it: a single stroke barb to point to barb reads as a line
       swept across the tip, not as an arrowhead flicked off it.

       nudge.js measures each path and writes --len, --dur and --delay.
       The fallback length is generous on purpose: too long and the line
       still draws, too short and it would never finish. */
    .nudge__arrow path {
      stroke-dasharray: var(--len, 400);
      stroke-dashoffset: var(--len, 400);
    }
    .nudge.is-drawn .nudge__arrow path {
      stroke-dashoffset: 0;
      transition: stroke-dashoffset var(--dur, 0.8s) var(--ease) var(--delay, 0s);
    }

    @media (prefers-reduced-motion: reduce) {
      .nudge__word { translate: 0 0; transition: none; }
      .nudge.is-drawn .nudge__arrow path { transition: none; }
    }

    /* ── the toggle note ──
       Sits in the air beside the pill, which is centred and about 160px
       wide inside a 1080px block, so the room is there at every width this
       note shows at. The arrow's tail ends at viewBox 112,6 of 118x40; at
       6.2u wide that is 5.88u across and 0.315u down, and the word's own
       half height is 0.875u, hence the -0.56u. */
    .nudge--view {
      top: 50%;
      left: 100%;
      translate: 0 -50%;
      margin-left: 0.5rem;
    }
    .nudge--view .nudge__arrow {
      width: calc(var(--u) * 6.2);
      height: calc(var(--u) * 2.1);
    }
    .nudge--view .nudge__word {
      left: calc(var(--u) * 6.4);
      top: calc(var(--u) * -0.56);
    }

    /* ── the rail note ──
       Tail starts at viewBox 6,6 of 132x48; at 6.6u wide that is 0.3u in
       and 0.3u down, and the word sits a full line above it. */
    .nudge--rail .nudge__arrow {
      width: calc(var(--u) * 6.6);
      height: calc(var(--u) * 2.4);
    }
    .nudge--rail .nudge__word {
      left: calc(var(--u) * 0.2);
      top: calc(var(--u) * -1.7);
    }

    /* Where the rail note goes depends on whether the page still has a
       gutter to write in. .s3-inner is min(shell-full, --s3-max), so the
       margin beside the card is 160px at a 1400px viewport and grows from
       there; the note measures about 150px including its word. Above that
       it sits out in the margin the way Can drew it, pointing right into
       the rail. */
    @media (min-width: 1400px) {
      .nudge--rail {
        /* Aimed at the third and fourth names rather than the first. The
           first row is already lit, so an arrow into it says "this one" when
           the note is there to say "any of these". */
        top: calc(var(--u) * 7.5);
        right: 100%;
        margin-right: 0.55rem;
      }
    }

    /* Below that there is no margin left to write in. The first try put the
       note in the band above the card, which is exactly where the sticky
       header sits once the section is scrolled to: the note went under the
       bar. So it moves inside instead, into the rail column's own empty
       foot, and points back up at the names.

       The numbers are measured, not guessed. qa/section3-nudge-capture.mjs
       reports the last rail row ending 422px below the top of the card at
       every width in this range, with between 197px and 312px of empty
       column under it, and --type-base pinned at its 17px floor throughout,
       so a pixel here is a fixed pixel. The note needs about 90.

       Only the arrow turns. A tilted word reads as a mistake rather than as
       handwriting. */
    @media (max-width: 1399px) {
      .nudge--rail {
        top: 442px;
        left: 20px;
      }
      .nudge--rail .nudge__arrow { rotate: -58deg; }
      .nudge--rail .nudge__word {
        left: 0;
        top: calc(var(--u) * 3.3);
      }
    }

    /* No note under 900px. The rail turns into a full width strip above the
       panel at 780 and the head band tightens well before that, so there is
       nowhere honest to put a margin note; a tap target that size is also
       far less likely to be missed. */
    @media (max-width: 899px) {
      .nudge { display: none; }
    }

    /* Off with one attribute, for a look at the section without them. */
    :root[data-nudges="off"] .nudge { display: none; }



    @media (max-width: 1120px) {
      .s3-picker { grid-template-columns: 240px minmax(0, 1fr); }
      .s3-agent { grid-template-columns: minmax(0, 1fr); }
      .s3-side { display: none; }
    }
    /* Can, 2026-09-03: the rail had become a full width list above the
       panel here, a whole screen of names before any agent was read. Now it
       is a narrow column beside the panel, face over a small name, so the
       seven stay in reach while the panel keeps the width. Roles go; the
       panel says the role in its first line. */
    @media (max-width: 780px) {
      /* No map on a phone, Can 2026-09-03 from his screen recording: the
         constellation is tiny at this width and reads as nothing. The
         switch goes, so the card is the detail panel and only that.
         section3-map.js also drops back to Detail if the map was up when
         the window came down to this width. */
      .s3-views { display: none; }
      /* Can, 2026-09-04, from his phone: "looks too narrow". The card sat
         inside the page's 10% frame, then lost 66px to the rail and 22px of
         padding each side, which left the copy a 200px column on a 390px
         screen. Measured by qa/section3-mobile-width-capture.mjs. The card
         now runs to a 12px inset, the rail trims to 60 and the panel padding
         to 18, and the column is 270px: a third more line for the same
         words. The head above keeps the shell, so only the card widens. */
      .s3-inner { width: calc(100% - 24px); }
      .s3-picker { grid-template-columns: 60px minmax(0, 1fr); }
      .s3-panel { padding: 22px 18px 26px; }
      .s3-rail { padding: 8px 3px 10px; }
      .s3-rail button {
        flex-direction: column;
        gap: 4px;
        margin-bottom: 4px;
        padding: 7px 2px;
      }
      .s3-rail .s3-face { width: 30px; height: 30px; }
      .s3-rl { width: 100%; text-align: center; }
      .s3-rn { font-size: 10px; line-height: 1.2; }
      .s3-rr { display: none; }
    }
    /* Paper wastes nothing on a picker: print all seven. */
    @media print {
      :root.s3-ready .s3-agent { display: grid; }
      .s3-rail { display: none; }
      .s3-picker { grid-template-columns: 1fr; }
    }

    /* ══ SECTION 4 · POWERED BY ═══════════════════════════════════════════
       Written 2026-08-20 on Can's brief: show what the employees actually run
       on, and carry the bring-your-own-key line the Cabinet site made, but in
       the agency's own terms, where the key belongs to the client and we are
       the ones holding the build.

       A band, not a screen. No 100vh, no scroll-snap: it sits between the
       roster and the turn as a fact, and trapping a reader in a logo row for a
       full viewport would be a lie about how much there is to read here.

       Bone between two papers, so it separates without adding a fifth colour.

       HONESTY, re-verified 2026-08-20 against src/lib/agents/adapters/registry.ts.
       Claude Code, Codex, Gemini, Cursor, opencode, Pi and Grok all ship as
       adapters, so all seven can be named flatly. Copilot ships too and is
       left off on the founder's call.

       Kimi is the one whose argument has to be written down, because it is not
       an adapter. OpenCode discovers its model list at runtime from whatever
       the user has authed, so Kimi is reached through that routing rather than
       through anything Aita implements. The same argument, and only that
       argument, covers any open weights model added to this row later, Qwen
       and GLM and DeepSeek included. They may sit under "also available on".
       None of them may ever be described as something Aita drives natively.
       That word belongs to Claude Code alone. Nothing here
       may name something the system cannot actually run. The claims about
       accounts, keys and markup are business terms, not features: if the
       commercial model changes, this copy changes with it. */
    .s4-power {
      position: relative;
      isolation: isolate;
      padding-block: clamp(3.5rem, 7vh, 5.5rem);
      background: var(--bone);
    }

    .s4-inner {
      width: var(--shell);
      margin-inline: auto;
    }

    .s4-head {
      margin-bottom: clamp(2.2rem, 4.5vh, 3.4rem);
      text-align: center;
    }
    .s4-headline,
    .s4-lede {
      margin-inline: auto;
      text-wrap: balance;
    }

    /* A step under the roster's H2, deliberately. This is the footnote to the
       team above it, not a third headline competing with it. */
    .s4-headline {
      max-width: 24ch;
      color: var(--ink);
      font-family: var(--display);
      font-size: var(--t-h3);
      font-weight: var(--display-weight);
      letter-spacing: -0.016em;
      line-height: 1.12;
    }
    .s4-headline em { color: var(--brass); font-style: normal; }

    .s4-lede {
      max-width: 64ch;
      margin-top: 1.1rem;
      color: var(--ink-soft);
      font-size: var(--t-lead);
      font-weight: 300;
      line-height: 1.6;
    }

    /* ── Section 3B, the deck ─────────────────────────────────────────────
       Can's pick of 2026-09-06 out of lab/section-panels: A3, "More alone",
       no clock, the staircase, large, the marks on. Six drawn screens of
       the app follow one job through a Tuesday and land one on the last as
       the reader scrolls; the earlier ones step up and to the left with
       their header bars kept. assets/section3-deck.js stacks and plays
       them; every word is authored in the markup below.

       Between the roster card and the powered-by block, both of which stay
       exactly where they were: .s3-inner is closed before it and reopened
       after it, so .s3-inner > .s4-inner still holds.

       THE WINDOW is one object: the roster card's 22px radius, glass off
       the ground tokens, and the app's own Verdigris band across the top,
       the same four stops motion.js and section3-field.js carry. Inside a
       window everything is 12px; a chip is a pill. Nothing else is rounded.

       TWO LITERALS, named: the band's bar reads in bone because the band is
       dark on both themes, and a copper chip's text is the app's own
       primary-foreground, ink on copper in both themes (docs/research/film/
       2026-09-04-aita-app-survey.md, 3a). Neither has a token because
       neither follows the theme. */
    .s3d {
      --r-win: 22px;
      --r-in: 12px;
      --pn-surface: color-mix(in srgb, var(--paper) 80%, transparent);
      --pn-inner: color-mix(in srgb, var(--bone) 74%, transparent);
      --pn-shadow: 0 60px 100px -50px rgba(0, 0, 0, 0.65), 0 1px 0 0 rgba(255, 255, 255, 0.05) inset;
      --pn-bone: rgba(242, 239, 233, 0.72);
      --pn-on-brass: #161616;
      position: relative;
      /* Large, Can's pick: the section runs to the page inset like section
         2 rather than sitting inside the roster's cap. Little under it: the
         powered-by block brings its own margin, and Can's note of
         2026-09-06 was the gap. */
      /* Locked, Can's ruling of 2026-09-06 on an ultra wide screen: the
         copy and the panel were drifting to opposite edges of the room.
         --shell is the page's own cap, the 10% frame up to 1560px and
         centred past it, the same shell the powered-by block under this
         one sits in; on a laptop nothing changes. */
      width: var(--shell);
      min-width: 0;
      margin-inline: auto;
      /* Room above, Can's note of 2026-09-06: the deck starts once the
         roster card is finished, not on its heels. The stage only sticks
         once it reaches its centred spot, so the first panel comes up
         through this room like any block. A little more under it too. */
      padding-block: clamp(7rem, 18vh, 12rem) clamp(2rem, 4vh, 3rem);
    }
    :root[data-theme="dark"] .s3d {
      --pn-shadow: 0 60px 100px -50px rgba(0, 0, 0, 0.85), 0 1px 0 0 rgba(255, 255, 255, 0.06) inset;
    }

    .s3d-grid {
      display: grid;
      grid-template-columns: minmax(280px, 0.55fr) minmax(0, 1.45fr);
      gap: clamp(2rem, 4vw, 4rem);
      align-items: start;
    }

    /* The copy holds while the panels stack. It sticks at an offset the
       module measures, the one that centres it in the viewport, and is
       only as tall as itself: a column that held a whole viewport was what
       left the hole under the deck. */
    .s3d-copy {
      position: sticky;
      top: var(--s3d-copy-top, 0px);
      align-self: start;
    }

    .s3d-h {
      margin: 0;
      color: var(--ink);
      font-family: var(--display);
      font-weight: var(--display-weight);
      font-size: var(--t-h2);
      letter-spacing: -0.02em;
      line-height: 1.08;
      text-wrap: balance;
    }
    .s3d-h em { color: var(--brass); font-style: normal; }

    /* The six lines, each a door to its panel. A day is a sequence, so the
       list is ordered by the clock, in twelve hours with a small meridiem. */
    .s3d-beats {
      margin: clamp(1.4rem, 3vh, 2.2rem) 0 0;
      padding: 0;
      list-style: none;
      border-top: 1px solid var(--hairline-paper);
    }
    .s3d-beats li { border-bottom: 1px solid var(--hairline-paper); }
    .s3d-beats button {
      display: grid;
      grid-template-columns: 8.2ch minmax(0, 1fr);
      gap: 14px;
      align-items: baseline;
      width: 100%;
      padding: 10px 4px;
      background: none;
      border: 0;
      text-align: left;
      cursor: pointer;
      color: var(--clay);
      font: inherit;
      transition: color .3s var(--ease);
    }
    .s3d-beats time {
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.1em;
      font-variant-numeric: tabular-nums;
      white-space: nowrap;
    }
    .s3d-beats time small { font-size: 0.82em; margin-left: 0.35em; letter-spacing: 0.08em; opacity: 0.8; }
    .s3d-beats span { font-size: var(--t-body); font-weight: 300; line-height: 1.35; }
    .s3d-beats button:hover { color: var(--ink-soft); }
    .s3d-beats button[aria-current="true"] { color: var(--ink); }
    .s3d-beats button[aria-current="true"] time { color: var(--brass-bright); }

    .s3d-foot {
      margin: clamp(1.6rem, 3vh, 2.4rem) 0 0;
      color: var(--clay);
      font-size: var(--t-small);
      line-height: 1.5;
    }
    .s3d-foot em { color: var(--ink-soft); font-style: normal; }
    /* The count in the foot: six on the staircase; on a phone dealt from
       its own cards, a few (the hand cards' block below flips it). */
    .s3d-foot__few { display: none; }

    /* The track is the scroll length, 60vh a beat plus the stage, written
       by the module in pixels once the stage is measured; the CSS value is
       the first frame's. Its only content is the sticky stage, which is as
       tall as the panels and sticks at the offset that centres it, so the
       pin ends where the sixth panel does and nothing is held under it.
       Nothing clips: the flight of headers rises above the stage's box and
       an arriving panel comes up from under it, faint until it is nearly
       in. */
    .s3d-track { --n: 6; height: calc((var(--n) - 1) * 60vh + 40svh); }
    .s3d-stage {
      position: sticky;
      top: var(--s3d-top, 0px);
      display: grid;
      place-items: center;
      perspective: 1500px;
      /* The stack moves down by up to two steps as the flight above it
         grows, so the flight and the panel in hand stay centred together;
         the box keeps that much under it so the last panel never lands on
         the block below. */
      padding-bottom: 92px;
    }
    /* The stack leaves the room for four steps on its left rather than
       running off the stage, and the ladder inside it is one step up,
       restated because the root's steps are computed there. */
    .s3d-stack {
      position: relative;
      justify-self: end;
      width: min(1000px, calc(100% - 120px));
      display: grid;
      transform-style: preserve-3d;
      --t-body: calc(var(--type-base) * 1.1);
      --t-micro: calc(var(--t-body) * 0.64);
      --t-small: calc(var(--t-body) * 0.80);
      --t-lead: calc(var(--t-body) * 1.25);
      --t-h4: calc(var(--t-body) * 1.5625);
      --t-h3: calc(var(--t-body) * 1.953);
    }
    /* Every panel in the same cell, sharing one top edge, which is the
       header each keeps in view, and pivoting from it. --cut is written
       per frame by the module: 2000px for the panel in hand, 52px once it
       has stepped back, so a taller earlier panel never shows its rows
       under the one in hand. */
    .s3d-stack > .pn {
      grid-area: 1 / 1;
      align-self: start;
      transform-origin: 50% 0;
      --cut: 2000px;
      -webkit-mask-image: linear-gradient(#000 var(--cut), transparent calc(var(--cut) + 64px));
      mask-image: linear-gradient(#000 var(--cut), transparent calc(var(--cut) + 64px));
    }

    /* ── the window ── */
    .s3d .pn {
      position: relative;
      border-radius: var(--r-win);
      background: var(--pn-surface);
      border: 1px solid var(--hairline-paper);
      box-shadow: var(--pn-shadow);
      -webkit-backdrop-filter: blur(14px) saturate(1.1);
      backdrop-filter: blur(14px) saturate(1.1);
      color: var(--ink);
      overflow: hidden;
      transform-style: preserve-3d;
      will-change: transform, opacity, filter;
    }
    /* Shuffled, Can's pick of 2026-09-06 out of lab/section-panels/bands:
       the same four stops in a different order on each window, at its own
       angle, starting at its own point along the gradient and drifting on
       its own clock, so the flight of headers is six weathers rather than
       one gradient cut six times. Each window carries its four stops, its
       angle, its phase and its clock inline on the band, a fixed function
       of its place in the day, so the page renders the same way twice and
       needs no script for it. The defaults below are the shipped band. */
    .s3d .pn__band {
      position: absolute;
      inset: 0 0 auto 0;
      height: 46%;
      pointer-events: none;
      background: linear-gradient(var(--band-angle, 115deg), var(--b1, #1E3639) 0%, var(--b2, #1C1C1C) 28%, var(--b3, #8B5E3C) 52%, var(--b4, #161529) 76%, var(--b1, #1E3639) 100%);
      background-size: 300% 100%;
      -webkit-mask-image: linear-gradient(#000, transparent);
      mask-image: linear-gradient(#000, transparent);
      opacity: 0.85;
      animation: s3d-band var(--band-dur, 22s) linear infinite alternate;
      animation-delay: var(--band-delay, 0s);
    }
    @keyframes s3d-band { from { background-position: 0% 0; } to { background-position: 100% 0; } }
    /* On the light page the body's ink would sit on a dark band, so the
       band is the bar and a little under it, in pixels the way the roster
       card masks its own. Dark keeps the deep wash. */
    :root:not([data-theme="dark"]) .s3d .pn__band {
      height: 130px;
      opacity: 0.72;
      -webkit-mask-image: linear-gradient(#000 0 34px, transparent 130px);
      mask-image: linear-gradient(#000 0 34px, transparent 130px);
    }
    .s3d .pn__bar {
      position: relative;
      display: flex;
      align-items: center;
      gap: 7px;
      padding: 15px 20px 0;
    }
    .s3d .pn__bar i { display: block; width: 9px; height: 9px; border-radius: 50%; background: rgba(242, 239, 233, 0.22); }
    .s3d .pn__title {
      margin-left: 8px;
      color: var(--pn-bone);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.14em;
      text-transform: uppercase;
    }
    .s3d .pn__when {
      margin-left: auto;
      color: var(--pn-bone);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.1em;
      font-variant-numeric: tabular-nums;
    }
    .s3d .pn__body {
      position: relative;
      padding: 18px 20px 20px;
      font-size: var(--t-small);
      line-height: 1.45;
    }

    /* ── inside a window ── */
    .s3d .g { border-radius: var(--r-in); background: var(--pn-inner); border: 1px solid var(--hairline-paper); }
    .s3d .u-face {
      display: block;
      flex: 0 0 auto;
      width: 30px;
      height: 30px;
      border-radius: 50%;
      object-fit: cover;
      border: 1.5px solid var(--brass-bright);
      background: var(--bone);
    }
    .s3d .u-face--s { width: 22px; height: 22px; border-width: 1px; }
    .s3d .u-orb {
      display: inline-block;
      flex: 0 0 auto;
      width: 9px;
      height: 9px;
      border-radius: 50%;
      background: radial-gradient(circle at 35% 35%, #E6B27F, var(--brass-bright) 55%, rgba(203, 143, 88, 0.15) 78%);
      box-shadow: 0 0 10px rgba(203, 143, 88, 0.55);
    }
    .s3d .u-orb--quiet { background: radial-gradient(circle at 35% 35%, var(--clay), var(--clay) 60%, transparent 78%); box-shadow: none; opacity: 0.6; }
    .s3d .u-orb--live { animation: s3d-orb 1.6s ease-in-out infinite; }
    @keyframes s3d-orb { 50% { box-shadow: 0 0 16px rgba(203, 143, 88, 0.9); } }
    .s3d .u-mono {
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.12em;
      text-transform: uppercase;
    }
    .s3d .u-chip {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      padding: 5px 11px;
      border-radius: 999px;
      border: 1px solid var(--hairline-paper);
      background: var(--pn-inner);
      color: var(--ink-soft);
      font-size: var(--t-micro);
      line-height: 1.2;
      white-space: nowrap;
    }
    .s3d .u-chip--on { border-color: var(--brass-bright); background: var(--brass-bright); color: var(--pn-on-brass); }
    .s3d .u-chip--you { border-color: var(--brass); color: var(--brass-bright); background: var(--brass-wash); }
    .s3d .u-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      padding: 8px 18px;
      border-radius: 999px;
      border: 1px solid var(--hairline-paper);
      color: var(--ink);
      font-size: var(--t-small);
      font-weight: 500;
      line-height: 1.2;
    }
    .s3d .u-btn--go { border-color: transparent; background: var(--brass-bright); color: var(--pn-on-brass); }
    .s3d .u-ask {
      margin: 0;
      font-family: var(--display);
      font-weight: var(--display-weight);
      font-size: var(--t-lead);
      line-height: 1.25;
      letter-spacing: -0.01em;
    }
    .s3d .u-ask .at { color: var(--brass-bright); }
    .s3d .u-ask .caret {
      display: inline-block;
      width: 2px;
      height: 0.95em;
      margin-left: 2px;
      vertical-align: -0.12em;
      background: var(--brass-bright);
      animation: s3d-caret 1s steps(1) infinite;
    }
    @keyframes s3d-caret { 50% { opacity: 0; } }
    .s3d .u-to { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
    .s3d .u-bar { display: flex; align-items: center; gap: 8px; margin-top: 16px; }
    .s3d .u-bar .u-btn { margin-left: auto; }
    .s3d .u-bub {
      padding: 12px 14px;
      border-radius: var(--r-in);
      background: var(--pn-inner);
      border: 1px solid var(--hairline-paper);
      color: var(--ink);
      font-size: var(--t-small);
      line-height: 1.45;
    }
    .s3d .u-bub--you { margin-left: 18%; background: var(--brass-wash); border-color: var(--brass-hair); }
    .s3d .u-turn { display: flex; gap: 10px; align-items: flex-start; }
    .s3d .u-turn + .u-turn { margin-top: 10px; }
    .s3d .u-turn > div { min-width: 0; flex: 1; }
    .s3d .u-steps { margin: 8px 0 0; padding: 0 0 0 4px; list-style: none; }
    .s3d .u-steps li {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 4px 0;
      color: var(--ink-soft);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.02em;
    }
    .s3d .u-steps li::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--clay); flex: 0 0 auto; }
    .s3d .u-steps li em { margin-left: auto; color: var(--clay); font-style: normal; font-variant-numeric: tabular-nums; }
    .s3d .u-steps li.live { color: var(--ink); }
    .s3d .u-steps li.live::before { background: var(--brass-bright); box-shadow: 0 0 8px var(--brass-bright); animation: s3d-orb 1.4s ease-in-out infinite; }
    .s3d .u-steps li.done::before { background: var(--brass); }
    .s3d .u-rows { display: grid; gap: 6px; }
    .s3d .u-row { display: flex; align-items: center; gap: 12px; padding: 9px 12px; }
    .s3d .u-row > div { min-width: 0; flex: 1; }
    .s3d .u-row b { display: block; font-weight: 500; color: var(--ink); }
    .s3d .u-row small { display: block; color: var(--clay); font-size: var(--t-micro); line-height: 1.35; margin-top: 1px; }
    .s3d .u-row .st { display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto; color: var(--ink-soft); font-size: var(--t-micro); white-space: nowrap; }
    .s3d .u-row .when {
      flex: 0 0 auto;
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.06em;
      white-space: nowrap;
      font-variant-numeric: tabular-nums;
    }
    /* The mark of the tool a row runs through, one of the marks the
       integrations wall already shows. */
    .s3d .u-via { display: block; flex: 0 0 auto; width: 16px; height: 16px; opacity: 0.85; }
    .s3d .u-row.is-sent { opacity: 0.55; }
    .s3d .u-row.is-sent .u-k { color: var(--brass-bright); border-color: var(--brass-hair); }
    .s3d .u-tick {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      flex: 0 0 auto;
      width: 28px;
      height: 28px;
      border-radius: 50%;
      border: 1.5px solid var(--brass-bright);
      color: var(--brass-bright);
      font-size: 14px;
    }
    .s3d .u-tick.is-on { background: var(--brass-bright); color: var(--pn-on-brass); }
    .s3d .u-k {
      flex: 0 0 auto;
      padding: 4px 8px;
      border-radius: 6px;
      border: 1px solid var(--hairline-paper);
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.08em;
      text-transform: uppercase;
    }
    .s3d .u-lanes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: 12px; }
    .s3d .u-lane { padding: 8px; min-height: 96px; }
    .s3d .u-lane .u-mono { display: flex; justify-content: space-between; margin-bottom: 6px; }
    .s3d .u-lane .u-mono b { color: var(--ink); font-weight: 500; }
    .s3d .u-card {
      display: flex;
      align-items: center;
      gap: 7px;
      padding: 7px 8px;
      margin-top: 5px;
      border-radius: 8px;
      background: var(--pn-surface);
      border: 1px solid var(--hairline-paper);
      font-size: var(--t-micro);
      line-height: 1.3;
      color: var(--ink);
    }
    .s3d .u-tabs { display: flex; gap: 18px; margin-bottom: 12px; padding: 0 2px; }
    .s3d .u-tabs span { padding-bottom: 8px; color: rgba(242, 239, 233, 0.6); font-size: var(--t-small); border-bottom: 1.5px solid transparent; }
    .s3d .u-tabs span.on { color: #F2F2F0; border-bottom-color: var(--brass-bright); }
    .s3d .u-toast { display: flex; align-items: center; gap: 12px; padding: 10px 14px 10px 10px; }
    .s3d .u-toast > div { min-width: 0; flex: 1; }
    .s3d .u-toast b { display: block; font-weight: 500; color: var(--ink); }
    .s3d .u-toast small { display: block; color: var(--clay); font-size: var(--t-micro); line-height: 1.35; }
    .s3d .u-toast .done {
      margin-left: auto;
      flex: 0 0 auto;
      color: var(--brass-bright);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.1em;
      text-transform: uppercase;
    }
    .s3d .u-count { display: flex; align-items: baseline; gap: 10px; margin-top: 14px; }
    .s3d .u-count b { font-family: var(--display); font-weight: var(--display-weight); font-size: var(--t-h3); line-height: 1; font-variant-numeric: tabular-nums; }
    .s3d .u-note { margin: 12px 0 0; color: var(--clay); font-size: var(--t-micro); line-height: 1.4; }
    .s3d .u-note em { color: var(--ink-soft); font-style: normal; }
    .s3d .u-cursor {
      position: absolute;
      z-index: 5;
      width: 22px;
      height: 22px;
      pointer-events: none;
      filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.6));
      opacity: 0;
    }
    .s3d .u-cursor path { fill: #F2EFE9; stroke: #0C0E11; stroke-width: 1.1; }

    /* ── still ───────────────────────────────────────────────────────────
       Two routes, the reader's own setting and the site's stamp, plus the
       page without the module: the track has no height, nothing sticks,
       nothing drifts, and the six read as a list in their finished state. */
    @media (prefers-reduced-motion: reduce) {
      .s3d .pn__band, .s3d .u-orb--live, .s3d .u-steps li.live::before, .s3d .u-ask .caret { animation: none; }
    }
    :root[data-reduced-motion="true"] .s3d .pn__band,
    :root[data-reduced-motion="true"] .s3d .u-orb--live,
    :root[data-reduced-motion="true"] .s3d .u-steps li.live::before,
    :root[data-reduced-motion="true"] .s3d .u-ask .caret { animation: none; }

    .s3d.is-still .s3d-copy,
    html.no-js .s3d-copy { position: static; padding-block: 0 2rem; }
    .s3d.is-still .s3d-track,
    html.no-js .s3d-track { height: auto !important; }
    .s3d.is-still .s3d-stage,
    html.no-js .s3d-stage { position: static; display: block; perspective: none; padding-bottom: 0; }
    .s3d.is-still .s3d-stack,
    html.no-js .s3d-stack { width: min(1000px, 100%); margin-inline: auto; gap: 20px; transform: none !important; }
    .s3d.is-still .s3d-stack > .pn,
    html.no-js .s3d-stack > .pn {
      grid-area: auto;
      transform: none !important;
      opacity: 1 !important;
      filter: none !important;
      visibility: visible !important;
      -webkit-mask-image: none;
      mask-image: none;
    }

    /* ── the phone, and anything under 900px: the hand ────────────────
       Can's ruling of 2026-09-06, after a scroll-driven fan he found not
       flowy: on the phone the six panels are a static hand of cards, five
       in view at once, the card in hand centred and on top, two peeking
       each side, turned away and stepped back. Nothing is tied to the
       page's scroll. A sideways swipe, or a tap on a side card, swaps
       which card is on top, with one short snap. The shape is the Codex
       floating fan he kept (lab/capability-stacks). What the phone does
       not get: no glass, no blur, no band drift, no plays, no six-line
       index (the card's own header is the time), and the panels trimmed
       to their first thing. Reduced motion or no script: the list. */
    @media (max-width: 900px) {
      .s3d { padding-block: clamp(3.5rem, 8vh, 5rem) clamp(1rem, 2vh, 1.5rem); }
      .s3d-grid { grid-template-columns: 1fr; gap: 1.2rem; }
      .s3d-copy { position: static; }
      .s3d-beats { display: none; }
      .s3d-foot { margin-top: 1rem; }
      .s3d-track { height: auto !important; }
      .s3d-stage { position: static; perspective: 900px; padding: 8px 0 32px; }
      /* flat, not preserve-3d: a turned side card's plane would cut through
         the card in hand and be painted split; flat keeps the look and
         lets z-index say who is on top. */
      /* The hand runs edge to edge of the screen, not of the section's
         inset: on a phone the inset would leave it 300px and the card in
         hand 180. The team box clips whatever a side card puts past the
         screen. */
      .s3d-stack { justify-self: stretch; width: 100vw; margin-left: calc(50% - 50vw); transform: none !important; transform-style: flat; touch-action: pan-y; -webkit-user-select: none; user-select: none; }
      /* No blue flash on a tap (Can, 2026-09-07): the card answers the tap
         by moving, which is all the acknowledgement it needs. */
      .s3d-stack, .s3d-stack * { -webkit-tap-highlight-color: transparent; }
      /* An image under a mouse starts a native drag that swallows the
         swipe. */
      .s3d-stack img { pointer-events: none; -webkit-user-drag: none; }
      .s3d-stack > .pn {
        width: 62%;
        justify-self: center;
        /* One height for the hand: every card as tall as the tallest, so a
           side card never shows under a shorter card in hand. */
        align-self: stretch;
        transform-origin: 50% 50%;
        /* visibility too, so a card leaving for the wings stays painted
           while it fades. */
        transition: transform .45s cubic-bezier(.2, .7, .2, 1), opacity .4s, visibility .4s;
        -webkit-mask-image: none;
        mask-image: none;
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        /* Opaque, and NOT the ground token itself: motion.js clears every
           wide, tall element painted in a ground colour so the page field
           can show through. A hair of bone in the paper keeps the panel a
           panel. */
        background: color-mix(in srgb, var(--paper) 96%, var(--bone));
        box-shadow: 0 18px 40px -30px rgba(0, 0, 0, 0.6);
        will-change: transform;
        cursor: pointer;
      }
      .s3d .pn__band { animation: none; }
      .s3d .u-orb--live, .s3d .u-steps li.live::before, .s3d .u-ask .caret { animation: none; }
      /* One thing a panel: the rows, the ask, the feed, the toast and the
         count. The chip bar, the tabs, the reply, the lanes and the notes
         are desktop furniture, and at this width the rows keep their
         words and lose the time on the right. */
      .s3d .u-bar, .s3d .u-tabs, .s3d .u-note, .s3d .u-bub--you, .s3d .u-lanes, .s3d .u-row .when, .s3d .u-k { display: none; }
      .s3d .u-count { margin-top: 12px; }
      .s3d .u-ask { font-size: var(--t-body); }
      /* The bar on one line at a card this narrow, and the reply's face
         goes so the bubble has the width. */
      .s3d .pn__title, .s3d .pn__when { white-space: nowrap; font-size: 10px; letter-spacing: 0.1em; }
      .s3d .pn__bar { gap: 5px; }
      .s3d .pn__bar i { width: 7px; height: 7px; }
      .s3d .u-turn > .u-face { display: none; }
      /* The list, for reduced motion and no script. */
      .s3d.is-still .s3d-stage, html.no-js .s3d-stage { perspective: none; padding: 0; }
      .s3d.is-still .s3d-stack, html.no-js .s3d-stack { gap: 16px; width: 100%; margin-left: 0; }
      .s3d.is-still .s3d-stack > .pn, html.no-js .s3d-stack > .pn { width: 100%; align-self: start; cursor: default; will-change: auto; transition: none; }
    }
    /* ── hand cards: start ── */
    /* The phone's own cards (Features, nine cards): a chat, a result, the schedule, the
       approvals, the done list, the pages. Under 900px they replace the
       six; above it they are not in the flow at all. A card is a flex
       column so its foot (the note, the composer's bar, the count) sits
       at the bottom of the window. Written by lab/section-panels/
       emit-hand.mjs; the lab file mobile.html carries the same rules. */
    .s3d .pn--h { display: none; }
    @media (max-width: 900px) {
      .s3d-stack.has-hand > .pn:not(.pn--h) { display: none; }
      .s3d-foot__six { display: none; }
      .s3d-foot__few { display: inline; }
      .s3d .pn--h { display: flex; flex-direction: column; }
      .s3d .pn--h .pn__body { display: flex; flex-direction: column; flex: 1; }
      .s3d .pn--h .u-bar { display: flex; }
      .s3d .pn--h .u-note, .s3d .pn--h .u-bub--you { display: block; }
      .s3d .pn--h .u-bar, .s3d .pn--h .u-note, .s3d .pn--h .u-count { margin-top: auto; padding-top: 12px; }
      .s3d .pn--h .u-row b .st { margin-left: 8px; font-weight: 400; vertical-align: 1px; }
      .s3d .u-chat { display: grid; gap: 7px; }
      .s3d .u-steps + .u-chat, .s3d .u-res + .u-chat, .s3d .u-rows + .u-chat, .s3d .u-done + .u-chat, .s3d .u-pages + .u-chat,
      .s3d .u-chat + .u-rows, .s3d .u-chat + .u-res, .s3d .u-chat + .u-done, .s3d .u-chat + .u-pages, .s3d .u-chat + .u-steps { margin-top: 9px; }
      .s3d .u-msg { display: flex; align-items: flex-end; gap: 7px; max-width: 94%; }
      .s3d .u-msg--you { justify-self: end; }
      .s3d .u-msg .u-bub { padding: 8px 11px; line-height: 1.4; }
      .s3d .u-msg--you .u-bub { margin-left: 0; }
      .s3d .u-res { padding: 4px 10px; }
      .s3d .u-res > div { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; padding: 5px 0; }
      .s3d .u-res > div + div { border-top: 1px solid var(--hairline-paper); }
      .s3d .u-res b { font-weight: 500; color: var(--ink); }
      .s3d .u-res em { color: var(--clay); font-family: var(--mono); font-size: var(--t-micro); font-style: normal; letter-spacing: 0.04em; white-space: nowrap; font-variant-numeric: tabular-nums; }
      .s3d .u-res .hot em { color: var(--brass-bright); }
      .s3d .u-bars + .u-res { margin-top: 10px; }
      .s3d .u-sched { display: grid; gap: 6px; }
      .s3d .u-slot { display: flex; align-items: center; gap: 9px; padding: 8px 10px; }
      .s3d .u-slot time { flex: 0 0 auto; min-width: 3.4em; color: var(--clay); font-family: var(--mono); font-size: var(--t-micro); letter-spacing: 0.06em; line-height: 1.25; }
      .s3d .u-slot time b { display: block; color: var(--ink-soft); font-weight: 400; }
      .s3d .u-slot > div { min-width: 0; flex: 1; }
      .s3d .u-slot > div b { display: block; font-weight: 500; color: var(--ink); }
      .s3d .u-slot > div small { display: block; color: var(--clay); font-size: var(--t-micro); line-height: 1.3; }
      .s3d .u-done { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; }
      .s3d .u-done li { display: flex; align-items: center; gap: 9px; padding: 6px 10px; border-radius: var(--r-in); background: var(--pn-inner); border: 1px solid var(--hairline-paper); }
      .s3d .u-done li > div { min-width: 0; flex: 1; }
      .s3d .u-done b { display: block; font-weight: 500; color: var(--ink); }
      .s3d .u-done small { display: block; color: var(--clay); font-size: var(--t-micro); }
      .s3d .u-tick--s { width: 18px; height: 18px; font-size: 10px; border-width: 1.2px; }
      .s3d .u-pages { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
      .s3d .u-page { padding: 8px 10px; }
      .s3d .u-page b { display: block; font-weight: 500; color: var(--ink); }
      .s3d .u-page small { display: block; color: var(--clay); font-size: var(--t-micro); margin-top: 2px; }
      .s3d .u-bars { display: flex; align-items: flex-end; gap: 4px; height: 30px; margin-top: 10px; }
      .s3d .u-bars i { flex: 1; border-radius: 3px 3px 0 0; background: color-mix(in srgb, var(--ink) 22%, transparent); }
      .s3d .u-bars i.hot { background: var(--brass-bright); }
    }
    /* ── hand cards: end ── */
    @media (max-width: 900px) and (prefers-reduced-motion: reduce) {
      .s3d-stack > .pn { transition: none; }
    }
    @media (max-width: 640px) {
      .s3d .pn__body { padding: 14px 14px 16px; }
      .s3d .pn__bar { padding: 13px 14px 0; }
      .s3d .u-via { display: none; }
      .s3d .u-row .when { white-space: normal; max-width: 34%; text-align: right; line-height: 1.3; }
      .s3d .u-row small { display: none; }
    }

    /* ── Natively powered by ──────────────────────────────────────────────
       Founder call 2026-08-20. Claude Code is the native tool and is named as
       one, on its own line with its own mark. Everything else is demoted to a
       second rank under "Also available on" and cycles rather than sitting in
       a flat row, which is what lets the list grow past what fits on a line. */
    .s4-native {
      display: flex;
      gap: 0.7em;
      align-items: center;
      justify-content: center;
      margin: 0;
      color: var(--ink);
      font-size: var(--t-h4);
      font-weight: var(--display-weight);
      letter-spacing: -0.012em;
      line-height: 1.2;
    }
        /* The product's name is one word for wrapping purposes, Can's call
       2026-09-04 off his phone, where the line broke between Claude and Code.
       The full stop sits outside the em with no space before it, so it rides
       along with Code and never lands alone at the head of a line. The line
       still wraps after "by", which is where it should break if it must. */
    .s4-native em { color: var(--brass); font-style: normal; white-space: nowrap; }
    .s4-native-mark {
      width: 1.15em;
      height: 1.15em;
      object-fit: contain;
    }
    /* On a phone the line wraps, and a flex row would centre the block but
       leave its two lines ragged left beside the mark (Can, 2026-09-07).
       The mark rides inline at the head of the first line and both lines
       centre. */
    @media (max-width: 900px) {
      .s4-native { display: block; text-align: center; }
      .s4-native-mark { display: inline-block; vertical-align: -0.18em; margin-right: 0.3em; }
    }

    .s4-also {
      margin-top: clamp(1.6rem, 3.2vh, 2.6rem);
      text-align: center;
    }
    .s4-also-lbl {
      display: block;
      color: var(--brass);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.16em;
      text-transform: uppercase;
    }

    /* A marquee, right to left, founder call 2026-08-20. The list is meant to
       keep growing past what fits on a line, so it travels instead of wrapping.

       THREE copies of the set, not two, and the animation runs exactly one
       copy's worth: -33.3333%. Two was wrong and it showed. A marquee only
       looks continuous while the copies BEHIND the one being shifted still
       cover the window, which needs setWidth x (copies - 1) >= windowWidth.
       With two copies that is just setWidth, and one set measured 795px
       against an 1117px window, so every lap ended with a 322px hole running
       in from the right. Founder caught it: "the right side is starting to go
       low and I'm not seeing things in there."

       At three copies the margin is two full sets, which holds at any width
       this page will ever be laid out at, and holds as the list grows.

       The seam still depends on each copy being exactly equal, which is why
       the spacing is a margin on each mark and not a gap on the row: with gap,
       N marks carry N-1 gaps and a third of the row is not a third of the
       content. The two extra copies are aria-hidden, so the list is read once.

       Linear, not eased: an eased marquee visibly speeds up and slows down
       every lap, which reads as a glitch rather than as motion. */
    .s4-ticker {
      overflow: hidden;
      cursor: grab;
      /* Sideways is ours, vertical stays the page's, so a touch drag over the
         strip still scrolls the article. */
      touch-action: pan-y;
      margin-top: clamp(0.9rem, 1.8vh, 1.4rem);
      mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
      -webkit-mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
    }

    /* The spacing is a margin on each mark, NOT a gap on the row, and that is
       load bearing. With gap, fourteen items carry thirteen gaps, so half the
       row is one set plus half a gap and the reset lands a few dozen pixels
       off: a small hitch, once a lap, forever. As a margin every mark is
       exactly item plus space wide, fourteen of them are two identical sets,
       and -50% is precisely one set. */
    .s4-marks {
      display: flex;
      width: max-content;
      gap: 0;
      margin: 0;
      padding: clamp(0.5rem, 1vh, 0.9rem) 0;
      list-style: none;
      animation: s4-marquee 34s linear infinite;
    }

    .s4-ticker .s4-mark { margin-right: clamp(30px, 4vw, 72px); }


    .s4-marks:hover { animation-play-state: paused; }

    .s4-ticker[data-dragging="true"] { cursor: grabbing; }
    /* Nothing inside should select or drag as an image while the strip is
       being thrown by hand. */
    .s4-ticker[data-dragging="true"] .s4-marks { user-select: none; }
    .s4-ticker img { -webkit-user-drag: none; user-select: none; }

    @keyframes s4-marquee {
      from { transform: translateX(0); }
      to   { transform: translateX(-33.3333%); }
    }

    /* Stacked and still. A ticker a reader cannot pause is exactly the thing
       this preference exists to switch off, and the list still reads. */
    /* Still, and wrapped into a centred block. A marquee a reader cannot stop
       is exactly what this preference exists to switch off. The duplicate set
       is dropped here rather than hidden, because with nothing moving it would
       simply read as every mark printed twice. */
    @media (prefers-reduced-motion: reduce) {
      .s4-ticker { overflow: visible; mask-image: none; -webkit-mask-image: none; }
      .s4-marks {
        width: auto;
        flex-wrap: wrap;
        justify-content: center;
        gap: clamp(12px, 1.4vw, 26px) clamp(26px, 3.4vw, 62px);
        animation: none;
        transform: none;
      }
      .s4-ticker .s4-mark { margin-right: 0; }
      .s4-mark[aria-hidden="true"] { display: none; }
    }
    html[data-reduced-motion="true"] .s4-ticker {
      overflow: visible; mask-image: none; -webkit-mask-image: none;
    }
    html[data-reduced-motion="true"] .s4-marks {
      width: auto;
      flex-wrap: wrap;
      justify-content: center;
      gap: clamp(12px, 1.4vw, 26px) clamp(26px, 3.4vw, 62px);
      animation: none;
      transform: none;
    }
    html[data-reduced-motion="true"] .s4-ticker .s4-mark { margin-right: 0; }
    html[data-reduced-motion="true"] .s4-mark[aria-hidden="true"] { display: none; }

    .s4-mark {
      display: flex;
      flex-direction: column;
      gap: 0.85rem;
      align-items: center;
      color: var(--ink-soft);
      text-align: center;
      transition: color 260ms var(--ease);
    }

    .s4-mark:hover { color: var(--ink); }

    /* Each mark is set the way its owner draws it. Eight of the fourteen have a
       colour version and are shown in it, straight from the file.

       The other six do NOT, and that is a fact about those brands rather than
       a gap in the assets: OpenAI, Grok, Cursor, Kimi, opencode and Pi all
       publish a single monochrome mark, and monochrome means black. On this
       ground black is invisible, so those six are painted in the page's own
       ink through a mask on the file's alpha, which also means they follow the
       theme when it flips. Asking for them "in colour" has no answer; there is
       no colour to use.

       The mask also lets a PNG and an SVG sit in one row without reading as
       two different things.

       Founder call 2026-08-20: use the real marks, in colour wherever the
       brand actually has colour. */
    .s4-glyph,
    .s4-logo {
      width: clamp(24px, 2vw, 32px);
      height: clamp(24px, 2vw, 32px);
    }

    .s4-logo {
      object-fit: contain;
      /* Same optical correction as the masked marks, applied to the box rather
         than to a mask size. */
      scale: var(--mark-scale, 1);
    }

    .s4-glyph {
      background: currentColor;
      -webkit-mask-image: var(--mark);
      mask-image: var(--mark);
      -webkit-mask-repeat: no-repeat;
      mask-repeat: no-repeat;
      -webkit-mask-position: center;
      mask-position: center;
      /* Not contain: every vendor leaves a different amount of air inside its
         own artboard, so eight marks in identical boxes do not read as one
         size. --mark-scale corrects each to the same optical weight. The fill
         ratios behind the numbers come from qa/provider-mark-probe.mjs, and the
         last few points are eye rather than arithmetic, because a solid square
         reads heavier than an open star at the same measured width. */
      -webkit-mask-size: calc(100% * var(--mark-scale, 1));
      mask-size: calc(100% * var(--mark-scale, 1));
    }

    .s4-name {
      font-family: var(--sans);
      font-size: var(--t-small);
      font-weight: 400;
      letter-spacing: 0.01em;
      line-height: 1.2;
    }

    .s4-foot {
      text-align: center;
      /* No measure. It is one line of caption under a row that already spans
         the shell, and a 68ch cap broke it two words from the end. */
      max-width: none;
      margin-top: clamp(1.6rem, 3vh, 2.2rem);
      color: var(--ink-soft);
      font-size: var(--t-body);
      font-weight: 300;
      line-height: 1.55;
    }
    .s4-foot em { color: var(--ink); font-style: italic; }

    /* ── The four variants ────────────────────────────────────────────────
       Founder ask 2026-08-20: "the only thing I really want to show is the
       icons of the different models, bring your own key, and maybe powered by.
       Suggest me some options and add buttons so I can switch between them."

       One markup, four states, switched by data-power on the root. The
       SHIPPED default is set in the HTML, not by the lab, so the live page
       gets it whether or not the localhost chrome is mounted.

         full    what it was: eyebrow, headline, lede, marks, closing line
         claim   eyebrow, headline, marks. The lede and the closing line go
         ribbon  eyebrow, marks, one line. The default
         folded  the ribbon, relocated under the roster card. No fourth band

       Only what is hidden is declared here; everything else stays as it is,
       so a variant can never drift from the real thing. */
    /* Set at --t-small, the same step as the ideas list, the side values and
       the costs caveat. Founder call 2026-08-20: this is a footnote to the
       row above it, not a statement, so it takes the page's small size rather
       than the lead. No measure, for the same reason the old foot had none:
       it is one line under a row that already spans the card. */
    .s4-note {
      display: none;
      max-width: none;
      margin: clamp(1.4rem, 2.6vh, 2rem) auto 0;
      text-align: center;
      color: var(--ink-soft);
      font-size: var(--t-small);
      font-weight: 300;
      line-height: 1.5;
      text-wrap: balance;
    }
    .s4-note em { color: var(--brass); font-style: normal; }

    :root[data-power="claim"] .s4-lede,
    :root[data-power="claim"] .s4-foot { display: none; }

    :root[data-power="ribbon"] .s4-headline,
    :root[data-power="ribbon"] .s4-lede,
    :root[data-power="ribbon"] .s4-foot,
    :root[data-power="folded"] .s4-headline,
    :root[data-power="folded"] .s4-lede,
    :root[data-power="folded"] .s4-foot { display: none; }

    :root[data-power="ribbon"] .s4-note,
    :root[data-power="folded"] .s4-note { display: block; }

    /* With the headline gone the eyebrow IS the heading, so the label and the
       row it introduces close right up: the two read as one object rather
       than a title floating above a strip.

       The rule under the label replaces the full width hairline that used to
       run above the marks. Founder call 2026-08-20: a short centred line, set
       to the label rather than to the card. It is drawn on the eyebrow so it
       travels with the word instead of belonging to the row. */
    :root[data-power="ribbon"] .s4-head,
    :root[data-power="folded"] .s4-head { display: none; }
    :root[data-power="ribbon"] .s4-power { padding-block: clamp(2.6rem, 5vh, 4rem); }

    /* Folded is what ships, so the block sits inside .s3-inner in the markup
       and there is no fourth band at all. It keeps the top rule: with the
       roster foot removed on 2026-08-20 that hairline is the only thing
       separating the marks from the card above them. */
    :root[data-power="folded"] .s4-inner { width: 100%; }
    :root[data-power="folded"] .s3-inner > .s4-inner {
      margin-top: clamp(3rem, 6vh, 4.5rem);
    }


    @media (max-width: 560px) {
      .s4-marks { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    }

    /* ══ THE FLOATING HEADER ══════════════════════════════════════════════
       Added 2026-08-26 on Can's brief. A glass pill that hovers clear of the
       top edge rather than a bar welded to it, so the page still reads as
       paper with something resting on it.

       It is ABSENT over the hero, deliberately. The sphere is the first thing
       this page says and a glass bar across it reads as chrome laid over art.
       The pill is built, parked above the viewport, and only drops in once
       the hero has fully left. After that it hides on a downward scroll and
       returns on an upward one, so reading is never fenced by it.

       Every colour here is mixed from --paper and --ink, which is what lets
       one rule serve both themes: the glass is the page's own ground at 72%
       and the hairline is the page's own ink at 12%. Nothing is hard coded.  */
    .site-nav {
      position: fixed;
      z-index: 60;
      top: clamp(12px, 1.8vh, 22px);
      left: 50%;
      display: grid;
      grid-template-columns: auto minmax(0, 1fr) auto;
      align-items: center;
      gap: clamp(0.75rem, 3vw, 2.5rem);
      width: min(100% - (2 * var(--gutter)), 1080px);
      padding: 9px 9px 9px 24px;
      border-radius: 999px;
      /* The paint moved to assets/glass.css on 2026-08-27, Can's glass set
         from lab/glass-lab: a 6px frost, a bone tint on paper and an ink one
         on ink, a specular rim and a half-pixel brass hairline. It was a
         paper fill at 72% under an 18px blur with a mixed hairline. The one
         lesson that file keeps: the cast shadow and the rim are CONSTANT,
         never mixed from --ink or --paper, because dark is dark and light is
         light in both themes. */
      /* Shown from the first frame since 2026-09-02 (Can): the script stamps
         data-shown on load, and without script the bar is simply there. */
      opacity: 1;
      pointer-events: auto;
      translate: calc(-50% - var(--nav-shift, 0px)) 0;
      transition: translate 620ms var(--ease), opacity 380ms var(--ease);
    }

    .site-nav[data-shown="true"] {
      opacity: 1;
      pointer-events: auto;
      translate: calc(-50% - var(--nav-shift, 0px)) 0;
    }

    .site-nav__brand {
      justify-self: start;
      color: var(--ink);
      text-decoration: none;
    }

    .site-nav__links {
      display: flex;
      justify-content: center;
      gap: clamp(0.9rem, 2.4vw, 2.1rem);
      margin: 0;
      padding: 0;
      list-style: none;
    }

    .site-nav__link {
      color: var(--ink-soft);
      font-family: var(--sans);
      font-size: var(--t-small);
      letter-spacing: -0.01em;
      text-decoration: none;
      white-space: nowrap;
      transition: color 320ms var(--ease);
    }

    .site-nav__link:hover { color: var(--ink); }

    .site-nav__actions {
      display: flex;
      align-items: center;
      gap: 8px;
      justify-self: end;
    }

    /* The bespoke door. Ghost on purpose: the brass is spoken for by the one
       action the page is actually built to get, and two filled pills in a
       36px bar read as an argument between them. The hairline is the same
       --brass-hair the close's second door carries, so the two secondary
       doors on the page are the same object at two sizes. */
    .site-nav__custom { white-space: nowrap; }

    /* The header's own button. Same pill as .btn, sized down so the bar stays
       a bar: a full height .btn makes the glass twice as tall as it needs. */
    .btn--sm {
      min-height: 40px;
      padding: 0.7rem 1.35rem;
      box-shadow: none;
    }

    /* No motion for a reader who asked for none: it simply is or is not there. */
    html[data-reduced-motion="true"] .site-nav { transition: opacity 200ms linear; }
    html[data-reduced-motion="true"] .site-nav,
    html[data-reduced-motion="true"] .site-nav[data-shown="true"] { translate: calc(-50% - var(--nav-shift, 0px)) 0; }

    @media (max-width: 900px) {
      /* The links go, the brand and the buttons stay. Four anchors squeezed
         into a phone width read as a scrollbar, not as navigation. */
      .site-nav {
        grid-template-columns: auto auto;
        justify-content: space-between;
        padding-left: 20px;
      }
      /* The WRAPPER, not just the list inside it. Hiding only .site-nav__links
         left an empty <nav> holding a grid cell, so three children went into
         a two column template and the buttons wrapped to a second row: the
         pill measured 111px tall at 900 and 95px at 390 against 60px on
         desktop. Pre-existing since the header shipped, caught 2026-08-26
         when the second button made the second row obvious. */
      .site-nav > nav { display: none; }
    }

    /* And below this the ghost stands down too. Measured, not guessed: the
       wordmark, both pills and the padding come to about 330px, which fits a
       620px bar and does not fit a 390px one. The offer is not lost, it is
       one screen further down in the close. */
    @media (max-width: 620px) {
      .site-nav__custom { display: none; }
    }

    /* ══ THE INTEGRATIONS BAND ════════════════════════════════════════════
       Added 2026-08-26 on Can's brief, off the reference wall he sent. Three
       rows instead of the reference's one static grid, and the middle row
       runs the other way: two rows travelling together read as one sheet
       sliding, three with the middle reversed read as a field.

       The marquee maths is section 4's, unchanged and for the same reasons.
       THREE copies of each set, the animation running exactly one copy's
       worth at -33.3333%, and the spacing carried as a margin on each mark
       rather than a gap on the row. See the long note above .s4-ticker for
       why two copies tore and why gap put the seam in the wrong place. The
       durations are deliberately mismatched (34s, 41s, 37s) so the three
       rows never settle into a repeating pattern with each other.

       THE TILES ARE FIXED CREAM, NOT --paper, and that is load bearing.
       Five of the twenty eight marks are black or near black (github, x,
       notion, miro, docusign) and would vanish the moment the theme flips.
       The alternative was masking those five into page ink like the provider
       marks above, but that flattens notion's outline into a blob and eats
       miro's yellow, and recolouring a partner's logo is the thing most
       brand guidelines actually forbid. A constant warm chip under every
       mark keeps all twenty eight in their own colours, correct in both
       themes, with no per logo exceptions to maintain. */
    /* .draft-section is display:flex with align-items:center, and every other
       section on the page hands it exactly one child, the .wrap. This one has
       three (head, rows, foot), so without a direction they laid out as three
       columns across the page: the headline on the left, the wall squeezed
       into the middle, the closing line off on the right. Column, not row. */
    /* NOT full height, and not snapped. Can's standing rule from 2026-08-26:
       no new section takes a whole screen unless he asks for one, and these
       sit closer together than the older sections do, the way an ordinary
       page reads. .draft-section's 100vh and its snap are both undone here. */
    .draft-section.ib-section {
      flex-direction: column;
      justify-content: center;
      min-height: 0;
      padding-block: var(--band-pad);
      /* One ground for every section, Can's call 2026-08-26. This was --bone
         and read as a warm band cutting across an otherwise even page. */
      background: var(--paper);
      overflow: hidden;
      scroll-snap-align: none;
    }

    .ib-section .sec-head { text-align: center; }
    .ib-section .sec-head .h2 { margin-inline: auto; }

    /* The break in the markup is the break Can asked for, so the measure has
       to be wide enough to honour it and balance has to stay out of it: both
       would otherwise re-wrap "that runs your company" onto a third line. */
    .ib-headline {
      max-width: none;
      text-wrap: nowrap;
    }

    @media (max-width: 620px) {
      /* Too narrow for the long line to hold. Let it wrap again.

         THE SPACE BEFORE THE BR IN THE MARKUP IS LOAD BEARING. Hiding the
         break here leaves the two words either side of it touching, and
         without that space the headline read "everythingthat runs" on Can's
         phone, 2026-09-04. At full width the space sits at the end of a line
         and collapses, so it costs the desktop nothing. */
      .ib-headline { text-wrap: balance; }
      .ib-headline br { display: none; }
    }

    .ib-lede {
      max-width: 46ch;
      margin: 1.1rem auto 0;
      color: var(--ink-soft);
      text-align: center;
    }

    .ib-rows {
      display: grid;
      gap: clamp(0.7rem, 1.4vw, 1.1rem);
      margin-top: clamp(2.2rem, 5vh, 3.4rem);
      /* Narrow, Can's call 2026-08-26, and the width is load bearing rather
         than taste. A tile plus its margin is about 84px, so 1000px shows
         about twelve tiles, which is exactly one row's disjoint set. Widen
         this and a row starts showing a logo twice on one screen, which is
         the repetition he caught. See the note on the markup for the rest of
         the arithmetic. */
      /* The gutters are already taken by .ib-field, which wraps this. Keep
         this figure and .ib-wires' width in step. */
      width: min(100%, 1000px);
      margin-inline: auto;
    }

    /* ── The wiring ──────────────────────────────────────────────────────
       The field is the full page width so the two nodes have somewhere to
       sit outside the 1000px band of rows. See the note on the markup for
       why the curves are SVG and the nodes are not. */
    /* Wider than the band of rows so the two nodes have room to sit outside
       it, but NOT the full page: at 100% the nodes landed on the viewport
       edges and were clipped in half. */
    .ib-field {
      position: relative;
      display: flex;
      justify-content: center;
      width: min(100% - (2 * var(--gutter)), 1340px);
      margin-inline: auto;
    }

    .ib-wires {
      position: absolute;
      top: 0;
      height: 100%;
      /* From the node across to the edge of the band: half of whatever the
         field has left over once the band of rows has taken its width. The
         inner expression is .ib-rows' own width, and the two have to stay in
         step, so change them together. */
      width: calc((100% - min(100%, 1000px)) / 2);
      fill: none;
      stroke: var(--brass-hair);
      stroke-width: 1;
      pointer-events: none;
    }

    .ib-wires--left { left: 0; }
    .ib-wires--right { right: 0; }

    .ib-node {
      position: absolute;
      top: 50%;
      width: 9px;
      height: 9px;
      border-radius: 50%;
      background: var(--brass);
      translate: 0 -50%;
      /* The same soft brass pool section 5's walked dots carry. */
      box-shadow: 0 0 0 4px var(--brass-wash), 0 0 14px 2px var(--brass-wash);
    }

    .ib-node--left { left: 0; translate: -50% -50%; }
    .ib-node--right { right: 0; translate: 50% -50%; }

    @media (max-width: 900px) {
      /* No room either side of the band once the page is this narrow, and a
         wire with nowhere to run reads as a stray hairline. */
      .ib-wires, .ib-node { display: none; }
    }

    .ib-row {
      overflow: hidden;
      cursor: grab;
      /* Sideways is ours, vertical stays the page's, so a touch drag across
         the wall still scrolls the article. Same call as the provider strip. */
      touch-action: pan-y;
      mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
      -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
    }

    .ib-row[data-dragging="true"] { cursor: grabbing; }
    .ib-row[data-dragging="true"] .ib-track { user-select: none; }
    .ib-row img { -webkit-user-drag: none; user-select: none; }

    .ib-track {
      display: flex;
      width: max-content;
      gap: 0;
      margin: 0;
      padding: 0;
      list-style: none;
      animation: ib-marquee 34s linear infinite;
    }

    .ib-row:hover .ib-track { animation-play-state: paused; }

    .ib-row--b .ib-track { animation-duration: 41s; animation-direction: reverse; }
    .ib-row--c .ib-track { animation-duration: 37s; }

    @keyframes ib-marquee {
      from { transform: translateX(0); }
      to   { transform: translateX(-33.3333%); }
    }

    .ib-tile {
      display: grid;
      place-items: center;
      flex: 0 0 auto;
      width: clamp(56px, 5.4vw, 74px);
      height: clamp(56px, 5.4vw, 74px);
      margin-right: clamp(0.55rem, 1.1vw, 0.9rem);
      /* Constant in both themes. See the note above. It was #F7F3EA, which
         sat two points off the old --bone ground and read fine against it;
         once every section went to --paper on 2026-08-26 the chip and the
         page were within a hair of each other and the tiles all but
         disappeared in light. Lifted above --paper rather than dropped below
         it, so the chip still reads as a light card in both themes.

         Neutral since 2026-08-31: it was the warm #FDFBF6 over a warm paper,
         and it still has to sit just above --paper, which is #F3F3F3 now. The
         hairline and the two shadows were the old ink and move with it. */
      border: 1px solid rgba(22, 22, 22, 0.07);
      border-radius: 20px;
      background: #FCFCFC;
      box-shadow: 0 2px 8px rgba(22, 22, 22, 0.05);
      transition: translate 420ms var(--ease), box-shadow 420ms var(--ease);
    }

    .ib-tile:hover {
      box-shadow: 0 10px 24px rgba(22, 22, 22, 0.14);
      translate: 0 -3px;
    }

    .ib-tile img {
      width: 52%;
      height: 52%;
      object-fit: contain;
    }

    .ib-foot {
      margin-top: clamp(1.8rem, 4vh, 2.8rem);
      color: var(--ink-soft);
      font-size: var(--t-small);
      text-align: center;
    }

    /* Stacked and still. A wall a reader cannot stop is exactly what this
       preference exists to switch off, and the marks still read as a grid. */
    html[data-reduced-motion="true"] .ib-rows {
      width: auto;
      margin-inline: auto;
    }
    html[data-reduced-motion="true"] .ib-row {
      overflow: visible;
      cursor: default;
      mask-image: none;
      -webkit-mask-image: none;
    }
    html[data-reduced-motion="true"] .ib-track {
      flex-wrap: wrap;
      justify-content: center;
      width: auto;
      animation: none;
    }
    html[data-reduced-motion="true"] .ib-tile { margin: 0 clamp(0.3rem, 0.6vw, 0.5rem) clamp(0.6rem, 1.2vw, 0.9rem) 0; }
    html[data-reduced-motion="true"] .ib-tile[aria-hidden="true"] { display: none; }

    /* ══ THE TRUST BAND ═══════════════════════════════════════════════════
       Added 2026-08-26. Static, not a marquee, and that is the point: a
       reader checking who holds what should not have to chase it past.

       EVERY CERTIFICATION NAMED HERE BELONGS TO THE VENDOR, NEVER TO US, AND
       THE MARKUP SAYS SO IN THE SAME BREATH AS THE LOGO. Aita holds no SOC 2,
       no ISO 27001 and no HIPAA attestation, and none of those seals may
       appear on this page as ours: they are licensed to the audited entity
       alone. What is true, and stronger, is that the client owns the
       DigitalOcean account and the Anthropic key outright, so they are the
       direct customer of both and hold those certifications first hand
       rather than through us. See deploy/README.md in the app repo.

       Verified against each vendor's own trust page on 2026-08-26:
         Anthropic     SOC 2 Type II, ISO 27001:2022, ISO 42001:2023, HIPAA BAA
         DigitalOcean  SOC 2 Type II, SOC 3 (Schellman), HIPAA and DORA eligible
         Caddy         automatic TLS via Let's Encrypt, per deploy/bootstrap.sh
         Ubuntu        24.04 LTS with ufw closed to all but SSH, HTTP, HTTPS

       DigitalOcean's ISO 27001 is NOT on their current trust page and is not
       claimed here. Do not add it back without re checking. There is also no
       GDPR mark: the EU emblem is restricted and the point is made in words.

       The four marks are monochrome by origin, so they are painted in page
       ink through a mask on the file's alpha, exactly like the six provider
       marks in section 4. That is also why they follow the theme for free. */
    /* Not full height, not snapped. Same standing rule as the wall above.

       The two paddings are DIFFERENT, and deliberately so. Can measured the
       run from section 5's button down to this eyebrow against the run from
       the vendor cards down to the close, and the second was the shorter of
       the two. The first is section 5's padding-bottom plus this section's
       padding-top; so this section's padding-bottom mirrors section 5's, and
       the close's padding-top mirrors this section's, which makes the two
       runs the same sum at every viewport height rather than at one.

       They still will not be pixel identical, because .nx-section is one of
       the older full height sections and centres its content, so whatever is
       left over after 100vh lands in that first gap as well. Matching that
       too would mean taking section 5 off full height, which is a separate
       job Can has said he wants to do himself. */
    .draft-section.tb-section {
      min-height: 0;
      padding-block: var(--band-pad);
      scroll-snap-align: none;
    }

    .tb-section .sec-head { text-align: center; }
    .tb-section .sec-head .h2 { margin-inline: auto; }

    .tb-lede {
      max-width: 62ch;
      margin: 1.1rem auto 0;
      color: var(--ink-soft);
      text-align: center;
    }

    /* THE RAIL. Chosen by Can on 2026-08-26 from four candidates, against a
       measured baseline: the four tall cards were 430px of content in a 682px
       band, and the band was the thing making the lower page long.

       The boxes came off entirely. Four segments split by hairlines, each
       vendor over ONE mono line of what it holds. Same four vendors, same
       facts, roughly a third of the height.

       The one line is a constraint on the copy, not a wrap setting. At the
       1152px shell a segment is 288px wide and about 250px inside its
       padding, which at --t-micro is a little over forty characters. Every
       string below was written to that budget:

         "SOC 3 Type II" became "SOC 3"        the Type II is on the SOC 2,
                                               where the distinction matters
         "CSA STAR Level 1" became "CSA STAR L1"
         "Renewed automatically by Caddy" became "renewed by Caddy"
         "Firewall closed to all but SSH and HTTPS" became "SSH and HTTPS
                                               only", which is the same fact
                                               said from the other side

       If you add a certification, shorten another or the segment wraps. It
       degrades to two lines rather than overflowing, so a wrap is a nudge and
       not a break. */
    .tb-rail {
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      margin-top: clamp(2rem, 4.5vh, 3rem);
      padding: 0;
      list-style: none;
    }

    .tb-seg {
      display: grid;
      justify-items: center;
      gap: 0.55rem;
      padding-inline: clamp(0.7rem, 1.2vw, 1.1rem);
      /* The divider is the left edge of every segment but the first, so the
         rail has three rules and not five. */
      border-left: 1px solid var(--hairline-paper);
      text-align: center;
    }

    .tb-seg:first-child { border-left: 0; }

    .tb-who {
      display: flex;
      align-items: center;
      gap: 0.5rem;
    }

    /* 20px, down from 34. At 34 the mark was the tallest thing in a segment
       and set the rail's height on its own. */
    .tb-mark {
      width: 20px;
      height: 20px;
      background: var(--ink);
      -webkit-mask-image: var(--mark);
      mask-image: var(--mark);
      -webkit-mask-repeat: no-repeat;
      mask-repeat: no-repeat;
      -webkit-mask-position: center;
      mask-position: center;
      -webkit-mask-size: contain;
      mask-size: contain;
      opacity: 0.82;
      transition: background-color 350ms var(--ease), opacity 350ms var(--ease);
    }

    /* Brand colour under the pointer, Can's call 2026-08-27. At rest the four
       marks are page ink and the rail is one quiet line; hovering a segment
       gives its mark the owner's own colour, which is the only movement the
       band has and all it needs. The colour rides --brand on each segment in
       the markup, next to the file it belongs to, so a new vendor is one li.

       --brand-dark is the exception for a mark whose brand colour is too dark
       to read on dark paper: Let's Encrypt's navy vanishes into it. Anthropic
       carries the Claude terracotta rather than its wordmark black, which is
       the colour the page already gives Claude in section 3 and the close. */
    .tb-seg:hover .tb-mark {
      background: var(--brand, var(--ink));
      opacity: 1;
    }
    :root[data-theme="dark"] .tb-seg:hover .tb-mark {
      background: var(--brand-dark, var(--brand, var(--ink)));
    }

    .tb-name {
      color: var(--ink);
      font-family: var(--sans);
      font-size: var(--t-small);
      font-weight: 600;
      letter-spacing: -0.01em;
    }

    /* balance is for the band between about 1180 and 1400, where four up
       still beats two up on height but the Anthropic line is two pixels wider
       than its segment. It takes two lines there. Without balance the wrap
       leaves "ISO 42001" alone on the second line; with it the two lines
       come out even and the wrap reads as a decision.

       Two up at those widths was measured and rejected: it is 576px against
       518px for the cramped four up, so the tidier option is the taller one
       and height is what this whole pass is buying. */
    .tb-holds {
      color: var(--ink-soft);
      font-family: var(--mono);
      font-size: var(--t-micro);
      line-height: 1.55;
      text-wrap: balance;
    }

    /* Two up, then one. The dividers have to follow the wrap: the segment
       that starts each row must lose its left edge, or the rail grows a rule
       hanging off its left side. */
    @media (max-width: 1180px) {
      .tb-rail {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: clamp(1.4rem, 3vh, 2rem) 0;
      }

      .tb-seg:nth-child(odd) { border-left: 0; }
    }

    @media (max-width: 520px) {
      .tb-rail { grid-template-columns: minmax(0, 1fr); }
      .tb-seg { border-left: 0; }
    }

    /* ══ THE SECTION 5 CALL TO ACTION ═════════════════════════════════════
       Added 2026-08-26. Can pointed at the empty ground under the price
       plaque: the reader has just been told what it costs and there was
       nothing to press. */
    .nx-cta {
      display: grid;
      justify-items: center;
      margin-top: clamp(2.2rem, 5vh, 3.2rem);
      text-align: center;
    }

    /* == SECTION 5C, THE FOUNDER =========================================
       Added 2026-08-26 on Can's brief. One photograph, one paragraph, and the
       same band padding as its neighbours so it reads as part of the run
       rather than an About page bolted on the end.

       Left aligned rather than centred, on doctrine and not taste: the
       centred modifier is for sections that are a title with copy under it.
       This one is composed against the photograph, which is the same reason
       the hero and the four beat flow set left.

       The portrait column is capped in px rather than set as a fraction. At
       1560px of shell a 0.35fr column comes out 546px wide, which at 5 / 7 is
       764px tall, and a quiet aside becomes the largest face on the page. */
    .draft-section.fd-section {
      min-height: 0;
      padding-block: var(--band-pad);
      scroll-snap-align: none;
    }

    /* Face left, copy right, and the pair narrowed and centred in the page,
       Can's call 2026-08-26 after seeing it both ways. This is the one band
       that does not run the full shell. The shell is 1560px and the founder
       is an aside, not an argument: at full width the paragraph stretched
       past a comfortable measure and the face drifted away from it.

       The order is carried by the markup rather than by grid placement, so
       the reading order and the painted order agree, and the stack below 860
       comes out face first. */
    .fd-inner {
      display: grid;
      grid-template-columns: clamp(210px, 26vw, 360px) minmax(0, 1fr);
      align-items: center;
      gap: clamp(1.8rem, 5vw, 4.5rem);
      max-width: 62rem;
      margin-inline: auto;
    }

    .fd-portrait { margin: 0; }

    /* cover against a fixed 5 / 7 box, not the file's own ratio, so swapping
       the photograph is a file drop and never a CSS edit. The current shot is
       1064 x 1478, which is 0.720 against the frame's 0.714, so today it
       crops by well under a percent. */
    /* height: auto is load bearing. The img carries width and height
       attributes, and a presentational height of 1671px plus a CSS width of
       100% makes both dimensions definite, at which point aspect-ratio is
       ignored outright and the portrait renders at its natural height. It
       shipped 1671px tall in the first pass and qa/site-founder-capture.mjs
       caught it. Do not drop this line to "tidy up". */
    .fd-portrait img {
      display: block;
      width: 100%;
      height: auto;
      aspect-ratio: 5 / 7;
      border: 1px solid var(--hairline-paper);
      border-radius: 22px;
      box-shadow: var(--draft-shadow-rest);
      object-fit: cover;
      object-position: 50% 35%;
    }

    /* No caption under the portrait since 2026-08-27, Can's call: the eyebrow
       says "The founder" and the copy says his name in its first word, so a
       third "Can, founder" under the face was saying it a third time. */

    .fd-copy .eyebrow { margin-bottom: 1rem; }

    /* The name in its own face, Can's call 2026-08-26. The band is the one
       place on the page that says "Aita" inside a running sentence rather
       than as a mark, and in the sans it read as an ordinary word.

       The size step up is not decoration. The Didone sets on a smaller x
       height than Satoshi, so at the same font-size it reads about a step
       small next to the words either side of it. font-synthesis and
       font-kerning are off for the same reason .aita-wordmark turns them off:
       the face is doing its own work and the browser must not help. */
    .fd-copy .aita-word {
      font-family: "Aita", "Gambetta", Didot, Georgia, serif;
      font-size: 1.22em;
      font-synthesis: none;
      font-kerning: none;
      letter-spacing: 0;
    }

    /* The security band's accent word never had this rule and had been
       rendering as a browser italic since it shipped, against the founder
       rule of 2026-08-18: colour or italic, never both, and a marked word is
       colour. Fixed here rather than in a second place. */
    .fd-section .h2 em,
    .tb-section .h2 em {
      color: var(--brass);
      font-style: normal;
    }

    .fd-body {
      max-width: 54ch;
      margin-top: clamp(1.2rem, 2.4vh, 1.8rem);
    }

    /* The turn, from the man to the product. Held apart from the paragraph
       above and set in ink rather than ink-soft, because it is the claim the
       band exists to make and not the last sentence of a biography.

       Was .fd-teach and carried the one to one teaching offer until
       2026-08-26. That offer moved out: the close's second door already makes
       it a few hundred pixels below, and saying it twice weakened both. */
    .fd-turn {
      max-width: 54ch;
      margin-top: 1.1rem;
      color: var(--ink);
    }

    @media (max-width: 860px) {
      .fd-inner {
        grid-template-columns: minmax(0, 1fr);
        gap: clamp(1.6rem, 4vh, 2.4rem);
      }

      /* Under the copy once stacked, and held to a width that keeps it a
         portrait rather than a banner. */
      .fd-portrait { max-width: 300px; }
    }

    /* == THE FOOTER ======================================================
       Added 2026-08-26. Cut to one line the same day: Can read the first
       version as thick and asked whether the page needed a footer at all.

       What went, and why, so none of it gets helpfully restored:
         Privacy and Terms   pointed at pages that do not exist. A link to
                             nowhere is worse than no link.
         Back to top         the floating pill's wordmark already goes home,
                             and it is on screen at the moment you would want
                             it. This was a second control for one job.
         the wordmark        a third Aita on the same screen as the pill's.

       What is left is exactly what Can named: the rights line and a mailbox.
       One row, mono, at micro, on nothing: the hairline over it went on
       2026-09-08, Can's call ("remove that line, just put the text on the
       footer"). It sets its own padding rather than --band-pad, which at the
       doubled value would have left most of a screen of empty paper under
       the close. */
    .site-footer {
      padding-block: clamp(0.9rem, 1.7vh, 1.3rem);
      /* No ground of its own, Can 2026-09-08: it wore paper and read as a
         grey band under the page field, which clears every section ground
         but skips a strip this short. The field, or the body, is the ground. */
      background: transparent;
    }

    .ft-inner {
      display: flex;
      flex-wrap: wrap;
      align-items: baseline;
      justify-content: space-between;
      gap: 0.4rem 1.5rem;
    }

    .ft-legal,
    .ft-mail,
    .ft-link {
      color: var(--clay);
      font-family: var(--mono);
      font-size: var(--t-micro);
      letter-spacing: 0.04em;
    }

    .ft-mail,
    .ft-link {
      text-decoration: none;
      transition: color 300ms var(--ease);
    }

    .ft-mail:hover,
    .ft-link:hover { color: var(--brass); }

    .ft-end {
      display: flex;
      flex-wrap: wrap;
      align-items: baseline;
      gap: 0.4rem 1.5rem;
    }

    /* ========================================================
       THE PAGE FIELD, AND THE TEAM CARD
       Can's ruling, 2026-09-02: the site opens looking like the
       "can 3" preset in lab/section-motion/v2.html. assets/motion.js
       owns the behaviour; these are the rules it drives. Nothing
       here applies until that module stamps the root, so with the
       module blocked the page is exactly what it was.
       ======================================================== */

    /* One field, fixed behind the page at a negative depth. That only holds
       while the grounds in front of it are off: a body background paints
       AFTER negative layers, so leave it on and the field is buried. */
    html[data-page-field="1"],
    html[data-page-field="1"] body { background: transparent; }

    /* Scroll snap fights a scrubbed expand. The page keeps jumping to the
       nearest section edge and the middle of the team's move is never seen. */
    html[data-page-field="1"] { scroll-snap-type: none; }

    .ml-page {
      position: fixed;
      inset: 0;
      /* The LARGE viewport, not the current one. On a phone the browser's
         bars retract as the page scrolls and inset:0 alone made the host,
         and so the canvas, grow with every scroll: the field cleared to
         black and drew again, which Can saw as the ground going dark and
         filling back between the hero and section two, 2026-09-03. Held at
         lvh the canvas never resizes for the bars; the extra strip sits
         behind them until they go. */
      height: 100vh;
      height: 100lvh;
      z-index: -1;
      overflow: hidden;
      pointer-events: none;
    }

    .ml-page canvas { display: block; width: 100%; height: 100%; }

    /* The site paints a ground on the sections themselves and on a few full
       bleed wrappers inside them. Leave either in place and everything behind
       it is invisible, which is what "the field does nothing" looks like. */
    [data-ml-cleared="1"] { background: transparent !important; }

    .ml-wrap {
      position: relative;
      --ml-ground: var(--bone);
    }
    /* On dark the box wears paper, a step darker than the bone card inside
       it, so the card sits on the section by tone. Can's ruling of
       2026-09-06 after two cuts of a blurred shadow: black rings on a ground
       this dark, copper looked like a glow. A flat darker ground has no
       gradient to band. Light stays bone: paper is lighter there and the
       card would sink. */
    :root[data-theme="dark"] .ml-wrap { --ml-ground: var(--paper); }

    .ml-box {
      position: relative;
      width: calc(100% - var(--ml-inset, 0px) * 2);
      margin-inline: auto;
      border-radius: var(--ml-radius, 0px);
      /* clip, not hidden: hidden makes the box a scroll container, and a
         sticky element inside a scroll container sticks to that box rather
         than to the viewport. The deck's stage (section 3B) sticks. clip
         rounds the corners just the same and scrolls nothing. */
      overflow: clip;
      isolation: isolate;
      background: var(--ml-ground, transparent);
      will-change: width;
      /* THE EDGE. Without it the move is invisible whenever the card's ground
         sits close to the page's, which on this site is most of the time. The
         hairline and the lift give the card an edge whatever the grounds are,
         and both fade to nothing as it reaches full bleed so no stray line is
         left across the page. Copper, the same channels as --brass-hair. */
      box-shadow:
        0 0 0 1px rgba(184, 115, 51, calc(0.42 * var(--ml-edge, 0))),
        0 24px 70px -30px rgba(0, 0, 0, calc(0.9 * var(--ml-edge, 0)));
    }

    /* ========================================================
       THE SHEET, the intake over the page
       Can's ruling 2026-09-02: every door opens the intake as a sheet
       over the page it was clicked on instead of leaving for start.html.
       The links keep their href, so without script the page is still the
       way in. assets/intake/sheet.js owns the behaviour.
       ======================================================== */
    .intake-sheet {
      position: fixed;
      inset: 0;
      z-index: 90;
      display: grid;
      place-items: center;
      padding: clamp(12px, 3vh, 40px) var(--gutter);
    }

    .intake-sheet[hidden] { display: none; }

    .intake-sheet__scrim {
      position: absolute;
      inset: 0;
      background: rgba(0, 0, 0, 0.55);
      -webkit-backdrop-filter: blur(6px);
      backdrop-filter: blur(6px);
      opacity: 0;
      transition: opacity 380ms var(--ease);
    }

    .intake-sheet__panel {
      position: relative;
      width: min(100%, 1180px);
      height: min(100%, 860px);
      overflow: hidden;
      border: 1px solid var(--hairline-paper);
      border-radius: 20px;
      background: var(--paper);
      box-shadow: 0 1px 1px var(--hairline-paper), 0 40px 90px -40px rgba(0, 0, 0, 0.6);
      opacity: 0;
      transform: translateY(32px);
      transition: transform 460ms var(--ease), opacity 380ms var(--ease);
      outline: none;
    }

    .intake-sheet__host { height: 100%; }

    .intake-sheet[data-open="1"] .intake-sheet__scrim { opacity: 1; }
    .intake-sheet[data-open="1"] .intake-sheet__panel { opacity: 1; transform: none; }

    /* The page holds still under the sheet. The gutter stays reserved so the
       bar does not jump when the scrollbar goes. */
    html[data-sheet="open"] { overflow: hidden; scrollbar-gutter: stable; }

    html[data-reduced-motion="true"] .intake-sheet__scrim,
    html[data-reduced-motion="true"] .intake-sheet__panel { transition: none; }

    @media (max-width: 720px) {
      .intake-sheet { padding: 0; }
      .intake-sheet__panel { width: 100%; height: 100%; border: 0; border-radius: 0; }
    }

    /* ========================================================
       THE THEME SWITCH
       Can's ask 2026-09-02: a small one, top right. It sits at the bar's
       height and the page gutter, and moves the bar over by its own
       footprint where the two would otherwise meet. Script only: without
       it there is nothing to switch.
       ======================================================== */
    .theme-toggle {
      position: fixed;
      z-index: 61;
      top: calc(clamp(12px, 1.8vh, 22px) + 6px);
      right: var(--gutter);
      display: grid;
      place-items: center;
      width: 40px;
      height: 40px;
      padding: 0;
      /* The glyph alone, Can's call the same day: no disc, no rim. The 40px
         box stays as the thing to hit. */
      border: 0;
      border-radius: 999px;
      background: none;
      color: var(--ink);
      cursor: pointer;
      transition: color 200ms var(--ease);
    }

    .theme-toggle:hover { color: var(--brass); }
    .theme-toggle:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }
    .theme-toggle svg { display: block; width: 18px; height: 18px; }
    /* One glyph at a time: the sun on the dark page, the moon on the light.
       Both rules carry the parent so they outrank the line above. */
    :is(.theme-toggle, .site-menu__theme) .theme-toggle__moon { display: none; }
    :root:not([data-theme="dark"]) :is(.theme-toggle, .site-menu__theme) .theme-toggle__sun { display: none; }
    :root:not([data-theme="dark"]) :is(.theme-toggle, .site-menu__theme) .theme-toggle__moon { display: block; }
    html.no-js .theme-toggle { display: none; }

    @media (max-width: 1240px) {
      .site-nav {
        --nav-shift: 28px;
        width: min(100% - (2 * var(--gutter)) - 56px, 1080px);
      }
    }

    /* ── The phone header ─────────────────────────────────────────────
       Under 900 the loose icon goes, the bar takes the full gutter width
       and sits dead centre, and a hamburger in the bar opens the panel.
       Can's ruling 2026-09-03. This block sits after the 1240 rule on
       purpose: it has to win the width and the shift on source order. */
    .site-nav__menu { display: none; }

    @media (max-width: 900px) {
      .site-nav {
        --nav-shift: 0px;
        width: min(100% - (2 * var(--gutter)), 1080px);
      }

      .theme-toggle { display: none; }

      .site-nav__menu {
        display: grid;
        place-content: center;
        gap: 4px;
        width: 40px;
        height: 40px;
        padding: 0;
        border: 0;
        border-radius: 999px;
        background: none;
        color: var(--ink);
        cursor: pointer;
      }

      .site-nav__menu span {
        display: block;
        width: 18px;
        height: 1.5px;
        background: currentColor;
        transition: translate 240ms var(--ease), rotate 240ms var(--ease), opacity 160ms var(--ease);
      }

      .site-nav__menu[aria-expanded="true"] span:nth-child(1) { translate: 0 5.5px; rotate: 45deg; }
      .site-nav__menu[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
      .site-nav__menu[aria-expanded="true"] span:nth-child(3) { translate: 0 -5.5px; rotate: -45deg; }

      .site-menu {
        position: fixed;
        z-index: 59;
        top: calc(clamp(12px, 1.8vh, 22px) + 68px);
        left: 50%;
        width: min(100% - (2 * var(--gutter)), 1080px);
        padding: 6px 10px 12px;
        border: 1px solid var(--hairline-paper);
        border-radius: 28px;
        background: color-mix(in srgb, var(--paper) 90%, transparent);
        backdrop-filter: blur(14px);
        -webkit-backdrop-filter: blur(14px);
        box-shadow: 0 24px 60px -30px rgba(0, 0, 0, 0.6);
        translate: -50% 0;
      }

      .site-menu[hidden] { display: none; }

      .site-menu__link {
        display: block;
        padding: 15px 14px;
        border-bottom: 1px solid var(--hairline-paper);
        color: var(--ink);
        font-family: var(--display);
        font-size: 1.45rem;
        font-weight: 300;
        letter-spacing: -0.01em;
        text-decoration: none;
      }

      .site-menu__foot {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        padding: 12px 4px 0;
      }

      /* The glyph, not the words. Can, 2026-09-04: "instead of writing
         light theme in here, just put the icon, sun and the moon". Same
         two glyphs as the desktop switch, one showing at a time by the
         rules above, in a 44px box so it is a thumb target. */
      .site-menu__theme {
        display: grid;
        place-items: center;
        width: 44px;
        height: 44px;
        padding: 0;
        border: 0;
        border-radius: 999px;
        background: none;
        color: var(--ink);
        cursor: pointer;
      }
      .site-menu__theme svg { display: block; width: 20px; height: 20px; }

      .site-menu__theme:hover { color: var(--brass); }
    }

    /* Desktop never shows the panel, whatever state a resize left it in. */
    @media (min-width: 901px) {
      .site-menu { display: none; }
    }

  