/* ==================================================================
   BORING PARTS  /  site v3  /  "THE SWITCH"
   A plain page with one thing on it that matters.
   Everything square to an 8px grid. No spec-sheet chrome.

   THE ONE STATE THIS SHEET CARES ABOUT
   ------------------------------------
   `html.lights-dark`   the switch has not been thrown. The page ground
                        is Ink, the hero copy is Spec Paper on it, and
                        the masthead reverses. Set before first paint by
                        the inline script in <head>, removed on the
                        frame the light wash reaches the copy column.
   `html.lights-on`     the switch HAS been thrown. Set on the seat
                        frame, one frame, no fade. The only thing that
                        reads it is the masthead dot, Steel until then.

   The two are separate on purpose: `lights-on` is the state machine's
   truth and flips at the seat, `lights-dark` is the paint and flips
   later, when the light actually arrives at the words.

   Everything BELOW the hero is light mode always. The dark rules are
   scoped to the ground, the masthead, the preloader and the hero, so
   throwing the switch back off cannot drag the rest of the page with
   it.
   ================================================================== */

/* ---------- 1. TOKENS ---------------------------------------- */
:root {
  --paper:    #F4F2ED;
  --paper-2:  #EDEAE2;   /* the second block field */
  --ink:      #14181A;
  --graphite: #1C1F22;
  --orange:   #FF5C1A;   /* surface + mark only, never text on paper */
  --torque:   #BF3C0A;   /* orange TEXT on light grounds */
  --steel:    #5F666D;
  --ash:      #8A9199;
  --zinc:     #CFCBC3;

  /* the ink side of the page: rules and secondary type on Ink */
  --ink-rule: #2A3033;
  --ink-2:    #9AA3AB;   /* secondary copy on Ink, 7.0 on #14181A */

  --u: 8px;
  --wrap: min(1240px, 100% - 48px);

  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  /* CSS mirrors of the brand curves. JS owns the canonical versions. */
  --e-seat:   cubic-bezier(0.2, 0.9, 0.1, 1);
  --e-reveal: cubic-bezier(0.2, 0.9, 0.1, 1);
  --e-drift:  cubic-bezier(0.4, 0.02, 0.2, 1);
  /* the indexed torque curve. Only for a bounded rotation or a cut. */
  --e-detent: cubic-bezier(0.25, 0, 0.15, 1);
  /* the seat, WITH its overshoot. `--e-seat` above is the flat mirror
     the older UI states use; the preloader is the one place in CSS
     that needs the real curve, because a part seating without its
     single overshoot is not this brand's seat. */
  --e-seatover: cubic-bezier(0.3, 1.4, 0.5, 1);

  --mast-h: 72px;

  /* the preloader's master timeline. It lives here rather than on
     `.pre` because the masthead mark's own hold has to agree with it,
     and neither may drift from the other. */
  --pre-T: 0.57s;
}

/* ---------- 2. RESET ----------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body, h1, h2, h3, h4, p, dl, dd, ol, ul, figure { margin: 0; padding: 0; }
ol, ul { list-style: none; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
a { color: inherit; }

html { scroll-behavior: auto; }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

body {
  position: relative;
  min-height: 100vh;
  overflow-x: hidden;
  /* #ground paints the page. The body colour only matters for overscroll
     and for the no-JS build, so it follows the same switch. */
  background: var(--paper);
  color: var(--graphite);
  font-family: var(--sans);
  font-variation-settings: "wdth" 100, "wght" 400;
  font-size: 17px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

html.lights-dark body { background: var(--ink); }

/* keep the page from jumping before the preloader lifts */
html.js:not(.nopre):not(.ready) body { overflow: hidden; height: 100vh; }

/* ---------- 2b. THE GROUND, AND THE LIGHT CROSSING IT ---------
   Two fixed layers under everything, including under the WebGL canvas,
   so the plate stays lit and visible the whole way through the wash and
   the words never flicker.

     #ground   the page's own colour. Ink while the switch is off.
     #wash     the light itself: a layer in the DESTINATION colour,
               clipped to a circle that grows out of the switch's own
               screen position over 650ms on `reveal`. Hidden, and
               clipped to nothing, whenever a wash is not running.

   js/switch.js writes clip-path, background and visibility on #wash and
   background on #ground. Everything here is the resting state.
   --------------------------------------------------------------- */
.ground {
  position: fixed; inset: 0; z-index: -2;
  background: var(--paper); pointer-events: none;
}
html.lights-dark .ground { background: var(--ink); }

.wash {
  position: fixed; inset: 0; z-index: -1;
  background: var(--paper); pointer-events: none;
  visibility: hidden;
  clip-path: circle(0px at 50% 50%);
  /* the wash is scrubbed frame by frame from JS: no CSS transition may
     ever smooth it, or the front stops being a real position */
  transition: none;
}
html.rm .wash { display: none; }

.skip {
  position: fixed; top: 8px; left: 8px; z-index: 500;
  padding: 12px 16px; background: var(--graphite); color: var(--paper);
  font-family: var(--mono); font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
  text-decoration: none; transform: translateY(-200%);
}
.skip:focus { transform: none; }

:focus-visible { outline: 2px solid var(--torque); outline-offset: 3px; }

/* ---------- 3. TYPE ------------------------------------------ */
.mono {
  font-family: var(--mono); font-weight: 500;
  font-size: 11.5px; letter-spacing: .10em; text-transform: uppercase;
  color: var(--steel); line-height: 1.4;
}
.tnum { font-variant-numeric: tabular-nums; }

.h2 {
  font-size: clamp(2rem, 3.6vw, 3.5rem);
  line-height: 1.02; letter-spacing: -0.025em;
  font-variation-settings: "wdth" 104, "wght" 700;
  text-wrap: balance;
}
.h4 {
  font-size: 1.0625rem; line-height: 1.3; letter-spacing: -0.01em;
  font-variation-settings: "wdth" 100, "wght" 600;
  margin-bottom: 16px;
}
.lede {
  font-size: clamp(1.0625rem, 1.4vw, 1.375rem);
  line-height: 1.42; letter-spacing: -0.012em;
  max-width: 46ch;
}
.small { font-size: 13.5px; line-height: 1.55; }

.wrap { width: var(--wrap); margin-inline: auto; }

/* section index, e.g.  01 ....... The desk */
.chap__index { display: flex; align-items: center; gap: 12px; }
.chap__index .rule { display: block; width: 48px; height: 1px; background: var(--zinc); }
.chap__index--rev { color: var(--ash); }
.chap__index--rev .rule { background: #2A3033; }

/* the shelf label: a museum caption on an ordinary thing */
.shelf { position: absolute; padding-top: 10px; }
.shelf::before {
  content: ""; position: absolute; top: 0; left: 0;
  width: 84px; height: 1px; background: var(--zinc);
}
.shelf--bl { left: 24px; bottom: 44px; }

/* ---------- 4. THE MARK -------------------------------------- */
.dotmark { width: 100%; height: 100%; }
.dotmark__b path { fill: var(--graphite); }
.mast.is-rev .dotmark__b path,
html.lights-dark .mast .dotmark__b path { fill: var(--paper); }

/* THE MASTHEAD DOT IS ALWAYS ON.
   Non-negotiable 0a: the O is a status dot that is always on. It used
   to be Steel until `lights-on` arrived, which meant the Steel to
   Safety Orange snap - the best moment on the site - played TWICE:
   once inside the preloader, then the dot was quietly reset to Steel
   and it snapped again on the switch throw. The preloader now hands
   the masthead a dot that is already orange and it never goes back.
   The throw's job is purely "the room comes on", which is what it
   should always have been.
   No transition is declared on `fill` anywhere, so nothing about the
   mark can ever cross-fade. */
.mast__mark .dotmark__dot { fill: var(--orange); }

/* No preloader on this visit - a phone, a repeat visit in this
   session, or reduced motion - so the masthead mark plays its own
   arrival instead. It is CSS, so it runs on the first paint with no
   dependency on anything loading, and it carries no colour change at
   all: the dot is already on. */
html.nopre:not(.rm) .mast__mark .dotmark__wipe {
  transform-box: view-box; transform-origin: 0 50%;
  animation: markWipe .20s var(--e-reveal) .12s both;
}
html.nopre:not(.rm) .mast__mark .dotmark__dotg {
  animation: markDot .28s var(--e-reveal) .28s both;
}
@keyframes markWipe { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* THE MASTHEAD MARK WAITS FOR ITS OWN ARRIVAL.
   The preloader's mark docks into this slot, so while that is in
   flight the slot has to be empty: the sheet clears at 1.94s and
   would otherwise expose a second, static mark 0.3s before the real
   one lands on top of it. It is held on the preloader's own clock, in
   CSS, so it needs nothing to have loaded - and it is revealed on the
   exact frame the dock seats, under the docked mark, which is why the
   handover is invisible. Not applied when there is no preloader to
   wait for (`nopre`: a phone, a repeat visit, reduced motion, or no
   script at all). */
html.js:not(.nopre) .mast__mark .dotmark {
  animation: mastHold var(--pre-T) steps(1, end) both;
}
@keyframes mastHold {
  0%, 93.333%   { visibility: hidden; }
  93.334%, 100% { visibility: visible; }
}
@keyframes markDot {
  from { transform: translate(70.73px, 50px) translateX(-84px); }
  to   { transform: translate(70.73px, 50px); }
}

/* ---------- 5. PRELOADER  /  C1 "THE BORE" + C3's DOCK -------
   Inline SVG and CSS keyframes: 0 KB, no network request, no font
   dependency, no JS dependency, first paint. The old build animated
   the mark through GSAP, so it could not start until GSAP had
   arrived - exactly when the connection is slow and a preloader
   matters most.

   ONE master timeline, `--T`. Every beat is a percentage of it, so
   the whole sequence can be frozen on an exact frame by pausing the
   animations and seeking, which is how the verification driver
   captures the bore.

     RETIMED. The client's note was "can we slow down the preloader a
     little bit, it's too fast", and the answer is not a uniform 1.25x:
     what was being lost is that the four OPERATIONS - marking out,
     cutting, clearing and seating - ran into each other, so the
     sequence read as one event rather than as a part being made. So
     the gaps BETWEEN the operations carry some of the extra time, and
     THE BORE ITSELF carries most of it: it is the beat the whole idea
     rests on and it had 160ms, which is less than the seat. It now has
     300ms. The snap is untouched - it is one frame and it stays one
     frame - and the dock is untouched in feel.

     0.06 - 0.34   the B sets in, wiped along the grid axis     (0.28)
     0.44 - 0.72   MARKING OUT: a Steel ring scribes itself     (0.28)
     0.78 - 0.86   the construction line goes: you cut a marked hole
     0.86 - 1.16   THE BORE, on `detent`, a cutting motion      (0.30)
     0.92 - 1.52   the waste clears as ONE slug                 (0.60)
     1.58 - 1.92   the part travels in, Steel                   (0.34)
     1.92          SNAP: Safety Orange in one frame; the bored rim goes
     1.92 - 2.02   the seat, one overshoot, dead stop           (0.10)
     2.02 - 2.20   hold
     2.20 - 2.80   THE DOCK, on `drift`: a framing move, not an arrival
     2.44 - 2.90   the sheet clears BENEATH the mark, which survives it
                                                        total 3.00s
   ------------------------------------------------------------- */
.pre {
  position: fixed; inset: 0; z-index: 200;
  pointer-events: none;

  --T: var(--pre-T);
  --pre-mark: clamp(120px, 16vw, 190px);

  /* The dock target, MEASURED at runtime from the live masthead mark
     by the inline script under <header> in index.html. These are only
     the fallbacks, for the case where that never runs. */
  --dock-x: calc(37px - 50vw);
  --dock-y: calc(var(--mast-h) / 2 - 50vh);
  --dock-s: 0.1368;
}
html:not(.js) .pre, html.nopre .pre { display: none; }

/* The sheet is Ink - the same value the hero's off room solves to -
   so when it clears there is no seam and no value step. */
.pre__sheet {
  position: absolute; inset: 0; background: var(--ink);
  animation: preSheet var(--T) var(--e-reveal) both;
}
@keyframes preSheet {
  0%, 81.333%   { clip-path: inset(0 0 0 0); }
  96.667%, 100% { clip-path: inset(0 0 0 100%); }
}

.pre__label {
  position: absolute; left: 0; right: 0;
  top: calc(50% + var(--pre-mark) / 2 + 20px);
  text-align: center; color: var(--ash); opacity: 0;
  animation: preLabel var(--T) var(--e-reveal) both;
}
@keyframes preLabel {
  0%, 36.667%   { opacity: 0; }
  46.667%, 66.667% { opacity: 1; }
  73.333%, 100% { opacity: 0; }
}

/* THE DOCK. The lockup lives above the sheet and travels onto the
   masthead's own mark while the sheet clears under it. `drift`,
   because this is a framing move and not a part arriving. */
.pre__dock {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  animation: preDock var(--T) var(--e-drift) both;
}
@keyframes preDock {
  0%, 73.333%   { transform: none; }
  93.333%, 100% { transform: translate(var(--dock-x), var(--dock-y)) scale(var(--dock-s)); }
}

.pre__mark {
  width: var(--pre-mark); height: var(--pre-mark);
  display: block; overflow: visible;
}
.pre__mark .dotmark__b path { fill: var(--paper); }

/* 1. the B sets in place, wiped along the grid axis */
.pre__mark .dotmark__wipe {
  transform-box: view-box; transform-origin: 0 50%;
  animation: preB var(--T) var(--e-reveal) both;
}
@keyframes preB {
  0%, 2%         { transform: scaleX(0); }
  11.333%, 100%  { transform: scaleX(1); }
}

/* 2. MARKING OUT. A scribed ring: you mark the hole before you cut it,
      and the construction line is gone by the time the cut is made. */
.pre__scribe {
  stroke-dasharray: 83;
  animation: preScribe var(--T) var(--e-reveal) both;
}
@keyframes preScribe {
  0%, 14.667%   { stroke-dashoffset: 83; opacity: 1; }
  24%, 26%      { stroke-dashoffset: 0;  opacity: 1; }
  28.667%, 100% { stroke-dashoffset: 0;  opacity: 0; }
}

/* 3. THE BORE. The hole opens through the sheet on the torque curve,
      because this is a cutting motion and not a shape appearing. */
.pre__hole {
  transform-box: view-box; transform-origin: 69.53px 50px;
  animation: preHole var(--T) var(--e-detent) both;
}
@keyframes preHole {
  0%, 28.667%   { transform: scale(0); }
  38.667%, 100% { transform: scale(1); }
}

/* the bored edge. A hole has a rim; a dot does not. It goes on the
   frame the part seats, because the part is filling it now - and it
   goes in ONE frame, on steps(), like every other state in the mark. */
.pre__rim {
  transform-box: view-box; transform-origin: 69.53px 50px;
  animation: preHole var(--T) var(--e-detent) both,
             preRimOff var(--T) steps(1, end) both;
}
@keyframes preRimOff {
  0%, 63.999%   { opacity: 1; }
  64%, 100%     { opacity: 0; }
}

/* 4. THE WASTE CLEARS. One slug of removed material: it breaks free,
      tips away on the axis the part will arrive on, and goes edge-on.
      It LEAVES rather than fading - nothing in this build fades out -
      and it is one object on an axis, never chips and never a particle
      system, which the design system bans outright. */
.pre__slug {
  transform-box: view-box; transform-origin: 69.53px 50px;
  animation: preSlug var(--T) var(--e-reveal) both,
             preSlugShow var(--T) steps(1, end) both;
}
@keyframes preSlug {
  0%, 30.667%   { transform: translate(0, 0) rotate(0deg) scaleY(1); }
  34.667%       { transform: translate(1.4px, -5px) rotate(-3deg) scaleY(1); }
  46.667%       { transform: translate(34px, 44px) rotate(24deg) scaleY(0.28); }
  50.667%, 100% { transform: translate(52px, 74px) rotate(34deg) scaleY(0); }
}
@keyframes preSlugShow {
  0%, 30.666%      { opacity: 0; }
  30.667%, 50.666% { opacity: 1; }
  50.667%, 100%    { opacity: 0; }
}

/* 5. the part arrives and seats into the hole that was just made */
.pre__dotg {
  animation: preDot var(--T) var(--e-reveal) both,
             preDotShow var(--T) steps(1, end) both;
}
@keyframes preDot {
  0%, 52.667%   { transform: translateX(-150px); }
  64%           { transform: translateX(-1.2px);
                  animation-timing-function: var(--e-seatover); }
  67.333%, 100% { transform: translateX(0); }
}
@keyframes preDotShow {
  0%, 52.666%   { opacity: 0; }
  52.667%, 100% { opacity: 1; }
}

/* 6. Steel to Safety Orange in ONE FRAME, on the seat. steps(1, end)
      makes a fade structurally impossible rather than a value that can
      drift - and this is the only time the snap plays. */
.pre__mark .dotmark__dot {
  animation: preDotColour var(--T) steps(1, end) both;
}
@keyframes preDotColour {
  0%, 63.999%   { fill: var(--steel); }
  64%, 100%     { fill: var(--orange); }
}

/* ---------- 6. GRAIN + NIGHT + CURSOR ------------------------ */
.grain {
  position: fixed; top: -50%; left: -50%; width: 200%; height: 200%;
  z-index: 90; pointer-events: none; opacity: .05;
  background-repeat: repeat; mix-blend-mode: multiply;
  will-change: transform;
}
html.rm .grain { animation: none; }

/* the quiet shift: sits above the desk, below every word on the page */
.night {
  position: fixed; inset: 0; z-index: 1;
  background: #0E1620; opacity: 0; pointer-events: none;
}
html.rm .night { display: none; }

.cursor {
  position: fixed; top: 0; left: 0; z-index: 100;
  pointer-events: none; opacity: 0;
}
html.coarse .cursor, html.rm .cursor { display: none; }
.cursor__dot {
  position: absolute; top: 0; left: 0; width: 6px; height: 6px;
  margin: -3px 0 0 -3px; background: var(--graphite); border-radius: 50%;
}
.cursor__ring {
  position: absolute; top: 0; left: 0; width: 34px; height: 34px;
  margin: -17px 0 0 -17px; border: 1px solid var(--orange); border-radius: 50%;
  display: grid; place-items: center;
  transition: width .28s var(--e-seat), height .28s var(--e-seat), margin .28s var(--e-seat), background-color .28s var(--e-seat);
}
.cursor__label {
  font-family: var(--mono); font-size: 9.5px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--graphite);
  opacity: 0; transition: opacity .18s linear; white-space: nowrap;
}
.cursor.is-link .cursor__ring, .cursor.is-drag .cursor__ring {
  width: 72px; height: 72px; margin: -36px 0 0 -36px; background: var(--orange);
}
.cursor.is-link .cursor__label, .cursor.is-drag .cursor__label { opacity: 1; }
.cursor.is-link .cursor__dot, .cursor.is-drag .cursor__dot { opacity: 0; }

/* ---------- 7. MASTHEAD -------------------------------------- */
.mast {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  height: var(--mast-h);
  display: flex; align-items: center; gap: 20px;
  padding: 0 24px;
  background: rgba(244, 242, 237, 0);
  border-bottom: 1px solid transparent;
  transition: background-color .3s var(--e-reveal), border-color .3s var(--e-reveal), color .3s var(--e-reveal);
}
.mast.is-set { background: var(--paper); border-bottom-color: var(--zinc); }
/* With the script off nothing ever sets `is-set`, so the bar stayed
   transparent over the whole page and the headline ran through it.
   That mattered less when the bar held only navigation; it holds the
   page's CTA now, so the no-script build gets the ground outright. */
html:not(.js) .mast { background: var(--paper); border-bottom-color: var(--zinc); }
.mast.is-rev { background: var(--ink); border-bottom-color: var(--ink-rule); color: var(--paper); }
.mast.is-rev .mast__name { color: var(--paper); }
.mast.is-rev .mast__nav a:hover { color: var(--paper); }
.mast.is-rev .mast__sound .mono { color: var(--ash); }
/* over the quiet shift the masthead reverses too, so the mark stays visible */
.mast.is-night { background: rgba(14, 22, 32, .72); border-bottom-color: var(--ink-rule); color: var(--paper); }
.mast.is-night .mast__name { color: var(--paper); }
.mast.is-night .dotmark__b path { fill: var(--paper); }
.mast.is-night .mast__nav a, .mast.is-night .mast__sound .mono { color: #A9B2BB; }

/* the masthead over the dark hero: reversed, and transparent, because
   the ground behind it is already Ink */
html.lights-dark .mast { color: var(--paper); }
html.lights-dark .mast__name { color: var(--paper); }
html.lights-dark .mast__nav a { color: var(--ink-2); }
html.lights-dark .mast__nav a i { color: #4A545C; }
html.lights-dark .mast__nav a:hover { color: var(--paper); }
html.lights-dark .mast__nav a:hover i { color: var(--orange); }
html.lights-dark .mast__sound .mono { color: var(--ink-2); }
html.lights-dark .mast__soundbars i { background: #4A545C; }
html.lights-dark .mast.is-set { background: var(--ink); border-bottom-color: var(--ink-rule); }

.mast__mark { display: flex; align-items: center; gap: 10px; text-decoration: none; }
.mast__mark .dotmark { width: 26px; height: 26px; }
.mast__name {
  font-size: 14px; letter-spacing: -0.01em;
  font-variation-settings: "wdth" 108, "wght" 700;
}
.mast__nav { display: flex; gap: 4px; margin-left: auto; }
.mast__nav a {
  display: flex; align-items: baseline; gap: 6px;
  padding: 8px 12px; text-decoration: none;
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--steel);
  transition: color .2s var(--e-seat);
}
.mast__nav a i { font-style: normal; color: var(--zinc); }
.mast__nav a:hover { color: var(--graphite); }
.mast__nav a:hover i { color: var(--torque); }

/* THE TUESDAY LIST ENTRY. It is a page, not a chapter of this scroll,
   so it carries no number and a hairline stands in front of it to say
   so. It is Graphite rather than Steel when it is the page you are
   already on, which is what `aria-current` says to everybody else. */
.mast__nav-tl {
  margin-left: 6px; padding-left: 14px;
  border-left: 1px solid var(--zinc);
}
.mast__nav a[aria-current="page"] { color: var(--graphite); }
html.lights-dark .mast__nav-tl { border-left-color: #2A3033; }
.mast.is-rev .mast__nav-tl, .mast.is-night .mast__nav-tl { border-left-color: var(--ink-rule); }

.mast__sound { display: flex; align-items: center; gap: 8px; padding: 8px 10px; flex: none; }
.mast__sound .mono { color: var(--steel); }
.mast__soundbars { display: flex; align-items: flex-end; gap: 2px; height: 10px; }
.mast__soundbars i { display: block; width: 2px; height: 3px; background: var(--zinc); }
.mast__sound[aria-pressed="true"] .mast__soundbars i { background: var(--orange); }
.mast__sound[aria-pressed="true"] .mast__soundbars i:nth-child(2) { height: 10px; }
.mast__sound[aria-pressed="true"] .mast__soundbars i:nth-child(3) { height: 6px; }
.mast__sound[aria-pressed="true"] .mono { color: var(--graphite); }

/* THE MASTHEAD'S WIDTH BUDGET. The bar carries four things: the
   mark, the section nav, the sound toggle and the CTA, which moved
   in here in section 8. The CTA is the one that has to survive at
   every width - it is the page's primary conversion element and it
   has to be reachable at every scroll position - while the nav is a
   convenience for a page that is one continuous scroll and repeats
   every link in the footer. So the nav is what comes out first.

   THE BREAKPOINT MOVED AGAIN, FROM 1099 TO 1249, AND THE REASON IS
   THE TUESDAY LIST. The outreach question carries no URL on purpose,
   because a link in a first message is the strongest spam signal on
   every channel it uses. So the only way a recipient can check the
   promise in that message is to search the company and land on this
   page, which means the list has to be findable FROM here or the
   promise cannot be checked at all. A sixth nav entry is 150px and
   it moves the whole bar's minimum from about 1100 to 1225.

   Measured at 1600 rather than guessed, with the tightened padding
   this file already applies below 1340: 24 + 124 mark + 20 + 720 nav
   + 20 + 110 sound + 20 + 162 CTA + 24 = 1224. The breakpoint sits
   at 1249 so there is real slack in it rather than a pixel.

   What the 150px costs, stated plainly: between 1100 and 1249 the
   section nav is no longer drawn, where it used to be. Every section
   is still reachable by scrolling, every link is repeated in the
   footer nav, and the footer nav carries The Tuesday List too, at
   every width, which is what covers the phone - where this bar has
   never had room for a nav at all. */
@media (max-width: 1249px) { .mast__nav { display: none; } }

/* ---------- 7b. THE MOBILE MENU ------------------------------
   The section nav above is not drawn below 1249px, so until this round
   a phone had no way to jump to the offer or to the form: the only
   route to either was to scroll the whole narrative. This is that
   route.

   IT IS A `<details>`, AND THAT IS THE WHOLE POINT. It opens and
   closes with no JavaScript at all, so on the no-script build - and on
   the build where the modules failed to arrive, which is the one a bad
   connection actually produces - the links inside it, `#enquiry`
   included, work exactly as they do everywhere else. There is no
   script anywhere on this page that touches it. */
.mast__menu { position: relative; flex: none; margin-left: auto; }
@media (min-width: 1250px) { .mast__menu { display: none; } }
.mast__menubtn {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; cursor: pointer; list-style: none;
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--steel);
}
.mast__menubtn::-webkit-details-marker { display: none; }
.mast__menubars { display: flex; flex-direction: column; gap: 4px; width: 17px; flex: none; }
.mast__menubars i { display: block; height: 1px; background: var(--steel); }
.mast__menu[open] > .mast__menubtn { color: var(--graphite); }
.mast__menu[open] > .mast__menubtn .mast__menubars i { background: var(--graphite); }

.mast__menupanel {
  position: absolute; top: calc(100% + 13px); right: 0; z-index: 60;
  display: grid; min-width: 238px;
  background: var(--paper); border: 1px solid var(--zinc);
}
.mast__menupanel a {
  padding: 15px 18px; text-decoration: none;
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  letter-spacing: .07em; text-transform: uppercase; color: var(--graphite);
  border-bottom: 1px solid var(--zinc);
}
.mast__menupanel a:last-child { border-bottom: 0; }
.mast__menupanel a:hover { background: var(--paper-2); }
/* the first entry is the offer, so it carries the section's own pip
   rather than a second orange surface in a bar that already has one */
.mast__menupanel a:first-child { color: var(--torque); }

/* the three reversed grounds the bar already has */
html.lights-dark .mast__menubtn, .mast.is-rev .mast__menubtn, .mast.is-night .mast__menubtn { color: var(--ink-2); }
html.lights-dark .mast__menubars i, .mast.is-rev .mast__menubars i, .mast.is-night .mast__menubars i { background: #4A545C; }
html.lights-dark .mast__menu[open] > .mast__menubtn,
.mast.is-rev .mast__menu[open] > .mast__menubtn,
.mast.is-night .mast__menu[open] > .mast__menubtn { color: var(--paper); }
@media (max-width: 1339px) and (min-width: 1250px) {
  .mast__nav a { padding: 8px 9px; }
}

/* ---------- 8. THE CTA, DOCKED IN THE MASTHEAD ---------------
   It was a pill fixed in the bottom right corner. It crossed live
   copy - most visibly step 04's heading on a phone, once section 04
   became an ink form that owns that corner - and no amount of padding
   inside one section can fix a fixed overlay, because the page keeps
   moving underneath it. So it is docked into the masthead instead.

   Why the masthead and not a full width bottom bar: the masthead is
   the only chrome on this page whose height is already RESERVED.
   Every section pads itself by `--mast-h`, the snap lands each one
   under it, and `checks.ctaClearance` walks every snap point on every
   profile and finds zero overlap with live copy. A bottom bar would
   have moved the same collision to a wider strip and would have had
   to be padded for at the bottom of every section instead.

   Everything else about it is unchanged. It is still a real
   `<a href="mailto:">`, it is still present at every scroll position
   (`.mast` is fixed and is never hidden or translated), and its
   colour states are still written by `createCtaStates` in js/ui.js:
   Safety Orange when it owns the screen's one orange thing, graphite
   with an orange pip when a section owns it instead.

   The class name is historical. It is kept because the verification
   report names the element in `screenOrange`, `stickyCtaInk` and
   `ctaClearance`, and renaming it would silently retire assertions. */
.sticky-cta {
  flex: none;
  display: flex; align-items: center; gap: 9px;
  padding: 10px 16px 10px 13px;
  background: var(--orange); color: var(--graphite);   /* graphite on orange: 5.36 AA */
  text-decoration: none; border-radius: 2px;
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; white-space: nowrap;
  transition: transform .24s var(--e-seat), background-color .3s var(--e-seat), color .3s var(--e-seat);
}
.sticky-cta:hover { transform: translateY(-1px); }
.sticky-cta__pip { width: 7px; height: 7px; background: var(--graphite); border-radius: 50%; flex: none; }
/* Exactly one orange thing per screen. Over the hero that one thing is
   the switch plate, over the services field it is the Part that is
   seating, in section 04 it is a block while it is actually crossing,
   and in the closing frame it is the dot. In all of them the pill
   steps back to graphite with an orange pip, which is exactly how the
   approved boards draw it. */
.sticky-cta.is-ink { background: var(--graphite); color: var(--paper); }
.sticky-cta.is-ink .sticky-cta__pip { background: var(--orange); }
/* a graphite pill needs an edge wherever the masthead's own ground is
   Ink: over the dark hero, over the quiet shift, and over the closing
   frame where the bar reverses */
html.lights-dark .sticky-cta.is-ink,
.mast.is-night .sticky-cta.is-ink,
.mast.is-rev .sticky-cta.is-ink { box-shadow: inset 0 0 0 1px var(--ink-rule); }
html.rm .sticky-cta { transition: none; }

/* The phone build's 5px Spec Paper gutter (`is-onpaper`) is GONE, and
   so is the code that set it. It existed because a pill fixed over the
   services tally landed on a filled Spec Paper block and read as part
   of it. Nothing scrolls under the pill any more, so there is nothing
   for it to disappear into. */

/* ---------- 9. SCENE ----------------------------------------- */
.scene { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.scene canvas { display: block; width: 100%; height: 100%; }
/* THE STAGE UNSTACKS WITH THE SCRIPT OFF TOO. The three chapters are
   `position: absolute; inset: 0` inside one sticky viewport and are
   cross-wiped by the master timeline; with no script there is no
   timeline, so without this they print on top of each other and
   chapter 02's copy runs through the hero's. Reduced motion already
   unstacked them - the no-script page needs exactly the same thing. */
html.rm .scene,
html:not(.js) .scene { position: absolute; top: 0; left: 0; right: 0; height: 100vh; }

/* THE AUTHORED STILL. Only three builds ever show it: reduced motion,
   no usable WebGL / weak hardware (`html.still`), and the script-off
   page. It is a BACKGROUND image on a display:none element in every
   other build, which is how the 52KB never leaves the server on the
   path that draws the room live: a rule that does not match is never
   fetched, and neither is a background on display:none.

   `motion resolves to the static asset` is the standing rule, so the
   lit still is the hero's resting state and the night still is
   chapter 02's. */
/* SCOPED TO .scene ON PURPOSE. `still` is also the class on <html>
   that selects this build, and a bare `.still { display: none }`
   matches the document element itself and hides the entire page. */
.scene > .still { display: none; }
html.still .scene > .still,
html.rm .scene > .still,
html:not(.js) .scene > .still {
  display: block; position: absolute; inset: 0;
  background: var(--paper) center center / cover no-repeat;
  background-image: url("../assets/hero/hero-lit-1440x900.webp");
}
html.still .scene > .still--night,
html.rm .scene > .still--night,
html:not(.js) .scene > .still--night {
  background-image: url("../assets/hero/hero-night-1440x900.webp");
  opacity: 0;
}
@media (orientation: portrait) {
  html.still .scene > .still, html.rm .scene > .still, html:not(.js) .scene > .still {
    background-image: url("../assets/hero/hero-lit-1080x1920.webp");
  }
  html.still .scene > .still--night, html.rm .scene > .still--night, html:not(.js) .scene > .still--night {
    background-image: url("../assets/hero/hero-night-1080x1920.webp");
  }
}
/* the still build never creates a WebGL context, so there is nothing
   in the canvas to show */
html.still #gl { display: none; }

main { position: relative; z-index: 2; }

/* ---------- 10. STAGE / CHAPTERS ----------------------------- */
/* One sticky viewport. Two chapters layered inside it, cross-wiped by
   the scrubbed master timeline. Never a half-and-half state.

   The stage is 250vh. It was 340vh, and 45vh of that was the scrubbed
   pre-throw, which is gone: the lights are on when the page paints and
   the switch is a thing to find rather than a gate on reading. The
   rest of the cut is the quiet shift, which was the longest held beat
   on the page for a chapter that says four things. To make the whole
   story slower or faster, change this height, never TIMING. */
.stage { position: relative; height: 250vh; }
.stage__hold {
  position: sticky; top: 0;
  height: 100vh; height: 100svh;
  overflow: hidden;
  user-select: none;
}
.hold {
  position: absolute; inset: 0; z-index: 1;
  display: flex; align-items: center;
  will-change: transform, opacity;
}
.hold > .hero, .hold > .chap { width: var(--wrap); margin-inline: auto; position: relative; z-index: 1; }
.hold > .shelf { z-index: 2; }

/* On a wide screen the copy sits on empty ground and the plate is far
   enough right that nothing needs a scrim. On a phone the plate rides
   high and the copy is under it, so the copy gets a band of whatever
   the ground currently is. Both cases are handled in the mobile block. */

/* HERO
   FOUR THINGS AND NOTHING ELSE. The name, one line of what we do, the
   offer, and the button to the form - and on a 390 x 844 phone all
   four have to be above the fold with nothing competing with them. The
   lede, the four row spec table and the scroll cue that used to sit
   under them are deleted rather than hidden: they were the fifth,
   sixth and seventh things asking for the same five seconds.

   Nothing here has a JavaScript start state. The hero used to be
   revealed by chapters.heroIn, which cannot run until the modules have
   arrived; on a slow phone that left the offer and the button
   invisible for as long as that took. The one part of the page that
   has to be readable in the frame it paints simply is. */
.hero__inner { max-width: 620px; }
/* THE TIMESTAMP ABOVE THE HEADLINE. It takes `.mono`'s own Steel and
   adds nothing: it was Torque Orange when it was the brand name, and
   a time is not a mark. Steel is 5.26 on Spec Paper, AA, and it leaves
   the hero's one orange thing to the button. */
.hero__eyebrow { margin-bottom: 18px; }

.hero__title {
  font-size: clamp(2.6rem, 5.4vw, 5.8rem);
  line-height: 0.94; letter-spacing: -0.032em;
  font-variation-settings: "wdth" 104, "wght" 700;
  margin-bottom: 24px;
  max-width: 15ch;
}

/* THE OFFER, IN ONE LINE, AND IT IS THE CONTRACT. The words are fixed
   and are not ours to edit: free first week, one flat weekly rate
   quoted before we start, no contract, cancel by text. The rule down
   its left is Torque Orange, which is the only orange this brand puts
   anywhere near type on paper - and it is a rule, not the words. */
.hero__offer {
  max-width: 44ch;
  font-size: clamp(1rem, 1.15vw, 1.1875rem); line-height: 1.5;
  color: var(--graphite);
  border-left: 2px solid var(--torque); padding-left: 14px;
}
.hero__cta { margin-top: 28px; }

/* ---------- 10b. THE HERO IN THE DARK ------------------------
   The switch has not been thrown yet. Everything in the hero inverts,
   and NOTHING here has a transition on colour: the wash is the
   transition, and the words flip on the single frame its front reaches
   the copy column. A cross-fade underneath it would read as a dissolve
   and the whole point is that a light does not dissolve on.

   Scoped to the hero, the shelf label and the cue only. The sections
   below the stage are light mode from the seat frame onward and must
   not follow the hero back off. */
html.lights-dark .hero__title { color: var(--paper); }
/* `--ink-2` is Steel's own ink-side value (7.0 on Ink): the timestamp
   keeps the register it has on paper instead of picking up an orange
   it no longer has any claim to. The dark hero is down to ONE orange
   thing now - the button - which is what the colour rule asks for and
   what this column could never quite manage while the eyebrow was the
   brand name in Safety Orange. */
html.lights-dark .hero__eyebrow { color: var(--ink-2); }
/* the rule stays NEUTRAL in the dark, not orange. */
html.lights-dark .hero__offer { color: var(--paper); border-left-color: var(--ink-rule); }
html.lights-dark .hold--hero .shelf { color: var(--ink-2); }
html.lights-dark .hold--hero .shelf::before { background: var(--ink-rule); }
html.lights-dark .switchcap { color: var(--paper); }

/* CHAPTERS */
.chap__inner { max-width: 620px; }
.chap--right .chap__inner { margin-left: auto; }
.chap__title {
  margin: 22px 0 30px;
  font-size: clamp(2.4rem, 5.2vw, 5.2rem);
  line-height: 0.96; letter-spacing: -0.03em;
  font-variation-settings: "wdth" 104, "wght" 700;
}
.chap__title .ln { display: block; overflow: hidden; padding-bottom: .06em; }

/* The caption the switch stamps on the frame it seats: "STILL ON."
   Positioned by projecting the plate's world position back to the
   screen, so it stays under the switch wherever the switch is. The
   wrapper carries the placement transform and the inner span carries
   the animation, so gsap can never overwrite the position. */
.switchcap {
  position: absolute; top: 0; left: 0; z-index: 3;
  padding-top: 10px; color: var(--graphite);
  pointer-events: none; white-space: nowrap;
}
.switchcap::before {
  content: ""; position: absolute; top: 0; left: 0;
  width: 64px; height: 1px; background: var(--orange);
}
.switchcap:not(.is-on) { visibility: hidden; }
.switchcap span { display: inline-block; }
html.rm .switchcap { display: none; }

/* Chapter 02, the quiet shift. The page ground is Spec Paper by the
   time anyone reaches this, so the night is this SECTION'S own scrim
   (#night, fixed, z-index 1) and not a page state. Light copy on it. */
.hold--c2 { color: var(--paper); }
.hold--c2 .chap__index { color: #9AA6B2; }
.hold--c2 .chap__index .rule { background: #46525E; }
.hold--c2 .chap__title { color: var(--paper); }
.clock { margin: 26px 0 0; border-top: 1px solid #3A4650; max-width: 560px; }
.clock li {
  display: grid; grid-template-columns: 110px 1fr; gap: 20px; align-items: baseline;
  padding: 15px 0; border-bottom: 1px solid #3A4650;
}
.clock__t { color: var(--orange); }
.clock__x { font-size: clamp(1rem, 1.25vw, 1.1875rem); color: #DFE3E6; }
.chap__payoff {
  margin-top: 40px;
  font-size: clamp(1.9rem, 3.6vw, 3.4rem); line-height: 1.0; letter-spacing: -0.03em;
  font-variation-settings: "wdth" 104, "wght" 700;
  color: var(--paper);
}

/* THE PAYOFF PANEL, and it is the first thing after the hero.
   One sentence, on the ground the quiet shift has just darkened,
   standing in the column chapter 02 is about to take - so the
   composition has already crossed to the left under it and nothing
   prints over the desk. It has NO index, NO heading and nothing after
   it: the whole screen is the line, which is what makes it read as a
   payoff rather than as a chapter title. It carries no ground of its
   own in the live build, because the room's own night IS the ground;
   the static builds below give it Ink outright, where there is no
   night to fall. */
.hold--pay { color: var(--paper); }
.hold--pay .chap__payoff {
  margin-top: 0;
  font-size: clamp(2.2rem, 4.6vw, 4.4rem);
  max-width: 14ch;
}

/* ---------- 11. SECTIONS ------------------------------------- */
.section { position: relative; background: var(--paper); }
/* the top padding clears the fixed masthead, so a snap onto a section's
   own offsetTop always leaves its heading fully in view */
.section--how   { padding: calc(var(--mast-h) + 32px) 0 140px; overflow: hidden; }
.section--about { padding: calc(var(--mast-h) + 40px) 0 110px; }

/* ---------- 12. 03  WHAT DO YOU RUN?  /  services ------------ */
/* Entered by business type, never by a list of Parts and never by build
   status. World B's block grammar holds: butted matte blocks with no
   gaps, and exactly one of them orange because it is the one you picked. */
.svc { padding: calc(var(--mast-h) + 22px) 0 88px; }
.svc__head { display: grid; gap: 14px; max-width: 900px; margin-bottom: 22px; }
.svc__display {
  font-size: clamp(2.4rem, 5.2vw, 4.6rem); line-height: 0.94; letter-spacing: -0.033em;
  font-variation-settings: "wdth" 104, "wght" 700;
}

.svc__row {
  display: grid; grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--zinc); border-left: 1px solid var(--zinc);
}
.svc__biz {
  padding: 20px; text-align: left;
  background: var(--paper-2); color: var(--graphite);
  border-right: 1px solid var(--zinc); border-bottom: 1px solid var(--zinc);
  font-family: var(--sans); font-size: clamp(0.9375rem, 1.35vw, 1.1875rem);
  line-height: 1.15; letter-spacing: -0.016em;
  font-variation-settings: "wdth" 102, "wght" 600;
  transition: background-color .22s var(--e-seat), color .22s var(--e-seat);
}
.svc__biz:hover { background: #E3DFD5; }
/* THE SECTION'S ONE ORANGE THING IS THE BUSINESS YOU PICKED, and it is
   orange in EVERY build - no script, reduced motion, mid sequence - so
   the rule "exactly one orange thing per screen" holds here by
   construction rather than by a handover between two moving elements.
   Safety Orange is a surface and it takes a graphite label, never
   white (5.36 against 3.09). It went to the Part blocks for one round,
   when the Part was the thing that moved; the Parts are a standing
   list now and the only live state in the row is which business you
   are looking at. Trucking is highlighted on exactly the same terms as
   the other seven: when, and only when, the reader picks it. */
.svc__biz[aria-pressed="true"] { background: var(--orange); color: var(--graphite); }
.svc__biz[aria-pressed="true"]:hover { background: #F04E0F; }

/* Every block inside a branch has a reserved height, so all eight
   branches are exactly the same height and changing business never
   shifts the page under the reader. */
/* Every branch reserves the height of the tallest, so changing
   business never shifts the page under the reader - and the snap
   points below this section never move while somebody is reading it.
   Four rows at 1440 is 236px; "Shop" has three and "Something else"
   has one, and they simply leave the space. */
.svc__branches { margin-top: 22px; min-height: 236px; }
.svc__branch[aria-hidden="true"] { display: none; }
html:not(.js) .svc__branch[aria-hidden="true"] { display: block; }

.svc__price {
  max-width: 62ch; margin: 0;
  font-family: var(--mono); font-size: 12.5px; line-height: 1.75;
  letter-spacing: .01em; color: var(--steel);
}

/* ---------- 12b. THE JOBS  /  all of them, at once ------------
   THIS IS THE MOST IMPORTANT BLOCK ON THE PAGE and the one test it has
   to pass is that a skimmer gets every job for the business they
   picked in ONE GLANCE. The build this replaces showed one sentence at
   a time out of four, swapping its subject, dropping into a ledger and
   taking six seconds to say what a four row list says instantly. The
   idea was good and it hid the information, so it is gone.

   What is left is a ruled list: the job on the left, the Part that
   takes it on the right, every row visible from the first frame and in
   every build. The only motion is that each Part block travels the
   last 18px into its rest - two tweens, `reveal` then `seat`, the same
   law as every other arrival here - and nothing is hidden or moved
   while it does, because the row it belongs to is already laid out.
   --------------------------------------------------------------- */
.jobs { border-top: 1px solid var(--zinc); }
.job {
  display: grid; grid-template-columns: 1fr auto; gap: 28px; align-items: center;
  padding: 17px 0; border-bottom: 1px solid var(--zinc);
}
.job__do {
  font-size: clamp(1.125rem, 1.9vw, 1.625rem); line-height: 1.16;
  letter-spacing: -0.022em;
  font-variation-settings: "wdth" 102, "wght" 600;
  color: var(--graphite);
}
/* the Part that takes it. Graphite: the one orange thing in this
   section is the business block above, and it is never both. */
.job__part {
  display: inline-flex; align-items: center; justify-self: end;
  padding: 7px 11px; white-space: nowrap;
  background: var(--graphite); color: var(--paper);
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  letter-spacing: .09em; text-transform: uppercase;
}
/* the start of the 18px run. Scoped to the build that can animate it,
   so the no-script page and the reduced build ship it already seated. */
html.js:not(.rm) .job__part { transform: translateX(18px); }

/* the offer and the button that acts on it, under the row. The button
   is GRAPHITE: the one orange thing in this section is the business
   block the reader picked. */
.svc__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 28px; flex-wrap: wrap; margin-top: 22px;
}

/* ---------- 13. BUTTONS -------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 15px 24px; border-radius: 2px; text-decoration: none;
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase;
  border: 1px solid transparent;
  transition: background-color .22s var(--e-seat), color .22s var(--e-seat), border-color .22s var(--e-seat);
}
.btn--primary { background: var(--orange); color: var(--graphite); }
.btn--primary:hover { background: var(--torque); color: var(--paper); }
.btn--ghost { border-color: var(--zinc); color: var(--graphite); }
/* used where an orange surface already owns the one orange on the screen */
.btn--ink { background: var(--graphite); color: var(--paper); }
.btn--ink:hover { background: var(--ink); }
.btn--ghost:hover { border-color: var(--graphite); }
/* THE MAIL BUTTON STEPS BACK, and this is a colour decision rather
   than a tidy-up. It was Safety Orange, and it was the footer's one
   orange thing while the only way to make contact was a mailto. There
   is a form on this screen now and the form's submit button owns the
   orange, so two orange surfaces would be on one screen. The address
   is not going anywhere - it is still the button's own label, and it
   is still the path for anyone who would rather email - it is simply
   no longer the loudest thing in the section. A graphite button on
   Ink needs its edge, exactly as the graphite pill does. */
.btn--magnet {
  background: var(--graphite); color: var(--paper);
  box-shadow: inset 0 0 0 1px var(--ink-rule);
  padding: 22px 34px; font-size: 14px; will-change: transform;
}
.btn--magnet:hover { background: #23272B; }

/* ---------- 14. HOW WE WORK ---------------------------------- */
.marquee {
  border-top: 1px solid var(--zinc); border-bottom: 1px solid var(--zinc);
  padding: 18px 0; margin-bottom: 120px; overflow: hidden;
}
.marquee__row { display: flex; white-space: nowrap; will-change: transform; }
.marquee__row span {
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ash);
}

.how__head { display: grid; gap: 24px; max-width: 1000px; margin-bottom: 88px; }
.how__display {
  font-size: clamp(3rem, 7.4vw, 8rem); line-height: 0.9; letter-spacing: -0.035em;
  font-variation-settings: "wdth" 106, "wght" 800;
  transform-origin: left center; will-change: transform;
}

/* ---------- 14b. THE HANDOFF  /  the section enacts its headline
   The headline is "Four steps. Then nothing." The previous build drew
   four steps that sit there forever, which is the one thing "then
   nothing" cannot mean. This one hands the work over: the four steps
   start on the READER'S side of a divide, cross to ours one at a time,
   and the reader's side ends EMPTY. That empty field is the payoff.

   ONE OBJECT, NOT TWO. Our side is Ink from the first frame with four
   empty holes waiting in it - the same thought as the mark, where the
   dot seats into the hole left for it in BORING, and as the Part block
   that takes "you" out of its slot in the section above. The work
   crosses into those holes, and the terms we then hold it under print
   along the bottom of the same ink form. There is no second module.

   THE SHAPE. The ink form is an L: a half width column on top of a
   full-content-width band. What is left is a rectangular NOTCH bitten
   out of its top left corner. That notch is the reader's side, and an
   emptiness with ink on two sides, a ruled label above it and one
   figure sitting in it cannot read as a layout accident. The figure
   counts 04 down to 00 as the work leaves, and 00 is the last thing
   read before the section ends.

   ONE VERTICAL LINE runs the whole object: the ink column's left edge
   above, the ruled column split of the terms band below. Both are at
   50% of the content width, which is why the terms band's horizontal
   padding is symmetric.

   NOT the block grammar of the services section above it. On the
   reader's side there are no boxes at all, only type on open Spec
   Paper. A block exists only while it is crossing, which is also the
   only moment this section carries any Safety Orange.
   --------------------------------------------------------------- */
.hand {
  --pad:  clamp(20px, 2vw, 30px);     /* the ink form's inner gutter */
  /* the column's foot: shorter than every slot at every width, and
     never ruled off from the last one. See .hand__ours below. */
  --foot: clamp(74px, 6.0vw, 88px);
}
.hand__cols { display: grid; grid-template-columns: 1fr 1fr; align-items: stretch; }

/* THE READER'S SIDE. Nothing is drawn on it: a label under a hairline
   at the top, a figure at the bottom, and between them open paper that
   four pieces of type happen to be standing on until they leave. */
.hand__mine { display: flex; flex-direction: column; }
.hand__label {
  padding: 14px 0 13px; margin: 0;
  border-bottom: 1px solid var(--zinc); color: var(--steel);
}
/* The figure sits in the column's foot, which is the strip of the
   notch below the last step. It is sized so the whole block - rule,
   gap and numeral - fits inside `--foot` and never runs up into a
   step that is still standing on the reader's side. */
.hand__count {
  margin-top: auto; margin-bottom: 12px;
  font-family: var(--mono); font-weight: 500;
  font-size: clamp(2.375rem, 4.6vw, 4rem); line-height: 0.86;
  letter-spacing: -0.03em; color: var(--graphite);
}
.hand__count::before {
  content: ""; display: block; width: 84px; height: 1px;
  background: var(--zinc); margin-bottom: 12px;
}

/* OUR SIDE. Ink from the first frame, with the holes already cut.

   FOUR CELLS, AND THE FOOT IS NOT A FIFTH.

   The notch has to be taller than the four steps standing in it,
   because the figure sits below them and a figure that overlaps step
   04's heading is worse than any amount of empty ink. The two columns
   are equal height, so that extra height is ink on our side whether
   we like it or not. What was wrong was not that the ink existed, it
   was that it was RULED OFF: slot 04 closed with a hairline, and a
   ruled empty region under four ruled full ones counts as a fifth.

   So the last slot no longer draws its bottom rule. There are four
   rules in the column - one under the label and three between the
   steps - which is exactly n-1 for four cells, and nothing closes the
   last one. What is left below step 04 is the column's foot, the same
   ink, running on into the terms band without a line anywhere in it.
   It is also cut to the size of the figure it exists for rather than
   to a slot's size. `checks.how.*.column` asserts the rule count, the
   open last cell and the foot's height. */
.hand__ours { background: var(--ink); padding-bottom: var(--foot); }
.hand__label--ours {
  padding-left: var(--pad); padding-right: var(--pad);
  border-bottom-color: var(--ink-rule); color: var(--ink-2);
}
.hand__slots { list-style: none; }
.slot { position: relative; }
/* the hole left for it: a ruled empty field on the ink, present from
   the first frame so the eye knows where the event is going to land */
.slot__hole {
  position: absolute; inset: 0; opacity: 0; pointer-events: none;
  background: rgba(244, 242, 237, .05);
  border-bottom: 1px solid var(--ink-rule);
}
/* the last cell is never closed, at any frame: the hole waiting in it
   draws no bottom rule either, so the column's foot is not ruled off
   from it before the work has even started crossing */
.slot:last-child .slot__hole { border-bottom: 0; }

/* THE THING THAT CROSSES. In normal flow inside its hole, so the hole
   is exactly the size of the thing that will fill it and nothing
   reflows when it lands. It is moved with a transform only. */
.part {
  position: relative;
  padding: 20px var(--pad) 22px;
  box-shadow: inset 0 -1px 0 var(--ink-rule);
  will-change: transform;
}
/* nothing closes the last cell: see the note above `.hand__ours` */
.slot:last-child .part { box-shadow: none; }
.part h3 {
  font-size: clamp(1.0625rem, 1.42vw, 1.3125rem); line-height: 1.16;
  letter-spacing: -0.019em; font-variation-settings: "wdth" 102, "wght" 640;
  color: var(--paper); margin-bottom: 9px;
}
.part p { font-size: 14.5px; line-height: 1.5; color: var(--ink-2); }

/* THE TERMS. The bottom of the same ink form, at the full content
   width, so our side ends up holding the whole page and the notch is
   closed underneath. A SET, so no numbering and no <ol>. */
.hand__terms {
  background: var(--ink); color: var(--paper);
  padding: 34px 40px 40px;
}
.hand__termslabel { color: var(--ink-2); margin-bottom: 20px; }

/* SEVEN INTO TWO COLUMNS, CLOSED. Four and three left the bottom
   right corner short and ran the column rule down past the last line
   into nothing, so the band ended ragged. It is now THREE and three
   with the seventh statement set full width along the bottom, which
   tiles the band completely: every row is filled edge to edge and the
   last line closes the ink form the way the form's own bottom edge
   does. Nothing is reworded, reordered or dropped - the seventh
   statement is still the seventh, it just spans.

   The column rule is drawn by the second column's own left border
   rather than by a pseudo element, so it stops exactly where the
   columns stop instead of running past them. It falls on the grid's
   own column line, which is 50% of the content box, which is the same
   x as the ink column's left edge above: ONE VERTICAL LINE still runs
   the whole object. `checks.how.terms.alignedWithDivide` asserts the
   x, `closesCleanly` asserts the tiling. */
.hand__lines {
  position: relative; list-style: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: repeat(3, auto) auto;
}
.hand__lines li {
  border-top: 1px solid var(--ink-rule);
  padding: 15px 0 16px; font-size: 15px; line-height: 1.5;
}
/* the placement is explicit rather than left to auto flow, because
   auto placement with a fixed column and sparse packing walks the row
   cursor forward and drops item 4 into row 3. Scoped to the two
   column build so the phone's single column needs no unwinding. */
@media (min-width: 901px) {
  .hand__lines li:nth-child(1) { grid-area: 1 / 1; }
  .hand__lines li:nth-child(2) { grid-area: 2 / 1; }
  .hand__lines li:nth-child(3) { grid-area: 3 / 1; }
  .hand__lines li:nth-child(4) { grid-area: 1 / 2; }
  .hand__lines li:nth-child(5) { grid-area: 2 / 2; }
  .hand__lines li:nth-child(6) { grid-area: 3 / 2; }
  .hand__lines li:nth-child(7) { grid-area: 4 / 1 / 5 / 3; }
  .hand__lines li:nth-child(-n+3) { padding-right: 40px; }
  .hand__lines li:nth-child(n+4):nth-child(-n+6) {
    padding-left: 40px; border-left: 1px solid var(--ink-rule);
  }
}

/* ---------- 15. ABOUT ---------------------------------------- */
.about { display: grid; grid-template-columns: 380px 1fr; gap: 88px; align-items: start; }
.about__side .h2 { margin: 24px 0 16px; }
.about__meta { line-height: 1.8; }
.about__body p { font-size: clamp(1.0625rem, 1.3vw, 1.25rem); line-height: 1.5; margin-bottom: 18px; max-width: 54ch; }
.about__kicker {
  font-variation-settings: "wdth" 104, "wght" 600;
  border-left: 2px solid var(--orange); padding-left: 20px; margin-top: 32px;
}

/* ---------- 16. FOOTER --------------------------------------- */
.foot {
  position: relative; z-index: 2;
  background: var(--ink); color: var(--paper);
  padding: 140px 0 56px;
}
.foot__display {
  margin: 28px 0 56px;
  font-size: clamp(2.6rem, 6.4vw, 7rem); line-height: 0.92; letter-spacing: -0.035em;
  font-variation-settings: "wdth" 106, "wght" 800;
  max-width: 14ch;
}
.foot__display .ln { display: block; overflow: hidden; padding-bottom: .06em; }
.foot__cta { display: flex; align-items: center; gap: 28px; flex-wrap: wrap; margin-bottom: 120px; }
.foot__note { color: var(--ash); }

/* ---------- 16b. THE CLOSING FRAME  /  H2 "The Dot" ----------
   The mark at hero scale, and the dot is a real object rather than a
   printed circle. The B is the approved path data straight out of
   direction-a-dot-lockup-reversed.svg, so it matches the mark exactly
   and nothing is redrawn.

   Geometry from CONCEPTS-V3 H2: the disc is 0.868 x cap height and the
   gap is 0.285 x cap height, tightened from the icon's 0.353 because at
   hero scale the icon ratio reads loose.

   The disc is Steel until the section is 60% in view, then Safety
   Orange. There is no transition on `background` here: the swap is one
   frame, no fade, exactly like the dot in the preloader. It is also the
   ONLY orange on this screen, which is why .foot__tag steps back to ash
   below and the sticky pill goes graphite over the footer. */
.closer {
  display: flex; align-items: flex-end;
  gap: calc(var(--cap) * 0.285);
  margin-bottom: 26px;
  --cap: clamp(128px, 19vw, 268px);
}
.closer__b {
  display: block; width: auto; height: var(--cap);
  fill: var(--paper);
}
.closer__disc {
  display: block; flex: none;
  width: calc(var(--cap) * 0.868); height: calc(var(--cap) * 0.868);
  border-radius: 50%;
  background: radial-gradient(circle at 36% 30%, #7D858D 0%, #626970 46%, #4C535A 78%, #434A50 100%);
  box-shadow:
    inset 0 calc(var(--cap) * -0.05) calc(var(--cap) * 0.10) rgba(0, 0, 0, .30),
    inset 0 calc(var(--cap) * 0.035) calc(var(--cap) * 0.075) rgba(255, 255, 255, .10),
    calc(var(--cap) * 0.075) calc(var(--cap) * 0.085) calc(var(--cap) * 0.16) rgba(0, 0, 0, .48);
}
.closer.is-on .closer__disc {
  background: radial-gradient(circle at 36% 30%, #FF8348 0%, #FF5C1A 46%, #E24A0E 78%, #CF430C 100%);
}
.closer__cap { color: var(--ash); margin-bottom: 56px; }

/* ---------- 16c. THE ENQUIRY FORM ---------------------------
   The contact section's headline is "Tell us the boring part." and
   until this round the only way to do that was a `mailto:` link. A
   mailto does nothing visible on a desktop with no mail client, hands
   a phone a blank compose window, and records nothing either way, so
   an interested reader who does not press send is a reader the
   business never hears about. The form is directly under the headline
   because the headline is its question.

   IT IS AN INK FORM, in the same sense section 04's is: a Graphite
   block on the footer's Ink, with the fields cut into it as darker
   wells at Ink with a hairline edge. A field is a hole left for
   something to go in, which is the same thought as the mark, as the
   Part block seating into the sentence in the services section, and
   as the four steps crossing into their holes in section 04.

   COLOUR, and it is the rule this section had to resolve
   deliberately:

   - Labels are `--ink-2` (7.0 on Ink), the optional marker and the
     hints are `--ash` (5.6). Field text is Spec Paper.
   - EXACTLY ONE ORANGE THING. The submit button is the Safety Orange
     SURFACE on this screen, with a graphite label (5.36), which is
     why `.btn--magnet` below it steps back to graphite on ink with a
     rule around it - it used to be the second orange surface in the
     footer and now it is the quiet alternative it should always have
     been once a form exists.
   - Errors are Safety Orange TEXT and a Safety Orange inset rule on
     the field that carries them. Orange text on the ink footer is
     sanctioned (5.97 here) and a 2px rule is a mark, not a surface,
     so the screen still carries exactly one orange surface while an
     error is showing. It is the same grammar as section 04: the
     orange is an EVENT, and it goes when the error does.
   - The focus ring goes Safety Orange inside the footer. Torque
     Orange is the ring everywhere else because everywhere else is
     Spec Paper; on Ink it measures 3.4 and Safety Orange measures
     6.0, and a focus ring is a mark.

   Nothing here moves on its own. The only motion in this block is the
   handover from the form to the success panel, in js/enquiry.js: one
   arrives, seats, and stops.
   ------------------------------------------------------------- */
.enq { max-width: 900px; margin-bottom: 96px; }
.enq__lede { color: var(--ink-2); max-width: 62ch; margin-bottom: 32px; }

.enq__form {
  max-width: 760px;
  background: var(--graphite);
  border: 1px solid var(--ink-rule);
  border-radius: 2px;
  padding: 30px 30px 28px;
  /* the native select popup, the autofill chrome and the caret all
     follow this rather than guessing from a light page */
  color-scheme: dark;
}
.enq__form[hidden], .enq__done[hidden] { display: none; }

/* the section's own h2 says this at display scale immediately above,
   so it is the form's accessible name and nothing more */
.enq__formtitle {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.enq__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px 24px; }
.fld { display: grid; gap: 8px; align-content: start; }
.fld--wide { grid-column: 1 / -1; }
.fld__label { color: var(--ink-2); }
.fld__opt { color: var(--ash); text-transform: none; letter-spacing: .04em; }
.fld__hint { font-size: 13.5px; line-height: 1.45; color: var(--ash); max-width: 68ch; }

.fld__in {
  width: 100%; display: block;
  padding: 13px 14px;
  background: var(--ink); color: var(--paper);
  border: 1px solid var(--ink-rule); border-radius: 2px;
  font-family: var(--sans);
  /* 16px, deliberately: anything smaller and iOS zooms the page in on
     focus, which throws the reader out of the layout mid-form */
  font-size: 16px; line-height: 1.35;
  transition: border-color .2s var(--e-seat), box-shadow .2s var(--e-seat);
}
.fld__in::placeholder { color: var(--steel); }
.fld__in:hover { border-color: #39414A; }
/* The border lifting is the pointer's cue and the outline is the
   keyboard's. `outline: none` on a bare `:focus` used to win the
   cascade over the `:focus-visible` rule below and left the ring with
   a colour, a width and NO STYLE - which is a ring that does not
   paint. It is scoped to the case it was for. Caught by
   `enq.focusRing`, which measures the ring on a focused field rather
   than reading the rule off the stylesheet. */
.fld__in:focus { border-color: var(--ash); }
.fld__in:focus:not(:focus-visible) { outline: none; }
.fld__area { resize: vertical; min-height: 112px; }
/* Chrome paints its own pale ground behind an autofilled field, which
   is the one thing on this page that can put a light rectangle on the
   Ink footer. Held to the field's own colours. */
.fld__in:-webkit-autofill,
.fld__in:-webkit-autofill:hover,
.fld__in:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--paper);
  -webkit-box-shadow: 0 0 0 1000px var(--ink) inset;
  caret-color: var(--paper);
}

/* the caret is drawn rather than fetched: an inline data URI, 0 KB,
   no request, in the same Steel tint the labels use */
.fld__sel {
  appearance: none; -webkit-appearance: none;
  padding-right: 40px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7'%3E%3Cpath d='M1 1l4.5 4.5L10 1' fill='none' stroke='%239AA3AB' stroke-width='1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
}
.fld__sel option { background: var(--ink); color: var(--paper); }

/* THE ERROR IS A MARK, NOT A SURFACE. See the note at the top. */
/* THE TUESDAY LIST NOTICE, under the open field and nowhere else.
   Nothing is published from an answer where the reader was not told
   first, so without this line every answer typed into that field
   would be permanently unpublishable. It reads at the same weight as
   the field's own hint because it is the same kind of thing - what
   happens to what you write - and not a block of terms. Ash on
   Graphite measures 6.2. The link is Spec Paper with a Steel
   underline rather than orange: the submit button below it is this
   screen's one orange thing. */
.fld__notice { font-size: 13.5px; line-height: 1.45; color: var(--ash); max-width: 68ch; }
.fld__notice a {
  color: var(--paper); text-decoration: underline;
  text-decoration-color: var(--steel); text-underline-offset: 3px;
}
.fld__notice a:hover { text-decoration-color: var(--paper); }

.fld__err { color: var(--orange); text-transform: none; letter-spacing: .02em; line-height: 1.4; }
.fld__err:empty { display: none; }
/* the field's own edge stays in the palette; the 2px bar is the mark
   and it is the only orange the field itself carries */
.fld__in[aria-invalid="true"] { box-shadow: inset 2px 0 0 var(--orange); }

/* the honeypot. NOT display:none, because something that fills every
   input on the page has to be able to find it; off screen, out of the
   tab order and hidden from assistive technology, so no person can */
.enq__trap {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}

.enq__send {
  display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
  margin-top: 28px;
}
.enq__btn { padding: 17px 28px; font-size: 12.5px; }
/* while it is sending there is no orange surface at all, which is the
   safe direction to be wrong in. There is no spinner, because a
   spinner is a continuous rotation and this build has none. */
.enq__btn[disabled] {
  background: var(--graphite); color: var(--ink-2);
  box-shadow: inset 0 0 0 1px var(--ink-rule); cursor: default;
}
.enq__note { color: var(--ash); flex: 1 1 240px; max-width: 46ch; }

/* the space is reserved from the first frame: a message arriving here
   must never move the closing frame under it */
.enq__alert {
  margin-top: 18px; min-height: 1.4em;
  color: var(--orange); text-transform: none; letter-spacing: .02em; line-height: 1.4;
}
.enq__alert a { color: var(--orange); }

/* THE SUCCESS STATE. Not an alert box and not a page reload: the form
   leaves and this arrives in its place, once, and stops. */
.enq__done {
  max-width: 760px; padding: 34px 30px 32px;
  background: var(--graphite); border: 1px solid var(--ink-rule); border-radius: 2px;
}
.enq__doneindex { position: relative; padding-top: 12px; color: var(--ink-2); }
.enq__doneindex::before {
  content: ""; position: absolute; top: 0; left: 0;
  width: 84px; height: 1px; background: var(--ink-rule);
}
.enq__donetitle {
  margin: 18px 0 16px;
  font-size: clamp(1.75rem, 3.2vw, 2.75rem); line-height: 1.02; letter-spacing: -0.028em;
  font-variation-settings: "wdth" 104, "wght" 700;
}
.enq__donebody { max-width: 52ch; margin-bottom: 18px; }
.enq__doneterms {
  font-family: var(--mono); font-size: 12.5px; line-height: 1.75;
  letter-spacing: .01em; color: var(--ash); max-width: 84ch;
}

/* Torque Orange is the focus ring on Spec Paper and measures 3.4 on
   Ink. Safety Orange measures 6.0, and a focus ring is a mark. The
   whole shorthand is restated rather than just the colour, so this
   rule owns the ring outright wherever it applies. */
.foot :focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; }

.foot__or { color: var(--ash); width: 100%; margin-bottom: -6px; }

.foot__tag { color: var(--ash); margin-bottom: 72px; }

.foot__bottom {
  display: flex; justify-content: space-between; gap: 40px; flex-wrap: wrap;
  border-top: 1px solid #2A3033; padding-top: 28px; padding-bottom: 24px;
}
@media (min-width: 901px) { .foot__nav { margin-right: 200px; } }
.foot__bottom .mono { color: var(--ash); margin-bottom: 6px; }
.foot__nav { display: flex; gap: 24px; flex-wrap: wrap; }
.foot__nav a {
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ash);
  text-decoration: none; transition: color .2s var(--e-seat);
}
.foot__nav a:hover { color: var(--orange); }

/* ---------- 17. ANIMATION START STATES ----------------------- */
html.js:not(.rm) [data-line],
html.js:not(.rm) [data-fade] { opacity: 0; }

/* THE HANDOFF. `[data-line]` is claimed above and is chapter 02's, so
   this section uses its own `[data-cross]`, `[data-count]` and
   `[data-terms]`. The DEFAULT state of every rule below is the
   RESOLVED one - the work already on our side, the holes gone, the
   terms printed - so with the script off the page ships the finished
   composition and says the true thing. Only `html.js:not(.rm)` winds
   it back to frame one, and the release rules follow in source order
   because they have to be able to win.

   `--cross-x` is measured by createHandoff and written on `.hand`.
   The -100% fallback is exact whenever the two columns are equal,
   which they are at every width. */
html.js:not(.rm) [data-cross] .part {
  transform: translateX(var(--cross-x, -100%));
  box-shadow: none;
}
html.js:not(.rm) [data-cross] .part h3 { color: var(--graphite); }
html.js:not(.rm) [data-cross] .part p  { color: var(--steel); }
html.js:not(.rm) [data-cross] .slot__hole { opacity: 1; }
html.js:not(.rm) [data-terms] { clip-path: inset(0% 0% 100% 0%); }
/* in flight it is a Safety Orange surface, so it takes graphite type:
   this section's one orange thing, and only while it is moving */
html.js:not(.rm) [data-cross] .part.is-flight h3,
html.js:not(.rm) [data-cross] .part.is-flight p { color: var(--graphite); }
/* Seated: ours, and the ruled edge that separates it from the next.
   The type crosses on the SAME 0.12s as the block's Safety Orange to
   Ink tween, because a one frame swap here would put Spec Paper type
   on a still orange surface for the length of that tween, and orange
   surfaces take graphite type, never white. Declaring the transition
   on the seated state only keeps the swap INTO flight instant, which
   is where a one frame change is the right law. */
html.js:not(.rm) [data-cross] .part.is-seated { box-shadow: inset 0 -1px 0 var(--ink-rule); }
/* ...except the last one, which never closes: see .hand__ours in 14b */
html.js:not(.rm) .slot:last-child .part.is-seated { box-shadow: none; }
html.js:not(.rm) [data-cross] .part.is-seated h3,
html.js:not(.rm) [data-cross] .part.is-seated p { transition: color .12s var(--e-seat); }
html.js:not(.rm) [data-cross] .part.is-seated h3 { color: var(--paper); }
html.js:not(.rm) [data-cross] .part.is-seated p  { color: var(--ink-2); }
html.js:not(.rm) .hold--c2,
html.js:not(.rm) .hold--pay { opacity: 0; visibility: hidden; }
html.js:not(.rm) [data-stamp] { opacity: 0; }

@keyframes grain {
  0%   { transform: translate3d(0, 0, 0); }
  12%  { transform: translate3d(-3%, -2%, 0); }
  24%  { transform: translate3d(2%, -4%, 0); }
  36%  { transform: translate3d(-4%, 3%, 0); }
  48%  { transform: translate3d(3%, 4%, 0); }
  60%  { transform: translate3d(-2%, -3%, 0); }
  72%  { transform: translate3d(4%, 2%, 0); }
  84%  { transform: translate3d(-3%, 4%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}
.grain { animation: grain 1.1s steps(1, end) infinite; }

/* ---------- 18. REDUCED MOTION ------------------------------- */
/* No pinning, no scrub, no wipes and NO THROW. The page loads already
   on: `lights-on` is set before first paint and `lights-dark` never is,
   so this build never sees the dark state at all. The switch is
   rendered once, static, rocker up, room lit. */
html.rm .stage,
html:not(.js) .stage { height: auto; }
html.rm .stage__hold,
html:not(.js) .stage__hold { position: static; height: auto; overflow: visible; }
/* the chapters become ordinary blocks, but each keeps its own
   containing block so its scrim cannot escape over the whole stage */
html.rm .hold,
html:not(.js) .hold { position: relative; inset: auto; display: block; padding: 96px 0; }
html.rm .hold--hero,
html:not(.js) .hold--hero { min-height: 100vh; padding-top: 180px; }
/* The payoff panel has no night to fall on when there is no timeline,
   so it takes Ink outright and becomes what it is on the live build
   anyway: one sentence on a dark band, between the hero and the quiet
   shift. No asset, no gradient, nothing to fetch. */
html.rm .hold--pay,
html:not(.js) .hold--pay { background: var(--ink); }
/* Chapter 02 on the still builds is the AUTHORED NIGHT FRAME, not a
   black rectangle. Motion resolves to the static asset, and the quiet
   shift's static asset exists. Fetched only by the builds that show
   it, for the same reason as the lit one. */
html.rm .hold--c2,
html:not(.js) .hold--c2 {
  background: var(--ink) url("../assets/hero/hero-night-1440x900.webp") center center / cover no-repeat;
}
@media (orientation: portrait) {
  html.rm .hold--c2,
  html:not(.js) .hold--c2 {
    background-image: url("../assets/hero/hero-night-1080x1920.webp");
  }
}
/* `left`, `right` and `bottom` are RESET, not just `position`. The
   shelf label is `position: absolute; left: 44px` in the hero, and
   `html.rm .shelf` outranks the phone rule that hides it, so on a
   reduced-motion phone it came back as a relatively positioned block
   still carrying a 44px offset - which pushed it 4px past the right
   edge and put horizontal overflow on the page from 320 to 414.
   Caught by the 320-1920 sweep in the driver. */
html.rm .shelf,
html:not(.js) .shelf {
  position: relative; z-index: 2;
  left: auto; right: auto; bottom: auto; top: auto;
  width: var(--wrap); margin: 48px auto 0; display: block;
}
html.rm .marquee__row { transform: none !important; }
html.rm .how__display { transform: none !important; }
/* the closing dot is a colour rule, not a motion one: on by default */
html.rm .closer .closer__disc {
  background: radial-gradient(circle at 36% 30%, #FF8348 0%, #FF5C1A 46%, #E24A0E 78%, #CF430C 100%);
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
}

/* ---------- 19. RESPONSIVE ----------------------------------- */
@media (max-width: 1180px) {
  .about { grid-template-columns: 1fr; gap: 40px; }
}

@media (max-width: 900px) {
  :root { --wrap: min(1240px, 100% - 40px); --mast-h: 60px; }
  body { font-size: 16px; }

  .mast { padding: 0 16px; gap: 10px; }
  /* THE BAR'S WIDTH BUDGET ON A 390px LINE. It carries the mark, the
     menu and the CTA, and that is already 362 of 390. The sound toggle
     is the one that comes out: it is a generated tone behind a user
     gesture, it is off by default, and on a coarse pointer it is the
     least useful control in the bar. Measured at 390: 16 + 126 mark +
     auto + 34 menu + 10 + 150 CTA + 16 = 352. */
  .mast__sound { display: none; }
  .mast__menubtn { padding: 8px 8px; }
  .mast__menulabel {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .mast__menupanel { min-width: 216px; }
  .mast__menupanel a { padding: 14px 16px; }
  /* The bar carries the mark, the sound toggle and the CTA on a 390px
     line. The sound toggle's LABEL comes out and its three bars stay,
     which is what the control actually reads as at this size; the
     words are still in the markup and still the button's accessible
     name, they are only taken off the screen. Measured at 390: 16 +
     134 mark + 12 + 22 bars + 12 + 150 CTA + 16 = 362. */
  .mast__soundlabel {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  /* the masthead sits on a band of the current ground, never floating on
     the render: on narrow screens the plate rides right up under it */
  .mast { background: var(--paper); border-bottom-color: var(--zinc); }
  html.lights-dark .mast { background: var(--ink); border-bottom-color: var(--ink-rule); }

  .stage { height: 210vh; }

  /* REAL MOBILE COMPOSITION, REBUILT RATHER THAN CROPPED, and it is
     the approved 9:16 the other way up from the build this replaces.
     BUILD-SPEC-A section 1 puts the still life in the LOWER TWO THIRDS
     with the top third open for type, and the authored portrait
     fallback is drawn that way; the previous mobile hero did the
     opposite - one object riding high with the copy under it - which
     was correct for one object and cannot hold ten. Ten objects need
     the deep half of the frame, and the switch has to be one of the
     things you can see, not the one thing under the copy band.

     So the copy sits at the TOP on a band of whatever the ground
     currently is, and the desk runs from the middle of the screen to
     the bottom edge. Both bands are declared and only the state class
     picks between them, so nothing cross-fades. */
  .hold { align-items: flex-start; padding-top: calc(var(--mast-h) + 20px); }
  .hold--hero::before {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(180deg,
      var(--paper) 0%,
      var(--paper) 52%,
      rgba(244,242,237,.55) 56%,
      rgba(244,242,237,0) 61%);
  }
  html.lights-dark .hold--hero::before {
    background: linear-gradient(180deg,
      var(--ink) 0%,
      var(--ink) 52%,
      rgba(20,24,26,.55) 56%,
      rgba(20,24,26,0) 61%);
  }
  /* THE STILL BUILDS ON A PHONE. With no timeline there is nothing to
     cross-fade, so the still cannot sit BEHIND the copy the way the
     live room does: full width, the copy would print straight over the
     objects. It becomes a block in normal flow instead - the desk on
     top, the copy under it - anchored to its own bottom edge, because
     the approved 9:16 puts the still life in the lower two thirds. */
  /* 40vh, not 54vh. With the script off the still is a block in normal
     flow ABOVE the copy, and at 54vh it pushed the offer and the
     button off a 390 x 844 screen - which is the one thing the first
     screen may not do, in any build. The composition is anchored to
     its own bottom edge, so a shorter block crops the empty paper at
     the top and keeps the desk. */
  html.rm .scene,
  html:not(.js) .scene {
    position: relative; top: auto; left: auto; right: auto;
    height: 40vh; margin-top: var(--mast-h);
  }
  html.rm .scene > .still,
  html:not(.js) .scene > .still { background-position: center bottom; }
  html.rm .hold--hero,
  html:not(.js) .hold--hero { min-height: 0; padding-top: 28px; }
  html.rm .hold--hero::before,
  html:not(.js) .hold--hero::before { display: none; }

  /* THE 390px FIRST SCREEN. Four things, and they all have to be above
     the fold: the name, the line, the offer, the button. The hero
     button used to be `display: none` here on the grounds that the
     masthead pill was always on screen; the pill is 11px of chrome and
     it is not the button a reader looks for at the end of the offer,
     so it is back, at full width. */
  .hero__title { font-size: clamp(1.9rem, 7.6vw, 2.5rem); margin-bottom: 14px; max-width: 100%; }
  .hero__eyebrow { margin-bottom: 10px; font-size: 10.5px; }
  .hero__offer { font-size: 14.5px; line-height: 1.45; padding-left: 11px; max-width: 100%; }
  .hero__cta { margin-top: 20px; }
  .hero__cta .btn { width: 100%; justify-content: center; }
  /* the mobile hero is a rebuilt 9:16 composition and the shelf label
     does not have a corner to stand in under it */
  .shelf--bl { display: none; }

  .chap__title { font-size: clamp(2rem, 8vw, 2.8rem); margin: 16px 0 20px; }
  .clock li { grid-template-columns: 88px 1fr; gap: 12px; padding: 11px 0; }
  .clock__x { font-size: 15px; }
  .chap__payoff { margin-top: 24px; font-size: clamp(1.6rem, 7vw, 2.2rem); }
  /* alone on the screen, so it can be the biggest thing on it - and it
     SETS IN THE MIDDLE of a phone screen rather than in the copy band
     the chapters share at the top. The approved 9:16 keeps the still
     life in the lower two thirds, so the middle of the frame here is
     open dark with nothing in it, which is the only place one sentence
     has ever wanted to stand. */
  .hold--pay { align-items: center; padding-top: var(--mast-h); }
  .hold--pay .chap__payoff { font-size: clamp(1.9rem, 8.4vw, 2.8rem); max-width: 13ch; }

  .section--how  { padding: calc(var(--mast-h) + 24px) 0 96px; }
  .section--about { padding: calc(var(--mast-h) + 24px) 0 104px; }
  .switchcap { font-size: 10px; }
  .marquee { margin-bottom: 72px; }
  .how__head { margin-bottom: 56px; }

  /* THE HANDOFF ON A PHONE. The columns stay equal - the crossing is
     only legible if the thing on the reader's side occupies the whole
     of the reader's side - so the type steps down instead. The notch
     becomes a tall narrow field rather than a wide one, which if
     anything reads harder: a column of nothing, ruled, labelled, with
     00 at the bottom of it. The terms band loses its second column. */
  .hand { --pad: 13px; }   /* --foot's clamp already floors at 74 here */
  .hand__label { padding: 11px 0 10px; font-size: 10.5px; letter-spacing: .07em; }
  /* re-stated after the shorthand above, which would otherwise flatten
     the ink label back against the divide */
  .hand__label--ours { padding-left: var(--pad); padding-right: var(--pad); }
  .hand__count { font-size: clamp(2.25rem, 11.6vw, 3.25rem); margin-bottom: 11px; }
  .hand__count::before { width: 56px; margin-bottom: 11px; }
  .part { padding: 13px var(--pad) 15px; }
  .part h3 { font-size: 0.9375rem; line-height: 1.2; margin-bottom: 7px; }
  .part p { font-size: 12.5px; line-height: 1.45; }
  /* The 84px of clear ink that used to sit under the last line was
     there for a pill fixed in this corner to stand on. The pill is in
     the masthead now (section 8), so the form closes on its own last
     line like any other block. */
  .hand__terms { padding: 24px 20px 28px; }
  .hand__termslabel { margin-bottom: 14px; }
  /* one column, so the explicit desktop placement and the column rule
     both come out and the seven run in order */
  .hand__lines { grid-template-columns: 1fr; grid-template-rows: none; }
  .hand__lines li { font-size: 14.5px; padding: 13px 0 14px; }

  /* the business blocks go two up, the sentence drops to three lines */
  .svc { padding: calc(var(--mast-h) + 40px) 0 88px; }
  .svc__row { grid-template-columns: repeat(2, 1fr); }
  .svc__biz { padding: 16px; }
  /* the rows wrap on a phone, so the eight branches are no longer
     naturally the same height. The floor is the tallest of the eight;
     a shorter branch simply leaves the space rather than moving the
     page under the reader. */
  .svc__branches { margin-top: 24px; min-height: 24rem; }
  .svc__foot { gap: 16px; margin-top: 20px; align-items: stretch; flex-direction: column; }
  .svc__foot .btn { justify-content: center; }

  /* THE JOBS ON A PHONE. The row stacks: the job, then the Part under
     it, so neither is squeezed and all of them are still on one
     screen. */
  .job { grid-template-columns: 1fr; gap: 7px; padding: 13px 0; align-items: start; }
  .job__do { font-size: 1.0625rem; line-height: 1.25; }
  .job__part { justify-self: start; padding: 5px 9px; font-size: 10px; }

  .foot { padding: 96px 0 48px; }
  .foot__bottom { padding-bottom: 56px; }
  .foot__cta { margin-bottom: 72px; }
  .closer { --cap: clamp(84px, 26vw, 140px); margin-bottom: 20px; }
  .closer__cap { margin-bottom: 40px; }
  .foot__tag { margin-bottom: 48px; }
  .btn--magnet { padding: 18px 26px; transform: none !important; }

  /* one column, and the block loses its side padding rather than its
     fields: a form squeezed narrower than its own labels is worse
     than a form with less margin around it */
  .enq { margin-bottom: 64px; }
  .enq__lede { margin-bottom: 24px; }
  .enq__form { padding: 22px 18px 20px; }
  .enq__grid { grid-template-columns: 1fr; gap: 18px; }
  .enq__send { gap: 16px; margin-top: 24px; }
  .enq__btn { width: 100%; justify-content: center; }
  .enq__note { max-width: none; }
  .enq__done { padding: 26px 18px 24px; }

  .sticky-cta { padding: 9px 13px 9px 11px; font-size: 11px; gap: 8px; }
}

/* below an iPhone SE the bar runs out of line before the wordmark
   does, and a wrapped wordmark is taller than the bar */
@media (max-width: 374px) {
  .mast { padding: 0 12px; gap: 8px; }
  .mast__name { white-space: nowrap; }
  .sticky-cta { padding: 9px 11px 9px 9px; font-size: 10.5px; gap: 7px; }
}

/* Short desktop viewports: the hero has to stay inside the first fold */
@media (max-height: 900px) and (min-width: 901px) {
  .hero__title { font-size: clamp(2.4rem, 4.4vw, 3.9rem); margin-bottom: 20px; }
  .hero__offer { font-size: 1.0625rem; }
  .hero__cta { margin-top: 22px; }
  .chap__title { font-size: clamp(2.2rem, 4.2vw, 3.6rem); }
  .chap__payoff { font-size: clamp(1.8rem, 3vw, 2.6rem); margin-top: 28px; }
  .clock li { padding: 11px 0; }
  .shelf--bl { bottom: 26px; }
  /* the sentence, the ledger and the tally still have to be one screen */
  .svc { padding: calc(var(--mast-h) + 16px) 0 72px; }
  .svc__head { margin-bottom: 16px; }
  .svc__display { font-size: clamp(2.2rem, 4.4vw, 3.9rem); }
  .job { padding: 13px 0; }
  .job__do { font-size: clamp(1.0625rem, 1.6vw, 1.375rem); }
}
