/* ============================================================================
   AI CHAUFFEUR · SHARED PAGE SHELL          RUN 9 — "SIGNAL CUT v1.5"
   ----------------------------------------------------------------------------
   Loaded by ELEVEN of the twelve chauffeur pages. The one exception is
   /index.html, which carries its own embedded <style> because it holds console
   machinery these pages do not need.

   CORRECTED IN RUN 9: this header used to name /demo/ as a second exception. It
   is not one and has not been since that page was rebuilt call-first — /demo/
   links this file in its <head> like every other content page and adds a short
   page-scoped block on top. Anyone reading the old note would have edited this
   file believing /demo/ could not be affected by it, which is exactly the kind
   of comment that turns a shared stylesheet into a trap. Verified by reading
   the <head> of all twelve pages, not by trusting the note.

   This is deliberately the SUBSET the content pages need: tokens, nav, the CTA
   stack, section typography, FAQ, footer. It is not a copy of the homepage
   stylesheet.

   HOST NOTE — aichauffeur.ai is a SEPARATE Vercel project whose Root Directory
   is /chauffeur/. Every absolute path here and in the pages that load it is
   relative to THAT root, so /assets/... means chauffeur/assets/... A repo-root
   path such as /js/tracking.js 404s on this host.

   RADIUS NOTE — CIRCULANT-X specifies 0px corners on new surfaces. These pages
   share a header, footer and CTA stack with the existing chauffeur homepage,
   which is built on 10-18px radii throughout. Sharp corners on these pages
   alone would read as two different sites wearing the same logo. Matching the
   brand's live idiom; the corner law applies when the whole surface is rebuilt.
   ========================================================================== */

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}

/* ══ RUN 8 · FONT LOCK — SELF-HOSTED FACES ═══════════════════════════════════
   CANONICAL BLOCK. Byte-identical in all three CSS homes on this site:
   assets/circulant.css (all 12 pages), assets/aic.css (the 11 pages that load
   it) and the embedded <style> in index.html (which loads no aic.css). RUN 7
   shipped a site with two heads because one rule landed in only one copy;
   `node tools/run8_gate.js` asserts these three copies match character for
   character. Change one, change all three.

   THE FILES ARE GOOGLE'S FILES. chauffeur/fonts/space-grotesk.woff2 and
   jetbrains-mono.woff2 are SHA-256 IDENTICAL to the files Google's font CDN
   served for this site's exact request — verified, not assumed. (The hostname is
   deliberately not written out anywhere on this site: the gate greps for it, and
   a comment that quotes the banned string makes every future audit lie. RUN 7
   learned that one the hard way.) Nothing about the
   rendered face changes; only where it is fetched from. That is why no line
   break on the site moves, and why that claim is a measurement rather than a
   hope: a subset that differed by one glyph would re-wrap every page.

   unicode-range is Google's own declared latin range, VERBATIM. It is what
   decides which family paints a character, so reproducing it exactly keeps
   '→' (U+2192) and '✓' (U+2713) — present in NO subset of either family —
   falling through to the system stack exactly as they already do. Widen it and
   those two glyphs start coming from the fallback face at a different width.

   The Fallback faces are metric-matched so the swap moves zero pixels:
     Space Grotesk  <- Arial        size-adjust 108.9963%  (worst weight error 0.25%)
     JetBrains Mono <- Courier New  size-adjust  99.9837%
   Arial is the reference the brief names and it is right for the sans. It is
   wrong for the mono: Arial's advances span 72% of an em while JetBrains Mono's
   are every one 0.600, so an Arial-backed mono fallback re-flows every label the
   moment the real face lands — the exact defect this run exists to remove.
   Courier New measures 0.6001 em, a 0.02% match, and is monospace like the face
   it stands in for.
   ═══════════════════════════════════════════════════════════════════════════ */
@font-face{
  font-family:'Space Grotesk';
  font-style:normal;
  font-weight:300 700;
  font-display:swap;
  src:url('/fonts/space-grotesk.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Space Grotesk Fallback';
  font-style:normal;
  font-weight:300 700;
  src:local('Arial'),local('Helvetica Neue'),local('Liberation Sans');
  size-adjust:108.9963%;
  ascent-override:90.2783%;
  descent-override:26.7899%;
  line-gap-override:0%;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'JetBrains Mono';
  font-style:normal;
  font-weight:400 700;
  font-display:swap;
  src:url('/fonts/jetbrains-mono.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'JetBrains Mono Fallback';
  font-style:normal;
  font-weight:400 700;
  src:local('Courier New'),local('Liberation Mono'),local('Nimbus Mono PS');
  size-adjust:99.9837%;
  ascent-override:102.0166%;
  descent-override:30.0049%;
  line-gap-override:0%;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
/* ══ END RUN 8 · FONT LOCK ══════════════════════════════════════════════════ */

/* ══ SIGNAL v1.1 · TOKEN BLOCK ═══════════════════════════════════════════════
   CANONICAL BLOCK. Byte-identical in all three CSS homes on this site:
   assets/circulant.css (all 12 pages), assets/aic.css (the 11 pages that load
   it) and the embedded <style> in index.html (which loads no aic.css).
   `node tools/aic_signal_gate.js` asserts the three copies match character for
   character. Change one, change all three.

   Every ratio below is MEASURED against the surface named, not eyeballed.

     --ink              16.74:1 on midnight  ·  15.70:1 on surface
     --signal-blue       5.13:1 on midnight  ·   4.81:1 on surface
     --sky               9.10:1 on midnight  ·   8.54:1 on surface
     --success-green    12.18:1 on midnight  ·  11.42:1 on surface
     --amber            10.76:1 on midnight  ·  10.09:1 on surface
     --miss-red          5.61:1 on midnight  ·   5.26:1 on surface
     --neutral           6.38:1 on midnight  ·   5.98:1 on surface
     --ink-soft         10.21:1 on midnight  ·   9.78:1 on surface
     --ink-mute          5.47:1 on midnight  ·   5.38:1 on surface
     #FFFFFF on an --action-blue fill        6.25:1
     --action-blue as a control on midnight  3.15:1  (non-text, needs 3)

   RUN 10 adds a THIRD elevation and an input ground. Measured on those:

     --ink              14.82:1 on surface-2 ·  16.18:1 on input-bg
     --ink-soft          9.42:1 on surface-2 ·   9.98:1 on input-bg
     --ink-mute          5.28:1 on surface-2 ·   5.47:1 on input-bg
     --signal-blue       4.54:1 on surface-2 ·   4.96:1 on input-bg
     --neutral           5.65:1 on surface-2 ·   6.16:1 on input-bg
     #FFFFFF on --action-blue-hover          5.19:1
     --action-blue-hover as a control        3.79:1 on midnight

   CANVAS LAW. Page-depth treatment is allowed and REQUIRED: a wide horizon
   band of the blue at 3-5% alpha rising out of the lower third, a corner
   vignette around 6%, and a grid that is brighter at the top of the document
   than at the foot of it. That is a PAGE layer — painted once, below every
   section, naming no state. COMPONENT halos stay banned outright and § 4 GLOW
   LAW is unchanged: a card separates from the canvas with a hairline and a step
   of surface, never with light thrown outside its own box.

   THE GRID TOKENS CHANGED SHAPE, and the reason is a measurement rather than a
   preference. The line colour carried .05 alpha AND was multiplied by a zone
   opacity of .04, so a rendered page line was 0.002 alpha of blue over
   midnight: 61 x 0.002 + 7 x 0.998 = 7.1, which rounds to 7. Identical to the
   background, on every channel. Every page zone on this site was drawing a grid
   that could not produce one different pixel. The hue now lives at full
   strength in --grid-line and the ZONE sets the alpha, so the two numbers below
   are what actually paints. --grid-floor is the down-page multiplier: .05 at
   the top of the document, .05 x .40 = .02 at the bottom of it.

   ROLE, not decoration. A rule that wants a colour and cannot name the state it
   paints is decoration and does not ship.

     --signal-blue   live states, text links, focus rings, selected UI, console
                     activity, and at most ONE highlighted headline phrase per
                     section. It is a TEXT and LINE colour.
     --action-blue   filled CTA buttons only, always with white lettering. It is
                     a FILL colour and never sets type.
     --sky           secondary highlight and eyebrow accent.
     --success-green renders ONLY beside a label naming the action that
                     succeeded. Label text and colour change on the same frame.
                     A flow that merely ended is not a success.
     --amber         ringing · pending · the cost of the miss.
     --miss-red      failure only. Never "attention".
     --neutral       inactive · not-yet · n/a.

   --signal-blue is deliberately never used as a fill under small white type:
   white on it lands under the body floor. Filled controls take --action-blue,
   which measures 6.25:1 with white.

   On a light or print surface the blue is --action-blue. The lighter blue
   measures 3.61:1 on paper and is large-text only there.

   The artwork under /assets/brand/ is EXEMPT and keeps its own values. Those
   are supplied production files; a recolour run does not repaint a logo.
   ══════════════════════════════════════════════════════════════════════════ */
:root{
  /* ---- surfaces ---------------------------------------------------------- */
  --midnight:#070B14;
  --surface:#0D1420;
  --surface-2:#111A2A;
  --input-bg:#0A101C;
  --line:#1B2536;
  --line-hot:rgba(61,123,255,.42);

  /* ---- type -------------------------------------------------------------- */
  --ink:#E8EDF5;
  --ink-soft:rgba(232,237,245,.78);
  --ink-mute:rgba(232,237,245,.55);

  /* ---- the three blues --------------------------------------------------- */
  --signal-blue:#3D7BFF;
  --action-blue:#1E56D6;
  --action-blue-hover:#2A63E8;
  --sky:#7FB2FF;

  /* ---- semantic ---------------------------------------------------------- */
  --success-green:#2EE6A8;
  --amber:#FFB020;
  --miss-red:#FF3B4E;
  --neutral:#8A93A6;

  /* ---- state by role ----------------------------------------------------- */
  --state-ringing:var(--amber);
  --state-listening:var(--signal-blue);
  --state-captured:var(--success-green);
  --state-idle:var(--neutral);
  --state-fail:var(--miss-red);

  /* ---- grid · § B. The HUE is one value; the ZONE sets the alpha. -------- */
  --grid-line:#3D7BFF;
  --grid-cell:64px;
  --grid-page:.05;
  --grid-console:.08;
  --grid-floor:.40;

  /* ---- elevation · § C. ONE shadow, no colour in it, one per raised panel.  */
  --raise:0 8px 24px rgba(0,0,0,.45);

  /* ---- families ---------------------------------------------------------- */
  --sans:'Space Grotesk','Space Grotesk Fallback',sans-serif;
  --mono:'JetBrains Mono','JetBrains Mono Fallback',monospace;

  /* ---- back-compat aliases ----------------------------------------------- */
  /* Every name below predates Signal and is still referenced by shipped rules.
     They are ALIASES, not second definitions: the value lives once, above. */
  --void:var(--midnight);
  --void-2:var(--surface);
  --void-raise:var(--surface);
  --void-card:rgba(13,20,32,.62);
  --canvas:var(--midnight);
  --panel:var(--surface);
  --text:var(--ink);
  --matter:var(--ink);
  --dim:var(--ink-soft);
  --body-dim:var(--ink-soft);
  --ink-dim:var(--neutral);
  --booked-green:var(--success-green);
  --booked:var(--success-green);
  --green:var(--success-green);
  --alarm:var(--miss-red);
  --red:var(--miss-red);
  --danger:var(--miss-red);
  --cyan:var(--signal-blue);
  --cyan-bright:var(--sky);
  --cyan-active:var(--sky);
  --cyan-dim:rgba(61,123,255,.18);
  --line-strong:var(--line-hot);

  /* ---- panel recipe ------------------------------------------------------ */
  /* Flat. The hairline separates the panel from the void (panel-on-void is
     1.07:1 and invisible without it); the shadow is the only elevation cue and
     it carries no hue. */
  --gx-fill:#0D1420;
  --gx-solid:#0D1420;
  --gx-line:var(--line);
  --gx-line-hot:var(--line-hot);
  --gx-line-mid:#243044;
  --gx-blur:0px;
  --gx-edge:none;

  /* ---- light + print ----------------------------------------------------- */
  --paper:#F7F8FA;
  --paper-ink:#14161C;
  --blue-on-paper:var(--action-blue);
}
/* ══ END SIGNAL v1.1 · TOKEN BLOCK ═════════════════════════════════════════ */

/* ── SECTION LIGHTING ────────────────────────────────────────────────────────
   Background tint only, and deliberately near-invisible: the brightest of these
   lifts midnight by ~2% luminance, which keeps every measured text contrast in
   the block above intact. The rhythm is what separates one section from the
   next now that prose no longer sits in cards. Applied as a data attribute so
   the value a section carries names the mood it is supposed to have, and an
   editor cannot quietly reach for a colour that means something else. */
:root{
  --lit-bright:rgba(61,123,255,.050);   /* hero — brightest                    */
  --lit-technical:rgba(61,123,255,.028);/* dispatch flow — cool, instrumental  */
  --lit-calm:rgba(138,147,166,.030);    /* integrations — neutral, no advance  */
  --lit-connected:rgba(61,123,255,.020);/* stages — faint, the rail carries it */
  --lit-dense:rgba(0,0,0,.34);          /* operator pain — denser, not louder  */
  --lit-warm:rgba(255,176,32,.014);     /* operator story — a whisper under
                                           the Crush's own amber floor         */
  --lit-flat:transparent;               /* FAQ — near-flat                     */
}

html{scroll-behavior:smooth;-webkit-font-smoothing:antialiased;background:var(--midnight)}
body{background:var(--midnight);color:var(--ink-soft);font-family:var(--sans);font-size:16px;
  line-height:1.6;overflow-x:hidden;min-height:100vh}

/* ── § B · THE GRID ──────────────────────────────────────────────────────────
   Two ambient radial washes used to sit here. They were the page's only light
   source and they were cyan, so every surface on every page read as lit from
   somewhere. § C keeps ambient light in exactly two scenes, neither of which is
   a content page, so the washes are gone and the grid takes the job.

   The mask is the point. It fades the grid OUT through the middle 56% of the
   viewport, which is where the 560px prose measure lives, so no line ever runs
   behind a line of body copy. On a phone the whole width is prose, so the grid
   effectively stops existing there — which is also the cheapest thing it can do
   on the device that can least afford it. Cell drops to 48px (x0.75) below 768
   for the margins that do survive. */
body::after{content:'';position:absolute;inset:0;pointer-events:none;z-index:0;
  opacity:var(--grid-page);
  background-image:linear-gradient(var(--grid-line) 1px,transparent 1px),
                   linear-gradient(90deg,var(--grid-line) 1px,transparent 1px);
  background-size:var(--grid-cell) var(--grid-cell);
  mask-image:linear-gradient(90deg,#000 0%,transparent 22%,transparent 78%,#000 100%);
  -webkit-mask-image:linear-gradient(90deg,#000 0%,transparent 22%,transparent 78%,#000 100%)}
/* RUN 10 · DEPTH. A second mask layer fades the grid down the DOCUMENT, from
   --grid-page at the top to --grid-page x --grid-floor at the foot. It is
   guarded because the fallback matters: with two mask layers and the default
   `add` compositing the result is the UNION of the two, which would put grid
   lines straight back behind the prose measure the horizontal mask exists to
   protect. Inside the guard the masks INTERSECT and both laws hold; outside it,
   the single horizontal mask above is what ships and only the depth is lost.
   Never let a progressive enhancement fail into a rule violation. */
@supports (mask-composite: intersect){
  body::after{
    mask-image:linear-gradient(90deg,#000 0%,transparent 22%,transparent 78%,#000 100%),
               linear-gradient(180deg,#000 0%,#000 9%,rgba(0,0,0,var(--grid-floor)) 62%,rgba(0,0,0,var(--grid-floor)) 100%);
    -webkit-mask-image:linear-gradient(90deg,#000 0%,transparent 22%,transparent 78%,#000 100%),
               linear-gradient(180deg,#000 0%,#000 9%,rgba(0,0,0,var(--grid-floor)) 62%,rgba(0,0,0,var(--grid-floor)) 100%);
    mask-composite:intersect;-webkit-mask-composite:source-in}
}
@media(max-width:767.98px){body::after{--grid-cell:48px}}

a{color:var(--signal-blue);text-decoration:none;transition:color .15s}
a:hover{color:var(--sky)}
::selection{background:rgba(61,123,255,.35);color:var(--ink)}

.wrap{max-width:1180px;margin:0 auto;padding:0 1.5rem;position:relative;z-index:1}
.skip{position:absolute;left:-9999px}
.skip:focus{left:1rem;top:1rem;z-index:99;background:var(--action-blue);color:#FFFFFF;
  padding:.75rem 1rem;border-radius:8px;font-weight:500}

/* ── NAV ─────────────────────────────────────────────────────────────────── */
nav.top{position:fixed;top:0;left:0;right:0;z-index:50;padding:14px 1.5rem;display:flex;
  align-items:center;gap:1.25rem;background:rgba(7,11,20,.86);
  backdrop-filter:blur(20px) saturate(1.4);-webkit-backdrop-filter:blur(20px) saturate(1.4);
  border-bottom:1px solid var(--line);transition:background .2s}
/* AIC-BRAND-INSTALL · the nav carries the real lockup now, not a car glyph in a
   gradient chip. The wordmark is INSIDE lockup-short.svg, so the anchor holds no
   text — alt carries the name. min-height 44: it is a link to the homepage.
   Kit minimum for the short lockup is 120px wide; at 30px tall it renders 176px.

   AIC RUN 5 · the compact mark used to take over below 420px, which meant the
   brand NAME was absent from the header on every phone — on a phone-first
   audience that is the majority of sessions, and a bare bar mark identifies
   nothing to a first-time visitor. The lockup is SCALED instead of dropped:
   the 446.1x76 viewBox makes 25px tall render 146.8px wide, comfortably over
   the kit's 120px minimum and inside the 140-150px target. Measured at 360px
   (the narrowest viewport that still carries it) the lockup and the phone CTA
   occupy 292px of 312px available, so the bar still does not wrap or overflow.
   Below 360px there is no longer room for both, and the number wins — it is
   the conversion — so mark-compact (kit min 16px, used at 26px) takes over. */
/* min-width 44 as well as min-height: in the compact state the anchor wraps a
   26px mark, which is a 26px-wide tap target without it. */
.brand{display:flex;align-items:center;min-height:44px;min-width:44px}
.brand-lockup{display:block;height:30px;width:auto}
.brand-compact{display:none;height:26px;width:26px}
@media (max-width:600px){
  .brand-lockup{height:23px}
}
@media (max-width:359.98px){
  .brand-lockup{display:none}
  .brand-compact{display:block}
}
/* ══ § G · THE NAV LAYER ═════════════════════════════════════════════════════
   SHARED BLOCK. Byte-identical in assets/aic.css and the embedded <style> in
   index.html. Two heads on one site is the defect RUN 7 shipped and RUN 8
   chased; every rule the header and footer need lives in this one block so a
   change cannot land in only one copy.

   Every link in here is a real <a href> in the served HTML — nothing is
   injected, nothing waits for JS, and the closed state uses opacity plus
   visibility rather than display:none, so the markup a crawler reads is the
   markup a person gets. JS adds keyboard behaviour on top; it is not what makes
   the links exist.

   BREAKPOINT. The desktop groups appear at 1024px, not at the old 880px. Three
   group triggers, a call chip and a filled primary need about 880px of bar on
   their own, and at 880 exactly they were touching the lockup. Below 1024 the
   whole thing becomes one full-height drawer.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the bar ────────────────────────────────────────────────────────────────
   `gap` plus `margin-right:auto` on the group list, rather than
   space-between: with five children space-between spread them evenly and the
   two right-hand controls drifted apart from each other. */
.nav-main{display:none;align-items:center;gap:.15rem;margin:0 auto 0 .5rem;
  padding:0;list-style:none}
.nav-right{display:flex;align-items:center;gap:.5rem;margin-left:auto;flex:none}
@media(min-width:1024px){.nav-main{display:flex}}
/* flex:none on BOTH ends of the bar. Without it the lockup is the only
   shrinkable child, so when the bar ran out of room at 390 it did not overflow
   — it squeezed the mark and clipped the wordmark to "AI CHAUFFEU" under the
   call chip. The overflow probe read clean the whole time, because nothing
   overflowed: the brand just got smaller. A bar that cannot fit should fail
   loudly at the document edge where a gate can see it. */
.brand{flex:none}

/* ── group trigger + panel ─────────────────────────────────────────────────── */
.nav-group{position:relative}
/* invisible hover bridge across the 12px gap between trigger and panel, so the
   pointer does not fall through the gap and close the menu on the way down */
.nav-group::before{content:'';position:absolute;top:100%;left:0;right:0;height:12px}
.nav-trigger{display:inline-flex;align-items:center;gap:.4rem;min-height:44px;
  padding:0 .7rem;background:none;border:0;border-radius:8px;cursor:pointer;
  font-family:var(--sans);font-size:.9375rem;font-weight:500;letter-spacing:.005em;
  color:var(--ink-mute);white-space:nowrap;transition:color .15s}
.nav-group:hover .nav-trigger,
.nav-group:focus-within .nav-trigger,
.nav-trigger[aria-expanded="true"]{color:var(--ink)}
.nav-trigger svg{width:10px;height:10px;flex:none;transition:transform .18s ease}
.nav-trigger[aria-expanded="true"] svg{transform:rotate(180deg)}
.nav-trigger:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}

.nav-panel{position:absolute;top:calc(100% + 12px);left:0;min-width:274px;margin:0;
  padding:.4rem;list-style:none;background:var(--surface);
  border:1px solid var(--line);border-radius:12px;box-shadow:var(--raise);
  opacity:0;visibility:hidden;transform:translateY(-4px);
  transition:opacity .16s ease,transform .16s ease,visibility 0s linear .16s}
.nav-group:hover .nav-panel,
.nav-group:focus-within .nav-panel,
.nav-group.is-open .nav-panel{opacity:1;visibility:visible;transform:translateY(0);
  transition-delay:0s,0s,0s}
.nav-panel a{display:flex;align-items:center;min-height:44px;padding:.5rem .7rem;
  border-radius:8px;font-family:var(--sans);font-size:.9375rem;font-weight:500;
  color:var(--ink);transition:background .12s,color .12s}
.nav-panel a:hover{background:rgba(61,123,255,.10);color:var(--sky)}
.nav-panel a[aria-current="page"]{color:var(--sky)}
.nav-panel a:focus-visible{outline:2px solid var(--signal-blue);outline-offset:-2px}

/* ── the two right-hand controls ─────────────────────────────────────────────
   ONE PRIMARY PER VIEWPORT. On desktop the filled control is "Book the setup
   call" and the number is a line button beside it. Below 1024 the book button
   moves into the drawer, so the number is the only control left in the bar and
   it becomes the filled one. The rule is not "the phone is secondary" — it is
   that exactly one thing in the header is filled at any width. */
.nav-cta{display:inline-flex;align-items:center;gap:.45rem;min-height:44px;
  padding:0 .85rem;border-radius:8px;border:1px solid var(--line);
  background:var(--surface);color:var(--ink);
  font-family:var(--sans);font-size:.9375rem;font-weight:500;letter-spacing:.005em;
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1;
  white-space:nowrap;transition:border-color .15s,color .15s,transform .15s}
.nav-cta svg{width:14px;height:14px;flex-shrink:0}
.nav-cta:hover{color:var(--sky);border-color:var(--sky);transform:translateY(-1px)}
.nav-cta:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}

.nav-book{display:none;align-items:center;min-height:44px;padding:0 1rem;
  border-radius:8px;background:var(--action-blue);color:#FFFFFF;
  box-shadow:inset 0 1px 0 rgba(127,178,255,.35);
  font-family:var(--sans);font-size:.9375rem;font-weight:500;letter-spacing:.005em;
  white-space:nowrap;transition:transform .15s,filter .15s}
.nav-book:hover{color:#FFFFFF;transform:translateY(-1px);filter:brightness(1.08)}
.nav-book:active{transform:translateY(0);filter:brightness(.96)}
.nav-book:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
@media(min-width:1024px){
  .nav-book{display:inline-flex}
  .nav-cta{background:var(--surface)}
  /* ── RUN 13 · A "DEFECT" THAT WAS THE SEPARATOR ─────────────────────────
     NOT CHANGED, and the reasoning is worth keeping so nobody re-opens it.

     `Call ` is its own span so it can drop below 1024, which also makes it its
     own FLEX ITEM. The container `gap` therefore spaces it from the DIGITS as
     well as from the glyph, and RUN 13 measured that on production — verb
     right edge 1095, digits left edge 1102 — and read the 7px as a hole sitting
     on top of the space already inside the span.

     It is not. TRAILING WHITESPACE INSIDE A FLEX ITEM IS TRIMMED. The span's
     own trailing space renders as nothing, so the 7.2px gap is the ONLY thing
     separating the word from the number. Removing it shipped `Call(414)`,
     which the shot caught before it left the machine.

     The rail does this properly, and the difference is one element:
     `.rail-label` wraps the verb and the digits so they are inline siblings
     INSIDE one flex item, where a trailing space is ordinary text and renders
     as an ordinary word space. The chip could take the same wrapper, and that
     is a markup change across sixteen pages for roughly 3px of tracking —
     a real but small improvement, and a job for a run that is doing chrome. */
}
@media(max-width:1023.98px){
  .nav-cta{background:var(--action-blue);color:#FFFFFF;border-color:transparent;
    box-shadow:inset 0 1px 0 rgba(127,178,255,.35)}
  .nav-cta:hover{color:#FFFFFF;border-color:transparent;filter:brightness(1.08)}
}

/* ── the drawer trigger ─────────────────────────────────────────────────────
   44x44 and it carries visible text ("Menu") down to 400px, where the bar runs
   out of room and the glyph carries it alone. aria-label is only present in the
   icon-only state, and where both exist the label CONTAINS the visible word —
   a label that replaces the visible text breaks voice control. */
.nav-burger{display:inline-flex;align-items:center;justify-content:center;gap:.45rem;
  min-height:44px;min-width:44px;padding:0 .7rem;flex:none;cursor:pointer;
  background:var(--surface);border:1px solid var(--line);border-radius:8px;color:var(--ink);
  font-family:var(--sans);font-size:.9375rem;font-weight:500;letter-spacing:.005em;
  transition:border-color .15s,color .15s}
.nav-burger:hover{color:var(--sky);border-color:var(--sky)}
.nav-burger:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
.nav-burger svg{width:18px;height:18px;flex:none}
@media(min-width:1024px){.nav-burger{display:none}}
@media(max-width:400px){.nav-burger{padding:0;min-width:44px}.nav-burger .nav-burger-t{display:none}}

/* ── the drawer ─────────────────────────────────────────────────────────────
   One full-height sheet carrying EVERY indexable page. visibility, not
   display:none, so the links are in the DOM at all times; visibility also takes
   the closed sheet out of the tab order, which display:none would do but a
   transform alone would not. */
.nav-drawer{position:fixed;inset:0;z-index:70;display:flex;flex-direction:column;
  background:var(--midnight);overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .2s ease,transform .2s ease,visibility 0s linear .2s}
.nav-drawer.is-open{opacity:1;visibility:visible;transform:none;
  transition-delay:0s,0s,0s}
@media(min-width:1024px){.nav-drawer{display:none}}
body.drawer-open{overflow:hidden}

.dw-top{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:8px 1rem;min-height:60px;border-bottom:1px solid var(--line);flex:none}
.dw-close{display:inline-flex;align-items:center;justify-content:center;gap:.45rem;
  min-height:44px;min-width:44px;padding:0 .7rem;cursor:pointer;
  background:var(--surface);border:1px solid var(--line);border-radius:8px;color:var(--ink);
  font-family:var(--sans);font-size:.9375rem;font-weight:500}
.dw-close:hover{color:var(--sky);border-color:var(--sky)}
.dw-close:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
.dw-close svg{width:16px;height:16px;flex:none}

.dw-body{padding:1.25rem 1rem 2rem;display:flex;flex-direction:column;gap:1.75rem}
.dw-group h2{font-family:var(--mono);font-size:.75rem;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--neutral);margin:0 0 .35rem}
.dw-group ul{list-style:none;margin:0;padding:0;border-top:1px solid var(--line)}
.dw-group li{border-bottom:1px solid var(--line)}
.dw-group a{display:flex;align-items:center;min-height:52px;
  font-family:var(--sans);font-size:1.0625rem;font-weight:500;color:var(--ink)}
.dw-group a:hover{color:var(--sky)}
.dw-group a[aria-current="page"]{color:var(--sky)}
.dw-actions{display:flex;flex-direction:column;gap:.75rem}
.dw-actions .nav-book{display:inline-flex;justify-content:center;min-height:52px;font-size:1rem}
.dw-actions .nav-cta{justify-content:center;min-height:52px;font-size:1rem;
  background:var(--surface);color:var(--ink);border-color:var(--line);box-shadow:none}
.dw-actions .nav-cta:hover{color:var(--sky);border-color:var(--sky);filter:none}

@media (prefers-reduced-motion:reduce){
  .nav-panel,.nav-drawer{transition:visibility 0s linear .01s}
  .nav-trigger svg{transition:none}
  .nav-cta:hover,.nav-book:hover,.nav-burger:hover{transform:none}
}

/* ── § G · BREADCRUMBS ─────────────────────────────────────────────────────── */
.crumbs{margin:0 0 1.1rem}
.crumbs ol{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem;margin:0;padding:0;
  list-style:none;font-family:var(--mono);font-size:.75rem;letter-spacing:.08em;
  text-transform:uppercase}
.crumbs li{display:inline-flex;align-items:center}
.crumbs li+li::before{content:'/';color:var(--neutral);margin-right:.4rem}
.crumbs a{color:var(--neutral);text-decoration:none}
.crumbs a:hover{color:var(--sky);text-decoration:underline;text-underline-offset:.2em}
.crumbs [aria-current="page"]{color:var(--ink-mute)}

/* ── § G · THE FAT FOOTER ───────────────────────────────────────────────────
   Four columns plus the brand block. This is the only place on the site where
   every indexable page is reachable from every other one, which is what closes
   the orphan gap: before this run /airport-transfer-booking/ and
   /madison-limo-answering-service/ each had ZERO incoming internal links. */
/* MEASURED. The first cut put the brand block in the same row as the four
   columns at 1.5fr + 4x1fr. At 1024 that left each column about 150px against a
   185px label and four of them wrapped onto two lines — visible in the gate at
   exactly one viewport band and nowhere else. The brand block takes its own row
   from 900 up, which hands the columns the full width, and the links sit at
   14px because they are secondary navigation, not body copy. Measured after the
   change: the longest label runs 172px into 195px at 900, the tightest point. */
.foot-grid{display:grid;grid-template-columns:1fr;gap:2rem 1.5rem;align-items:start}
@media(min-width:600px){.foot-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(min-width:900px){
  .foot-grid{grid-template-columns:repeat(4,minmax(0,1fr))}
  .foot-brand{grid-column:1/-1;padding-bottom:1.25rem;border-bottom:1px solid var(--line)}
}
.foot-col h2{font-family:var(--mono);font-size:.75rem;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--neutral);margin:0 0 .25rem}
.foot-col ul{list-style:none;margin:0;padding:0}
.foot-col a{display:inline-flex;align-items:center;min-height:44px;
  font-family:var(--sans);font-size:.875rem;font-weight:400;color:var(--ink-mute);
  transition:color .15s}
.foot-col a:hover{color:var(--sky)}
.foot-col a[aria-current="page"]{color:var(--sky)}

/* ── MOBILE HEADER, STILL <=64px ─────────────────────────────────────────────
   8 + 44 + 8 = 60px plus the 1px border. The height comes out of the PADDING,
   never out of the 44px tap targets, and adding the drawer trigger did not
   change that: the bar holds the lockup, the call chip and a 44px button, and
   the two labels give up tracking rather than type size as the viewport
   narrows. 12px is the floor and nothing here goes under it. */
/* MEASURED AT 360/375/390/430. Three controls have to share a 328px row at the
   narrowest supported width. What gave was the word "Call" in front of the
   number, not the number and not the wordmark: the chip is a filled blue button
   with a phone glyph on a tel: link, so the verb is carried by the affordance,
   and aria-label keeps the full "Call (414) 775-0019" as the accessible name —
   which still CONTAINS the visible text, so voice control matches on it.
   The lockup goes to 23px tall = 135px wide from the 446.1x76 viewBox, over the
   kit's 120px minimum for the short lockup, so the brand NAME stays in the
   header on every phone. That was a deliberate RUN 5 decision and this run does
   not undo it. */
@media (max-width:1023.98px){
  nav.top{padding:8px 1rem;gap:.5rem}
  .nav-cta{padding:0 .7rem;font-size:.875rem}
  .nav-cta svg{width:13px;height:13px}
  .nav-cta-verb{display:none}
}
/* 21px tall renders the short lockup at 123.3px from its 446.1x76 viewBox —
   over the kit's 120px floor, and the 12px it gives back is what puts the whole
   row inside the nav's content box at 360 instead of 6px past it. Measured:
   123 + 8 + 147 + 8 + 44 = 330 into 336 available. */
@media (max-width:400px){
  nav.top{padding:8px .75rem}
  .nav-cta{padding:0 .55rem;gap:.35rem}
  .brand-lockup{height:21px}
}

/* ── PAGE HEAD ───────────────────────────────────────────────────────────── */
/* RUN 7 · the mobile top pad tracked a 72px header. The header is 60px now, so
   this comes down with it rather than leaving 12px of dead air above the H1. */
.phead{padding:140px 0 40px}
@media(max-width:600px){.phead{padding:96px 0 32px}}
/* ── § C/D · THE KICKER, DE-PILLED ───────────────────────────────────────────
   This was a filled pill with a hairline border and a dot that pulsed forever.
   Three problems in one 40px object: § 3 bans pills used as ornament, the dot
   named no state (nothing on a content page is ringing, live, or captured), and
   an infinite animation ran whether or not it was on screen.

   The brief asked for a reduced-motion guard and an offscreen pause on that
   pulse. Removing the dot is the stronger version of the same fix — a pulse
   that does not exist cannot run offscreen and cannot fight reduced motion —
   and it leaves the element doing the one job it is good at: an eyebrow. Mono,
   because § D gives eyebrows to the mono face, and --sky, because § A gives
   eyebrow accents to --sky. Pulses that DO name a state are kept, guarded, in
   the two scenes § C allows. Size stays at 12px, the floor. */
.kicker{display:inline-block;font-family:var(--mono);font-size:.75rem;
  font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--sky);
  margin-bottom:1.75rem}
/* ── RUN 7 · TASK E — TYPE SCALE ─────────────────────────────────────────────
   Measured targets, not vibes. Headline 40px at 390 and 64-72px on a desktop
   fold; measure capped at ~16ch so a headline breaks into deliberate lines
   instead of running the full 1180px wrap. Body settles at 17-18px on a 560px
   measure — around 62 characters, inside the 60-75 band. */
h1.page-h{font-family:var(--sans);font-size:clamp(2.5rem,5vw,4.5rem);line-height:1.03;
  letter-spacing:-.022em;color:var(--ink);font-weight:400;margin-bottom:1.5rem;max-width:16ch;
  text-wrap:balance}
h1.page-h em{font-style:italic;color:var(--signal-blue)}
.page-sub{font-size:clamp(1.0625rem,1.35vw,1.125rem);color:var(--ink-soft);max-width:560px;
  line-height:1.58;margin-bottom:2.25rem;text-wrap:pretty}

/* ── SECTIONS ────────────────────────────────────────────────────────────── */
/* Section rhythm: 96px desktop / 56px mobile. Was 80px desktop, under the
   >=96px floor. The 64px homepage exception is the AVA homepage's own ratified
   value and does not reach this host. */
section{padding:96px 0;position:relative;z-index:1}
@media(max-width:600px){section{padding:56px 0}}
/* .75rem, not .68rem — 10.88px was under the 12px floor in CLAUDE.md Appendix A
   ("Nothing below 12px, ever"), on the label that opens every section. */
.sec-kicker{font-family:var(--mono);font-size:.75rem;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--sky);margin-bottom:1rem}
.sec-h{font-family:var(--sans);font-size:clamp(2rem,3.6vw,3.25rem);line-height:1.06;
  letter-spacing:-.018em;color:var(--ink);font-weight:400;margin-bottom:1.25rem;max-width:16ch;
  text-wrap:balance}
.sec-h em{font-style:italic;color:var(--signal-blue)}
.sec-sub{font-size:1.0625rem;color:var(--ink-soft);max-width:560px;line-height:1.58;
  margin-bottom:1.25rem;text-wrap:pretty}
.sec-sub:last-child{margin-bottom:0}

/* ── RUN 7 · TASK D — SECTION LIGHTING ───────────────────────────────────────
   With prose out of cards, nothing was left to say "a new section started" but
   a gap. These tints do that job — a change of surface, which APPENDIX A says
   is the divider on a dark canvas. Applied as a data attribute so the value a
   section carries names the mood it is supposed to have, and an editor cannot
   quietly reach for a colour that means something else.

   The seam is what reads, not the fill: 96px of a 2%-lifted surface meeting
   96px of void is a visible edge without ever being a "coloured section". */
section[data-lit]{background:var(--lit-flat)}
section[data-lit="bright"]{background:var(--lit-bright)}
section[data-lit="technical"]{background:var(--lit-technical)}
section[data-lit="calm"]{background:var(--lit-calm)}
section[data-lit="connected"]{background:var(--lit-connected)}
section[data-lit="dense"]{background:var(--lit-dense)}
section[data-lit="warm"]{background:var(--lit-warm)}
section[data-lit="flat"]{background:var(--lit-flat)}

/* ══ INLINE PROSE LINKS · SHARED BLOCK ═══════════════════════════════════════
   Byte-identical in assets/aic.css and the embedded <style> in index.html.

   An inline link inside a paragraph must be distinguishable by something other
   than colour. --signal-blue on --ink-soft is only 1.99:1 against the text
   around it, well under the 3:1 WCAG asks for when colour is the ONLY signal,
   so a colour-blind reader cannot see where the link is. The underline is the
   signal; the colour is reinforcement. Links that are their OWN block — .rel
   cards, nav, drawer, footer, CTAs — are exempt, because position identifies
   them.

   RUN 9 CAUGHT THIS THE HARD WAY. The rule existed in aic.css and nowhere else,
   so the eleven pages that load aic.css were fine and the homepage — which
   carries its own embedded copy of the shell — was not. This run added in-body
   links to the homepage for the first time, and Lighthouse dropped its
   accessibility score to 96 on `link-in-text-block` with six offenders, every
   one of them a link added an hour earlier. Exactly the three-CSS-homes defect
   the token block and the nav layer are guarded against; the guard just did not
   cover this rule yet. It does now.
   ══════════════════════════════════════════════════════════════════════════ */
.sec-sub a,.page-sub a,.card-p a,.vs-b a,.tk-sub a,.calc-basis a,.ticket-foot a,
.feat-p a,.pain-p a,.step-p a,.setup-p a,.int-mini a,.crush-p a,.operators-p a,
.hero-sub a,.cta-note a,.legal p a,.legal li a{
  text-decoration:underline;text-underline-offset:.18em;text-decoration-thickness:1px}

/* ── CARD GRID (3-up → 2-up → 1-up) ──────────────────────────────────────── */
/* ── RUN 7 · TASK D — DE-CARD ────────────────────────────────────────────────
   A card is a container that says "this is one discrete record." Every one of
   these grids was three paragraphs of supporting prose wearing that container,
   which made an explanation look like a data object and put six pieces of
   glass on a page that only ever holds one real record — the trip ticket.
   Chrome is off by default now; a hairline and space do the separating.

   Chrome is KEPT in exactly two places, both of which are genuinely discrete
   records rather than prose: a numbered stage in an ordered sequence
   (.card--stage, applied in markup so this does not depend on :has support),
   and a FAQ item, which is a question you open and close. */
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
  gap:1.75rem 2.5rem;margin-top:2rem}
.card{background:none;border:0;border-radius:0;padding:1.15rem 0 0;
  border-top:1px solid var(--line);
  -webkit-backdrop-filter:none;backdrop-filter:none}
.card-h{color:var(--ink);font-weight:600;font-size:1.08rem;margin-bottom:.55rem}
.card-p{font-size:1.0625rem;color:var(--ink-mute);line-height:1.58;max-width:560px}
.card-num{font-family:var(--mono);font-size:.7rem;letter-spacing:.18em;text-transform:uppercase;
  color:var(--cyan-bright);margin-bottom:.85rem;font-variant-numeric:tabular-nums}

/* ── § C · THE ONE RAISED-PANEL RECIPE ───────────────────────────────────────
   Every panel on this site used to carry its own fill, its own blur radius and
   its own coloured halo, which is six pieces of glass on a shadow ladder. One
   recipe now: a solid surface, one hairline, one black shadow. No backdrop
   blur anywhere except the two fixed overlay bars (nav and rail), which is
   what keeps the sitewide blur count inside the budget. */
.card--stage,#faq .card,.ticket-card,.calc,.cb-form{
  background:var(--surface);border:1px solid var(--line);
  box-shadow:var(--raise);
  -webkit-backdrop-filter:none;backdrop-filter:none}
/* no --raise on a <details>: an accordion row is a list row, and the hairline is
   what separates it. Shadows are for panels genuinely lifted off the page. */
details{background:var(--surface);border:1px solid var(--line);
  -webkit-backdrop-filter:none;backdrop-filter:none}
.card--stage,#faq .card{border-radius:14px;padding:1.3rem}
/* stays at the 17px body size — a card is a different container, not a
   different reading experience, and 16px is under the RUN 7 body floor */
.card--stage .card-p,#faq .card .card-p{font-size:1.0625rem}
#faq .cards,.cards:has(.card--stage){gap:1rem}

/* ── TRIP TICKET (the product, shown not described) ──────────────────────── */
/* This is an instrument, so it is one of the two zones § B lets the grid speak
   at .08 instead of .04. isolation + a masked ::before keeps the texture inside
   the ticket's own rounded box and behind its contents. */
.ticket-card{border-radius:16px;padding:1.4rem;margin-top:2rem;position:relative;isolation:isolate}
.ticket-card::before{content:'';position:absolute;inset:0;pointer-events:none;z-index:-1;
  border-radius:inherit;opacity:var(--grid-console);
  background-image:linear-gradient(var(--grid-line) 1px,transparent 1px),
                   linear-gradient(90deg,var(--grid-line) 1px,transparent 1px);
  background-size:32px 32px}
.ticket-bar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.5rem;
  padding-bottom:1rem;margin-bottom:1.1rem;border-bottom:1px solid var(--line);
  font-family:var(--mono);font-size:.68rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-mute)}
.ticket-live{display:inline-flex;align-items:center;gap:6px;color:var(--booked)}
/* § A STATE LAW — the dot is green because the word beside it names the action
   that succeeded. No halo: § C allows no coloured light outside the two scenes. */
.ticket-live::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--success-green)}
.ticket-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.85rem}
.tk{background:rgba(7,11,20,.55);border:1px solid var(--line);border-radius:10px;padding:.9rem 1rem}
.tk-label{font-family:var(--mono);font-size:.6rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-dim);margin-bottom:.4rem}
.tk-val{color:var(--ink);font-weight:600;font-size:.98rem;font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1}
.tk-sub{font-family:var(--mono);font-size:.74rem;color:var(--ink-mute);margin-top:.2rem;
  font-variant-numeric:tabular-nums}
/* RUN 7 · sans and 13px. This looked like a stamp on a record, which is why it
   was left in mono at 10.56px on the first pass — but it is a 20-word
   explanatory sentence about what the sample is, not a status, a field, a time,
   a route or a quote. Mono is for the ticket's data, not for the caveat under
   it, and 10.56px is under the 12px floor. */
.ticket-foot{font-family:var(--sans);font-size:.8125rem;letter-spacing:0;text-transform:none;
  color:var(--ink-mute);text-align:center;padding-top:1rem;margin-top:.85rem;line-height:1.5;
  border-top:1px solid var(--line);font-variant-numeric:tabular-nums}

/* ── COMPARISON ROWS ─────────────────────────────────────────────────────── */
/* RUN 7 · a comparison is a TABLE, and a table's rows are separated by rules,
   not boxed individually. Five stacked boxes read as five unrelated records;
   five hairline rows read as one comparison, which is what they are. */
.vs{display:grid;gap:0;margin-top:2rem;border-top:1px solid var(--line)}
.vs-row{display:grid;grid-template-columns:1fr;gap:.35rem;padding:1.1rem 0;
  border:0;border-bottom:1px solid var(--line);border-radius:0;background:none}
@media(min-width:720px){.vs-row{grid-template-columns:1fr 1fr;gap:1.5rem;align-items:baseline}}
.vs-a{font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-dim)}
.vs-b{color:var(--ink);font-size:1.0625rem;line-height:1.58}
/* § A STATE LAW — this is emphasis inside a comparison, not a captured
   outcome, so it does not get the success colour. Weight and ink carry it. */
.vs-b strong{color:var(--ink);font-weight:600}

/* ── FAQ ─────────────────────────────────────────────────────────────────── */
details{border-radius:12px;margin-bottom:.6rem;transition:border-color .2s}
details[open]{border-color:var(--line-hot)}
summary{cursor:pointer;padding:1.1rem 1.4rem;color:var(--ink);font-weight:500;font-size:1rem;
  list-style:none;display:flex;justify-content:space-between;align-items:center;gap:1rem;min-height:44px}
summary::-webkit-details-marker{display:none}
summary::after{content:'+';color:var(--sky);font-size:1.4rem;font-family:var(--mono)}
details[open] summary::after{content:'−'}
summary:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
details > div{padding:0 1.4rem 1.2rem;color:var(--ink-mute);font-size:.95rem;line-height:1.65}
details > div a{color:var(--signal-blue);text-decoration:underline}

/* ── CTA STACK (Task A — identical to the homepage) ──────────────────────── */
.cta-stack{display:flex;flex-direction:column;gap:1rem;max-width:520px;margin:0 0 1rem}
.cta-stack--wide{max-width:560px}

/* ══ § D · THE BUTTON SYSTEM ═════════════════════════════════════════════════
   Three levels, one law, and the type face is the tell.

     PRIMARY    filled --action-blue, white lettering, a 1px --sky bevel along
                the top edge. Space Grotesk 500, sentence case.
     SECONDARY  1px --line on --surface. Border and label go --sky on hover.
     TERTIARY   a --sky text link with an arrow. No box at all.

   Nothing paints outside its own box. The old primary threw a cyan halo and an
   inner bloom; the lift stays, the light is a brightness filter on the fill
   itself, and there is no shadow to leak onto the page.

   Sentence case, not upper: § D gives buttons, nav and conversion copy to
   Space Grotesk, and an uppercase mono button reads as a machine label rather
   than something a person presses. Mono keeps the ticket, not the control.
   ══════════════════════════════════════════════════════════════════════════ */
.tel-btn{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;
  padding:1.15rem 1.5rem;border-radius:12px;background:var(--action-blue);color:#FFFFFF;
  min-height:44px;text-decoration:none;box-shadow:inset 0 1px 0 rgba(127,178,255,.35);
  transition:transform .2s ease,filter .2s ease}
.tel-btn:hover{color:#FFFFFF;transform:translateY(-1px);filter:brightness(1.08)}
.tel-btn:active{transform:translateY(0);filter:brightness(.96)}
.tel-btn:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
/* white-space:nowrap — the number was breaking after "775-" and orphaning
   "0019" onto its own line at narrow widths. A phone number is one token. */
.tel-main{display:flex;align-items:center;gap:.6rem;font-family:var(--sans);font-size:1.0625rem;
  font-weight:500;letter-spacing:.005em;line-height:1.2;
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1;white-space:nowrap}
.tel-main svg{width:17px;height:17px;flex-shrink:0}
.tel-sub{font-family:var(--sans);font-size:.85rem;font-weight:400;letter-spacing:.01em;
  text-transform:none;color:rgba(255,255,255,.82)}
@media(max-width:600px){
  .tel-main{font-size:1rem}
  .tel-btn{padding:1rem 1.15rem}
}

/* The private scroll-margin-top that used to sit here moved to the § 1 ANCHOR
   GUTTER block in assets/circulant.css, where ONE rule covers every anchor
   target on all twelve pages. Leaving a copy here would have beaten the shared
   rule at equal specificity from a later stylesheet, and the two would have
   ADDED to 137px of offset instead of 77. */
.cb-form{border-radius:12px;padding:1.25rem}
/* eyebrow -> mono + --sky (§ D, § A). The dot went with the kicker's: it named
   no state and it carried a glow, which § C removes outside the two scenes. */
.cb-head{font-family:var(--mono);font-size:.75rem;
  letter-spacing:.16em;text-transform:uppercase;color:var(--sky);margin-bottom:.35rem}
.cb-sub{font-size:.9rem;color:var(--ink-mute);margin-bottom:.9rem;line-height:1.5}
.cb-row{display:flex;flex-wrap:wrap;gap:.6rem;margin-bottom:.75rem}
.cb-field{flex:1 1 150px;display:flex;flex-direction:column;gap:.3rem}
.cb-label{font-family:var(--mono);font-size:.62rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-dim)}
.cb-input{width:100%;min-height:44px;padding:.7rem .85rem;border-radius:8px;
  background:rgba(7,11,20,.70);border:1px solid var(--line);color:var(--ink);
  font-family:var(--sans);font-size:1rem;transition:border-color .15s,box-shadow .15s}
.cb-input::placeholder{color:var(--ink-dim)}
.cb-input:focus{outline:none;border-color:var(--signal-blue);box-shadow:0 0 0 3px rgba(61,123,255,.22)}
.cb-input:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
.cb-input[aria-invalid="true"]{border-color:var(--alarm)}
.cb-consent{display:flex;align-items:flex-start;gap:.6rem;margin-bottom:.85rem;cursor:pointer}
.cb-consent input{flex-shrink:0;width:20px;height:20px;margin-top:2px;
  accent-color:var(--action-blue);cursor:pointer}
.cb-consent input:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
.cb-consent span{font-size:.78rem;line-height:1.45;color:var(--ink-mute)}
.cb-consent a{color:var(--signal-blue);text-decoration:underline}
/* SECONDARY (§ D): a line button, not a second filled control. One primary per
   viewport, and on this stack the phone is it. */
.cb-submit{width:100%;min-height:44px;padding:.85rem 1.2rem;border:1px solid var(--line);
  border-radius:10px;background:var(--surface);color:var(--ink);font-family:var(--sans);
  font-size:1rem;font-weight:500;letter-spacing:.005em;cursor:pointer;
  transition:border-color .15s,color .15s,transform .15s}
.cb-submit:hover:not(:disabled){border-color:var(--sky);color:var(--sky);transform:translateY(-1px)}
.cb-submit:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
.cb-submit:disabled{opacity:.55;cursor:default}
/* RUN 7 · sans, not mono — these are full sentences read by a person who just
   made a mistake, not a machine readout. */
.cb-note{font-family:var(--sans);font-size:.85rem;letter-spacing:0;color:var(--ink-mute);
  margin-top:.6rem;line-height:1.5;min-height:1.2em}
.cb-note.is-err{color:var(--alarm)}
.cb-note.is-ok{color:var(--booked)}
.cb-form.is-done .cb-row,.cb-form.is-done .cb-consent,.cb-form.is-done .cb-submit{display:none}

.cta-tertiary{display:flex;flex-direction:column;gap:.2rem}
/* `a.cta-tertiary` as well as `.cta-tertiary a`: on /demo/ the class sits on
   the anchor itself rather than on a wrapper, so the descendant selector never
   matched and that page rendered the setup CTA in sentence-case Space Grotesk
   at 16px while the other nine rendered uppercase mono at 12.16px. One name is
   not one name if two of its thirty-six instances are typeset differently. */
/* TERTIARY (§ D): --sky text link with an arrow. Sentence case Space Grotesk,
   same as every other control on the page. */
.cta-tertiary a,a.cta-tertiary{font-family:var(--sans);font-size:1rem;font-weight:500;
  letter-spacing:.005em;white-space:nowrap;
  color:var(--sky);border-bottom:1px solid var(--line-hot);align-self:flex-start;padding:.5rem 0;
  min-height:44px;display:inline-flex;align-items:center}
.cta-tertiary a:hover,a.cta-tertiary:hover{color:var(--ink);border-color:var(--sky)}
.cta-tertiary a:focus-visible,a.cta-tertiary:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
/* RUN 7 · TASK E — this was mono. Mono is for operational data: a status, a
   field, a time, a route, a quote. "The setup call is a conversation, not a
   pitch." is a sentence, and mono makes a sentence read like a machine
   readout. Sales prose is Space Grotesk here and everywhere. */
.cta-note{font-family:var(--sans);font-size:.9rem;color:var(--ink-mute);letter-spacing:0;
  line-height:1.55;max-width:560px}

/* ── RUN 7 · TASK A — MOBILE STICKY ACTION RAIL ──────────────────────────────
   ── rewritten RUN 13 · § 1: THE PAIR IS CALL + BOOK ─────────────────────────
   Below the nav breakpoint the hero CTAs scroll away and the operator is left
   with no control at all until the next section. The rail restores two, and
   they are this site's two real asks: call the line now, or book the setup
   call.

   THE SECOND CONTROL USED TO BE "GET A CALL BACK", and it pointed at the
   callback console further down the same page. A fixed bar whose second button
   exists to scroll you to something already on the page is spending half of
   the only chrome a phone reader has on navigation. The console is untouched
   and is still the callback path — a reader meets it by scrolling and opens it
   by tapping its own 64px header. What the bar offers now is the one thing
   that is NOT on the page: /book/.

   EQUAL COLUMNS, not the old 60/40. § 4's demo pair settled this idiom
   already: two controls with different labels and no width rule read as a
   primary with a stray link beside it rather than as a choice. These are one
   choice. Hierarchy is carried by the fill, which is what fill is for.

   It is hidden — not merely dimmed — whenever an inline primary, the callback
   console, the booking calendar or the footer is on screen, so it can never
   sit on top of an input or restate a control already in view. `visibility` is
   part of the hidden state on purpose: a transformed-off bar is still
   focusable, and a keyboard user would otherwise tab into a control they
   cannot see. `body.rail-on` pays back the height at the foot of the document
   so the rail cannot cover the last line of the page either. */
.rail{position:fixed;left:0;right:0;bottom:0;z-index:60;display:none;
  grid-template-columns:1fr 1fr;gap:8px;
  padding:8px 12px calc(8px + env(safe-area-inset-bottom,0px));
  background:rgba(7,11,20,.94);border-top:1px solid var(--line);
  -webkit-backdrop-filter:blur(18px) saturate(1.3);backdrop-filter:blur(18px) saturate(1.3);
  transform:translateY(110%);visibility:hidden;
  transition:transform .22s ease}
.rail--solo{grid-template-columns:1fr}
.rail.is-on{transform:translateY(0);visibility:visible}
/* The separation between the glyph and the label is the SVG's own margin, not
   the flex `gap`. With a gap, every child is spaced from every other child —
   and the label is two children once the verb is its own span, so the gap
   lands BETWEEN the word and the digits and renders "Call (414) 775-0019" with
   a hole in it. `.rail-label` wraps both so the label is one flex item; the
   margin then separates exactly the two things that should be separated. */
.rail a{display:inline-flex;align-items:center;justify-content:center;
  min-height:56px;padding:0 .75rem;border-radius:10px;font-family:var(--sans);
  font-size:.9375rem;font-weight:500;letter-spacing:.005em;
  font-variant-numeric:tabular-nums;white-space:nowrap}
.rail svg{width:15px;height:15px;flex-shrink:0;margin-right:.5rem}
.rail-call{background:var(--action-blue);color:#FFFFFF;box-shadow:inset 0 1px 0 rgba(127,178,255,.35)}
.rail-call:hover,.rail-call:active{color:#FFFFFF}
.rail-book{background:var(--surface);color:var(--ink);border:1px solid var(--line)}
.rail-book:hover{color:var(--sky);border-color:var(--sky)}
.rail a:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
@media (max-width:879.98px){
  .rail{display:grid}
  body.rail-on{padding-bottom:calc(72px + env(safe-area-inset-bottom,0px))}
}
/* ── THE VERB DROPS BEFORE THE NUMBER DOES ───────────────────────────────────
   Measured on the real faces at the rail's own type, not computed from the
   CSS. Each figure is glyph + margin + label + the control's own padding:

       "Call (414) 775-0019"   194px at 15px
       "Book the setup call"   166px at 15px
                               ---
                               360px of control

   The 360 viewport offers 328px between the bar's padding and the gap, and 390
   offers 358. THE TWO FULL LABELS DO NOT FIT SIDE BY SIDE ON A PHONE, and no
   split of the columns changes that — the shortfall is 32px of text, not 32px
   of layout.

   § 3's header chip settled the identical problem the identical way: the
   digits are the payload, the word is the affordance, and once the glyph is
   there the word is the part that can go. So the verb drops below 480 and the
   bar steps to 14px below 400 — over the 12px floor, and it buys real slack at
   360 instead of the two pixels a single measure left. Re-measure both labels
   before changing either one; this is a text-width budget, not a preference.
   ─────────────────────────────────────────────────────────────────────────── */
@media (max-width:479.98px){
  .rail-verb{display:none}
}
@media (max-width:399.98px){
  .rail a{font-size:.875rem}
}
/* 320 is below the render gate's floor of 360, and the pair still overflowed
   there by 28px — measured, not assumed: tracks 324 into a 296px content box,
   which clips the right edge of "Book the setup call" off the screen. A grid
   item's `min-width` is `auto`, so a nowrap label GROWS its own 1fr track and
   the control never reports an overflow; the BAR does, and only if something
   sums the tracks. `tools/aic-run13-railfit.mjs` is what sums them.

   The relief is taken from the box, not the type — 14px is already the floor
   this bar should go to. Bar padding 12→8 and control padding 12→6 per side
   returns 32px against a 28px need. Re-measure at 320 after any label change. */
@media (max-width:359.98px){
  .rail{padding-left:8px;padding-right:8px}
  .rail a{padding:0 .375rem}
  .rail svg{margin-right:.375rem}
}
@media (prefers-reduced-motion:reduce){
  .rail{transition:visibility 0s linear .01s}
}

/* ── MISSED-NIGHT CALCULATOR ──────────────────────────────────────────────
   The only number an operator believes is the one he typed in himself. Both
   inputs ship EMPTY — no seeded "average fare", no assumed miss rate, nothing
   for us to be accused of inventing. Nothing is stored and nothing is sent.
   Outputs are --amber because they represent loss, and tabular-nums so the
   figures do not jitter as they recompute on every keystroke.
   ────────────────────────────────────────────────────────────────────────── */
.calc{border-radius:16px;padding:1.6rem;margin-top:2rem}
@media(min-width:760px){.calc{padding:2rem}}
.calc-row{display:flex;flex-wrap:wrap;gap:1rem;margin-bottom:1.5rem}
.calc-field{flex:1 1 190px;display:flex;flex-direction:column;gap:.35rem}
.calc-label{font-family:var(--mono);font-size:.62rem;letter-spacing:.16em;
  text-transform:uppercase;color:var(--neutral)}
.calc-input{width:100%;min-height:44px;padding:.7rem .85rem;border-radius:8px;
  background:rgba(7,11,20,.70);border:1px solid var(--line);color:var(--ink);
  font-family:var(--mono);font-size:1.15rem;font-variant-numeric:tabular-nums;
  transition:border-color .15s,box-shadow .15s}
.calc-input::placeholder{color:var(--neutral);opacity:.6}
.calc-input:focus{outline:none;border-color:var(--signal-blue);box-shadow:0 0 0 3px rgba(61,123,255,.22)}
.calc-input:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
/* number spinners removed — they invite fiddling and misalign the mono figures */
.calc-input::-webkit-outer-spin-button,.calc-input::-webkit-inner-spin-button{
  -webkit-appearance:none;margin:0}
.calc-input[type=number]{-moz-appearance:textfield;appearance:textfield}

.calc-out{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:.85rem;
  padding-top:1.4rem;border-top:1px solid var(--line)}
.calc-cell{background:rgba(7,11,20,.55);border:1px solid var(--line);border-radius:10px;
  padding:1rem}
.calc-k{font-family:var(--mono);font-size:.6rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--neutral);margin-bottom:.45rem}
.calc-v{font-family:var(--mono);font-size:1.5rem;font-weight:500;color:var(--amber);
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1;line-height:1.1}
.calc-v[data-empty="true"]{color:var(--neutral);opacity:.5}
.calc-note{font-family:var(--mono);font-size:.7rem;letter-spacing:.08em;color:var(--neutral);
  margin-top:1.1rem;text-transform:uppercase}
.calc-basis{font-size:.85rem;color:var(--ink-mute);margin-top:.6rem;line-height:1.55}

/* ── RELATED / INTERNAL LINKS ────────────────────────────────────────────── */
.related{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:.85rem;margin-top:2rem}
/* RUN 7 · de-carded like everything else. These were the last four-sided boxes
   outside the six families the brief allows, and the affordance survives the
   change: the cyan eyebrow, the title weight and a hairline that lights up on
   hover all still say "this is a link". */
.rel{display:block;padding:1.15rem 0 0;border:0;border-top:1px solid var(--line);border-radius:0;
  background:none;transition:border-color .18s,transform .18s}
.rel:hover{border-top-color:var(--sky);transform:translateY(-2px)}
.rel:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
/* 12px floor: this eyebrow rendered at 9.92px. */
.rel-k{font-family:var(--mono);font-size:.75rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--sky);margin-bottom:.4rem}
.rel-t{color:var(--ink);font-weight:600;font-size:1rem;line-height:1.35}

/* ── LEGAL PAGES ─────────────────────────────────────────────────────────── */
.legal{max-width:74ch}
.legal h2{font-family:var(--sans);font-size:1.5rem;color:var(--ink);font-weight:500;
  margin:2.5rem 0 .85rem;line-height:1.2}
.legal h2:first-child{margin-top:0}
.legal h3{font-family:var(--mono);font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--sky);margin:1.75rem 0 .6rem}
/* RUN 7 · the .legal scope was the one prose surface no task owned, and it was
   the widest text on the site: 16px at an uncapped measure rendered 92-95ch,
   well past the 60-75ch rule. It is not only legal copy either — the "what we
   need from you" list on /how-setup-works/ borrows this wrapper, so a sales
   list was rendering smaller, looser and 200px wider than every other block on
   its own page. Same 17px / 1.58 / 560px as the rest of the site now. */
.legal p{font-size:1.0625rem;color:var(--ink-soft);line-height:1.58;margin-bottom:1rem;max-width:560px}
.legal ul{margin:0 0 1.25rem 1.25rem;color:var(--ink-soft);line-height:1.58;max-width:560px}
.legal li{margin-bottom:.5rem;font-size:1.0625rem}
.legal a{color:var(--signal-blue);text-decoration:underline}
.legal strong{color:var(--ink);font-weight:600}
.legal-meta{font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-dim);margin-bottom:2.5rem;padding-bottom:1.25rem;border-bottom:1px solid var(--line)}

/* ── FOOTER ──────────────────────────────────────────────────────────────── */
footer{padding:60px 0 40px;border-top:1px solid var(--line);margin-top:80px}
.foot{display:flex;flex-wrap:wrap;justify-content:space-between;gap:1.5rem;align-items:flex-start}
.foot-brand{flex:1;min-width:240px}
.foot-mark{font-family:var(--sans);font-size:1.5rem;color:var(--ink);font-weight:400;margin-bottom:.5rem}
.foot-mark em{font-style:italic;color:var(--signal-blue)}
.foot-nap{font-family:var(--mono);font-size:.8rem;color:var(--ink-soft);letter-spacing:.05em;
  line-height:1.8;margin-bottom:.75rem;font-variant-numeric:tabular-nums}
/* The footer phone was an 18px-tall tap target. On a phone-first site the number
   IS the conversion — it gets a full 44px target like every other CTA. */
.foot-nap a{color:var(--ink);border-bottom:1px solid var(--line-hot);
  display:inline-flex;align-items:center;min-height:44px}
.foot-nap a:hover{color:var(--sky)}
.foot-nap a:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
.foot-bottom{display:flex;flex-wrap:wrap;justify-content:space-between;gap:1rem;padding-top:2rem;
  margin-top:2rem;border-top:1px solid var(--line);font-family:var(--mono);font-size:.7rem;
  color:var(--ink-dim);letter-spacing:.08em;text-transform:uppercase}

/* ── MOTION ──────────────────────────────────────────────────────────────── */
/* `@keyframes pulse` used to live here. Nothing in this file referenced it once
   the kicker lost its dot, and a keyframe with no caller is a rule that ships
   bytes and reads as an affordance the site does not have. */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none!important;transition:none!important}
  .rel:hover,.tel-btn:hover,.cb-submit:hover{transform:none!important}
}

/* ══ RUN 13 · THE HOMEPAGE COMES HOME ════════════════════════════════════════
   Everything below this line rendered the homepage from an embedded <style> in
   index.html until RUN 13. That block was this site's THIRD CSS home, and it is
   why § 1's trap has cost a run every time it has fired: a rule written for
   "the stylesheet" landed in one head, and the site shipped wearing two.
   RUN 12's FAQ-answer link was the most recent instance — underlined on eleven
   pages, colour-only on the homepage, live since the section shipped, with
   Lighthouse reading 100 either way. THE AUDIT IS NOT THE RULE.

   There are TWO CSS homes now. The homepage loads exactly what the other
   fifteen pages load, so a shared rule has nowhere left to diverge to.

   POSITION IS LOAD-BEARING, exactly as it was inside index.html: these are the
   BASE rules, and the RUN 10 / 11 / 12 blocks below still override them. Move
   this block below those and the container radii, the primary-control physics
   and the 12px type floor all silently revert — no error, no warning, just an
   older-looking page.

   Nothing in here matches an element on the other fifteen pages. That is
   asserted rather than assumed: `node tools/aic-run13-reach.mjs` runs every
   migrating selector against every page's real DOM. Exactly one collided and it
   is scoped at the foot of this file.
   ══════════════════════════════════════════════════════════════════════════ */

.hero{padding:140px 0 80px;position:relative}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.85)}}
h1.hero-h{font-family:var(--sans);font-size:clamp(2.5rem,6.5vw,5.25rem);line-height:1.02;letter-spacing:-.02em;color:var(--ink);font-weight:400;margin-bottom:1.5rem;max-width:18ch}
h1.hero-h em{font-style:italic;color:var(--signal-blue)}
.hero-sub{font-size:clamp(1.05rem,1.6vw,1.25rem);color:var(--ink-soft);max-width:60ch;line-height:1.55;margin-bottom:2.25rem}
.ctas{display:flex;flex-wrap:wrap;gap:.85rem;margin-bottom:3.5rem}
/* ══ § D · THE BUTTON SYSTEM ═════════════════════════════════════════════════
PRIMARY    filled --action-blue, white lettering, a 1px --sky bevel on the
top edge. Space Grotesk 500, sentence case.
SECONDARY  1px --line on --surface; border and label go --sky on hover.
TERTIARY   a --sky text link with an arrow, no box.
Hover lifts 1px and brightens the fill. Nothing paints outside its own box.
Mirrors aic.css exactly. */
.btn{display:inline-flex;align-items:center;gap:.5rem;padding:.95rem 1.4rem;border-radius:10px;font-family:var(--sans);font-size:1rem;font-weight:500;letter-spacing:.005em;transition:transform .15s,filter .15s,border-color .15s,color .15s;cursor:pointer;border:none}
.btn-ghost{background:var(--surface);color:var(--ink);border:1px solid var(--line)}
.btn-ghost:hover{background:var(--surface);border-color:var(--sky);color:var(--sky);transform:translateY(-1px)}
.btn-ghost:active{transform:translateY(0)}
.btn svg{width:14px;height:14px}
/* THE CIRCULANT RING IS GONE. It wrapped the primary CTA in a rotating conic
arc plus a pulsing halo, both painted OUTSIDE the button, and RUN 3.5 already
had to switch it off with display:none because at rest it rendered as a hard
rectangle floating a few pixels off a 10px pill. Two runs of dead chrome, a
custom @property, two keyframes and a @supports fallback, all for something
nobody could see. § C removes coloured halos outright, so it is deleted here
rather than recoloured. */
/* ── § B/C · THE CONSOLE ─────────────────────────────────────────────────────
An instrument, so it is one of the two zones § B lets the grid speak at .08
rather than .04, and the hero copy of it is one of the two scenes § C lets
carry ambient light. Flat fill, one hairline, one colourless shadow. The
lit top edge stays on the HERO console only (see the scoped rule below);
the sample console further down the page is a record at rest and gets none. */
.console{background:var(--surface);border:1px solid var(--line);padding:1.4rem;box-shadow:var(--raise);position:relative;overflow:hidden;isolation:isolate}
.console::after{content:'';position:absolute;inset:0;pointer-events:none;z-index:-1;opacity:var(--grid-console);background-image:linear-gradient(var(--grid-line) 1px,transparent 1px),linear-gradient(90deg,var(--grid-line) 1px,transparent 1px);background-size:32px 32px}
.hero .console::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--signal-blue),transparent);opacity:.6}
.console-bar{display:flex;align-items:center;justify-content:space-between;padding-bottom:1rem;margin-bottom:1.2rem;border-bottom:1px solid var(--line);flex-wrap:wrap;gap:.5rem}
.console-title{display:flex;align-items:center;gap:.6rem;font-family:var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-mute)}
.dots{display:flex;gap:5px}
.dots span{width:10px;height:10px;border-radius:50%}
/* § A · these three were amber, blue and green — the site's three signal hues,
spent on a window-chrome affordance that names no state. They pushed the hero
and the sample console to three accents each with nothing to show for it.
Neutral now: they are a console's window dots, not a status readout. */
.dots span{background:var(--neutral);opacity:.45}
/* 12px floor: this label rendered at 10.56px. */
.live-badge{display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;color:var(--success-green)}
.live-badge::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--success-green)}
.console-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:.85rem;margin-bottom:.85rem}
.c-card{border-radius:10px;padding:.95rem 1.05rem}
/* 12px floor: field labels rendered at 9.92px. Mono is right here — these
are field names, which is exactly what § D gives the mono face. */
.c-label{font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;color:var(--neutral);margin-bottom:.45rem}
.c-main{color:var(--ink);font-weight:600;font-size:1rem;margin-bottom:.25rem}
.c-sub{font-family:var(--mono);font-size:.78rem;color:var(--ink-mute)}
.c-row{display:grid;grid-template-columns:repeat(3,1fr);gap:.85rem;padding:.85rem;background:rgba(61,123,255,.05);border:1px solid var(--line);border-radius:10px;margin-bottom:.85rem}
.c-row .c-card{background:transparent;border:none;padding:0}
/* 12px floor: rendered at 10.56px. */
.c-meta{font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;color:var(--neutral);text-align:center;padding-top:.85rem;border-top:1px solid var(--line)}
/* These are STATE LABELS, not ornament: each one sits beside a field and names
what happened to it. § A's state law is satisfied because the word and the
colour are one object and change together. 12px floor applied. */
.badge{display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:.75rem;letter-spacing:.12em;text-transform:uppercase;padding:5px 10px;border-radius:99px}
.badge-green{background:rgba(46,230,168,.12);color:var(--success-green);border:1px solid rgba(46,230,168,.3)}
.badge-amber{background:rgba(255,176,32,.12);color:var(--amber);border:1px solid rgba(255,176,32,.3)}
.badge-blue{background:rgba(61,123,255,.12);color:var(--sky);border:1px solid var(--line-hot)}
.badge::before{content:'';width:5px;height:5px;border-radius:50%;background:currentColor}
.steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:1rem}
.step{background:var(--surface);border:1px solid var(--line);padding:1.6rem;position:relative;transition:border-color .25s,transform .25s}
.step::before{content:'';position:absolute;top:0;left:0;width:60%;height:1px;background:linear-gradient(90deg,var(--signal-blue),transparent);opacity:.65}
.step::after{content:'';position:absolute;top:14px;left:14px;width:4px;height:4px;border-radius:50%;background:var(--signal-blue)}
.step:hover{border-color:var(--line-hot);transform:translateY(-2px)}
.step-num{font-family:var(--mono);font-size:.75rem;letter-spacing:.16em;text-transform:uppercase;color:var(--sky);margin-bottom:1rem}
.step-h{font-family:var(--sans);font-size:1.6rem;color:var(--ink);font-weight:400;margin-bottom:.6rem;line-height:1.15}
.step-p{font-size:.95rem;color:var(--ink-mute);line-height:1.55}
.pain-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:.85rem}
.pain-card{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:1.4rem}
.pain-icon{width:32px;height:32px;border-radius:8px;background:rgba(61,123,255,.10);border:1px solid var(--line-hot);display:flex;align-items:center;justify-content:center;margin-bottom:.85rem;color:var(--sky)}
.pain-icon svg{width:16px;height:16px}
.pain-h{color:var(--ink);font-weight:600;font-size:1rem;margin-bottom:.35rem}
.pain-p{font-size:.9rem;color:var(--ink-mute);line-height:1.5}
.feat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:1rem}
.feat{background:var(--surface);border:1px solid var(--line);padding:1.5rem;transition:border-color .25s,transform .25s;position:relative;overflow:hidden}
.feat::before{content:'';position:absolute;top:0;left:0;width:60%;height:1px;background:linear-gradient(90deg,var(--signal-blue),transparent);opacity:.65}
.feat::after{content:'';position:absolute;top:14px;left:14px;width:4px;height:4px;border-radius:50%;background:var(--signal-blue)}
.feat:hover{border-color:var(--line-hot);transform:translateY(-2px)}
.feat-h{color:var(--ink);font-weight:600;font-size:1.1rem;margin-bottom:.6rem}
.feat-p{font-size:.95rem;color:var(--ink-mute);line-height:1.55}
.crush{padding:3rem 2rem;text-align:center;position:relative;overflow:hidden}
.crush-h{font-family:var(--sans);font-size:clamp(1.8rem,3.6vw,2.8rem);color:var(--ink);font-weight:400;margin-bottom:1rem;line-height:1.1;position:relative}
.crush-h em{font-style:italic;color:var(--amber)}
.crush-p{font-size:1.05rem;color:var(--ink-soft);max-width:62ch;margin:0 auto 2rem;line-height:1.6;position:relative}
.crush-calc-link{position:relative;margin-top:1.4rem}
.crush-calc-link a{color:var(--amber);border-bottom:1px solid rgba(255,176,32,.4);display:inline-flex;align-items:center;min-height:44px}
.crush-calc-link a:hover{color:var(--ink);border-color:var(--ink)}
.crush-calc-link a:focus-visible{outline:2px solid var(--amber);outline-offset:2px}
.crush .btn-primary{position:relative}
.setup-card{padding:2.5rem 2rem;display:grid;gap:1.5rem;align-items:center;position:relative;overflow:hidden}
@media(min-width:760px){
  .setup-card{grid-template-columns:1.5fr 1fr;padding:3rem}
}
.setup-h{font-family:var(--sans);font-size:clamp(1.6rem,3vw,2.4rem);color:var(--ink);font-weight:400;line-height:1.1;margin-bottom:.75rem}
.setup-p{font-size:1rem;color:var(--ink-soft);line-height:1.55}
.setup-cta{display:flex;flex-direction:column;gap:.6rem}
.setup-note{font-family:var(--mono);font-size:.7rem;color:var(--ink-dim);letter-spacing:.06em}
@media(max-width:600px){
  .hero{padding:110px 0 60px}
  .console{padding:1rem}
  .c-row{grid-template-columns:repeat(3,1fr);gap:.5rem;padding:.7rem}
  .ctas{flex-direction:column;align-items:stretch}
  .btn{justify-content:center}
  .crush{padding:2.2rem 1.4rem}
  .setup-card{padding:2rem 1.4rem}
}
/* ── § C · THREE DECORATIVE LOOPS, REMOVED ───────────────────────────────────
A nine-bar waveform that breathed forever inside both setup CTAs; a radar
ring that expanded to 140px behind the demo CTA; and a footer mesh row. All
three are gone rather than recoloured.

The waveform: § C allows one active pulse at a time in two named scenes, and
a CTA is neither of them. It also encoded nothing — a button is not listening
— and it forced a max-width:400px rule that hid it again to stop the label
wrapping. Deleting it gives the label back ~28px at every width.

The radar ring: its selector was `#demo > .wrap > div[style*="margin-bottom:2rem"]`
and no element on this page has carried that inline style since RUN 4. It has
been painting nothing for four runs.

The mesh row: `.foot-mesh` markup was removed with the cross-brand promo in
RUN 1. Only the rules were left.
────────────────────────────────────────────────────────────────────────── */
/* Demo controls — two-button cluster (CIRCULANT cyan, Space Grotesk) */
.demo-play-bar{align-items:center}
.demo-play-btn{display:inline-flex;align-items:center;gap:.6rem;padding:.95rem 1.5rem;font-size:1rem;cursor:pointer;text-decoration:none}
.demo-call-btn{display:inline-flex;align-items:center;gap:.6rem;padding:.95rem 1.5rem;border-radius:10px;font-family:var(--sans);font-size:1rem;font-weight:500;letter-spacing:.005em;cursor:pointer;border:1px solid transparent;transition:transform .15s,filter .15s,border-color .15s,color .15s;text-decoration:none}
.demo-play-btn[data-state="playing"]{background:var(--surface);color:var(--sky);border-color:var(--line-hot);box-shadow:none}
.demo-call-btn{background:var(--surface);color:var(--ink);border-color:var(--line)}
.demo-call-btn:hover{color:var(--sky);border-color:var(--sky);transform:translateY(-1px)}
.demo-play-btn .play-glyph,.demo-call-btn .play-glyph{display:inline-flex;align-items:center;justify-content:center;font-size:1rem;line-height:1}
/* Idle: complete, sharp reservation ticket (no blur, no dim). On tap, .demo-armed enables a brief cyan capture-flash per card as audio hits each threshold. */
/* Feedback, not ambience: this only fires while the operator is playing the
recording, and it is an INSET rule so nothing paints outside the card. */
@keyframes capture-flash{0%,100%{box-shadow:inset 0 0 0 0 rgba(61,123,255,0)}30%{box-shadow:inset 0 0 0 2px var(--signal-blue)}}
#demo-console.demo-armed [data-reveal].on{animation:capture-flash 1.1s ease}
#demo-final-badge{display:none;margin-top:1rem;padding:.95rem 1.1rem;border-radius:12px;background:rgba(46,230,168,.10);border:1px solid rgba(46,230,168,.45);color:var(--ink);font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;text-align:center}
#demo-final-badge.show{display:block;animation:final-badge-in .6s ease}
/* § A STATE LAW — green because the sentence beside it names three actions
that succeeded, and the badge only appears once they have. */
#demo-final-badge::before{content:'✓ ';color:var(--success-green);font-weight:600}
@keyframes final-badge-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
@media (prefers-reduced-motion: reduce){
  #demo-console.demo-armed [data-reveal].on{animation:none}
  #demo-final-badge.show{animation:none}
}
/* Integrations strip — cyan-bordered platform pills (text only, no logos) */
.int-pills{display:flex;flex-wrap:wrap;gap:.6rem;margin:0 0 1.1rem}
.int-pill{display:inline-flex;align-items:center;font-family:var(--mono);font-size:.75rem;letter-spacing:.08em;color:var(--ink);padding:.5rem .9rem;border-radius:99px;background:var(--surface);border:1px solid var(--line)}
.int-muted{font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;color:var(--neutral);margin:0 0 1.25rem;text-transform:uppercase}
.int-mini{font-size:1rem;color:var(--ink-soft);max-width:62ch;line-height:1.6}
/* CIRCULANT cyan pulse on the hero console card (not #demo-console) */
/* ── § C · SCENE 1 OF 2 · THE HERO CONSOLE ───────────────────────────────────
One of the two places on this site allowed ambient light. It used to throw a
10px cyan ring OUTSIDE its own box; the light is now an inset edge that
breathes, so nothing paints onto the page around it. Paused offscreen by the
same IntersectionObserver that drives the console's own timeline, and off
entirely under reduced motion. */
.hero .console{animation:hero-console-pulse 2.6s ease-in-out infinite}
@keyframes hero-console-pulse{0%,100%{box-shadow:var(--raise),inset 0 0 0 1px rgba(61,123,255,.16)} 50%{box-shadow:var(--raise),inset 0 0 0 1px rgba(61,123,255,.50)}}
@media (prefers-reduced-motion: reduce){
  .hero .console{animation:none}
}
/* Hero Dispatch Console — the scripted intake animation */
.hero-console [data-hc-stage]{opacity:0;transform:translateY(4px);transition:opacity .55s ease,transform .55s ease}
.hero-console [data-hc-stage].on{opacity:1;transform:translateY(0)}
.hero-console .hc-state{display:inline-flex;align-items:center;font-family:var(--mono);letter-spacing:.16em;margin-left:.7rem;padding:3px 9px;border-radius:99px;text-transform:uppercase;transition:color .25s,background .25s,border-color .25s}
.hero-console .hc-state.ringing{color:var(--amber);background:rgba(255,176,32,.10);border:1px solid rgba(255,176,32,.45);animation:hc-ring 1s ease-in-out infinite}
.hero-console .hc-state.live{color:var(--sky);background:rgba(61,123,255,.12);border:1px solid var(--line-hot)}
.hero-console .hc-state.complete{color:var(--ink);background:rgba(61,123,255,.18);border:1px solid var(--signal-blue)}
@keyframes hc-ring{0%,100%{opacity:.55}50%{opacity:1}}
.hero-console .hc-transcript{display:flex;flex-direction:column;gap:.45rem;padding:.95rem 1rem;margin:0 0 1rem;border-radius:10px;background:rgba(61,123,255,.05);border:1px solid var(--line);min-height:7.5rem}
.hero-console .hc-line{font-family:var(--mono);font-size:.78rem;line-height:1.5;color:var(--ink-soft);opacity:0;transform:translateX(-4px);transition:opacity .4s ease,transform .4s ease}
.hero-console .hc-line.on{opacity:1;transform:translateX(0)}
.hero-console .hc-who{color:var(--sky);font-weight:600;margin-right:.4rem}
@media (prefers-reduced-motion: reduce){
  .hero-console [data-hc-stage],.hero-console .hc-line{opacity:1;transform:none;transition:none}
  .hero-console .hc-state.ringing{animation:none}
}
/* ── § C · ONE PANEL RECIPE, NO GLASS ────────────────────────────────────────
RUN 3.5 unified six different fills and blur radii into one frosted recipe.
§ C finishes the job by removing the frost: backdrop-filter survives on
exactly two surfaces sitewide, both of them fixed overlay bars (the nav and
the sticky rail), which is what keeps the blur count inside budget. Panels
are a solid surface, one hairline and one colourless shadow.

Which of these still get a panel at all is decided further down in TASK D:
.feat, .pain-card, .setup-card and .crush are de-carded there and this rule
has to run first for that override to land. */
.console,.pain-card,.setup-card,.step,.feat{background:var(--surface);border:1px solid var(--line);box-shadow:var(--raise)}
.btn:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}
@media (prefers-reduced-motion: reduce){
  .btn{transition:none}
  .btn:hover,.btn:active{transform:none}
}
@media(min-width:1020px){
  /* ════════════════════════════════════════════════════════════════
  AIC SITE RUN 1 · "OPERATOR CUT"
  Radius note: CIRCULANT-X calls for 0px corners on NEW surfaces, but
  this page is a touchup, not a rebuild, and every existing surface on
  it is 10-18px. Sharp corners on the new CTA stack alone would read as
  a broken graft. Matching the page's own idiom; the corner law applies
  when this page is genuinely rebuilt.
  ════════════════════════════════════════════════════════════════ */
  /* --- HERO GRID ----------------------------------------------------- */
  /* Single column by default (mobile-first). At 1020px the console moves
  beside the CTA stack instead of below it — the stack caps at 520px, so
  one column wasted the right half of a desktop fold. align-items:start
  keeps the console at its natural height instead of stretching. */
  .hero-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:3rem;align-items:start}
  .hero .hero-left{min-width:0}
  .hero .hero-console{margin-top:0;position:sticky;top:96px}
  .hero h1.hero-h{font-size:clamp(2.5rem,4.4vw,4rem)}
}
/* --- B3 · tabular figures on every ticket/quote/time value --------- */
.c-main,.c-sub,.c-meta,.hc-line,.int-pill,.setup-note{font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
.hc-ico{width:12px;height:12px;color:var(--cyan-bright);margin-right:.45rem;display:inline-block;vertical-align:-1px;flex-shrink:0}
/* --- B1 · play control -------------------------------------------- */
.demo-play-btn svg,.demo-call-btn svg{width:14px;height:14px;flex-shrink:0}
/* --- B2 · resting ticket + "watch it fill" ------------------------- */
/* The console is COMPLETE at rest. An operator who never taps still sees
a finished trip ticket. Only on replay do the fields clear and rebuild. */
/* RUN 7 · `.demo-rest` deleted — no element has carried that class since
before this run, so it was two rules of chrome for an affordance that is
not on the page. The resting-ticket SEMANTICS it was written for are
intact and are what actually matter: the sample console below is complete
at rest, and only clears and refills when someone taps play. */
#demo-console.demo-armed [data-reveal]{opacity:.28;transition:opacity .35s ease}
#demo-console.demo-armed [data-reveal].on{opacity:1}
@media (prefers-reduced-motion:reduce){
  #demo-console.demo-armed [data-reveal]{opacity:1;transition:none}
}
/* --- B6 · the visitor's own clock --------------------------------- */
/* The dot is INLINE-BLOCK inside the text flow, not a flex sibling. As a
flex item it anchored to the container's left edge and, once the line
wrapped inside the centred .crush card, floated away from the words as
a stray amber mark. Inline-block keeps it welded to the first word. */
.crush-now{display:block;font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:var(--amber);margin:0 auto 1rem;max-width:44ch;line-height:1.7;position:relative;font-variant-numeric:tabular-nums}
.crush-now::before{content:'';display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--amber);margin-right:.5rem;vertical-align:middle;animation:pulse 2s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){
  .crush-now::before{animation:none}
}
/* --- THE OPERATOR SECTION ----------------------------------------- */
/* RUN 9 · renamed. § E takes the retired individual-attribution word out of
every shipped surface, and a class name is a shipped surface — it is in
the HTML a crawler reads and in the stylesheet it fetches. (The word is
deliberately not written here: comments ship, and a grep-based audit that
matches its own fix note reports a clean page as dirty. Describe the
rule, never quote it.) The card chrome that used
to wrap this was deleted rather than overridden in RUN 7, for the same
reason: chrome left one class name away from the element it used to wrap
is how a de-carded section quietly comes back. */
.operators-h{font-family:var(--sans);font-size:clamp(1.7rem,3.2vw,2.6rem);color:var(--ink);font-weight:400;line-height:1.1;margin-bottom:1.1rem;max-width:20ch}
.operators-p{font-size:1.05rem;color:var(--ink-soft);line-height:1.65;max-width:64ch;margin-bottom:1rem}
.operators-p:last-child{margin-bottom:0}
/* ══════════════════════════════════════════════════════════════════════
AIC RUN 7 · "STATE & LIGHT"
----------------------------------------------------------------------
This page does NOT load /assets/aic.css — it carries its own embedded
shell, and the other eleven pages consume the shared file. So every
shell rule RUN 7 changed exists in two places by design, and this block
is the mirror. Keep them moving together; a header or CTA change that
lands in only one of the two ships a site wearing two different heads.

Placed last on purpose: it has to beat the GLASS-X recipe above, which
styles .console, .pain-card, .setup-card, .step, .feat and details as
one family. De-carding .feat without overriding that rule would have
moved five other surfaces with it.
══════════════════════════════════════════════════════════════════════ */
/* ── STATE COLOUR BY ROLE ─────────────────────────────────────────────
The hexes are already declared above. These aliases exist so a rule
reads as the STATE it paints. --state-fail is deliberately referenced
by nothing at rest: nothing on this page fails, so red never appears. */
:root{--lit-technical:rgba(61,123,255,.028);--lit-calm:rgba(138,147,166,.030);--lit-connected:rgba(61,123,255,.020);--lit-dense:rgba(0,0,0,.34);/* halved:at .026 this tint painted the same value as the Crush's own mid-height wash, so "amber lives only in the Crush" did not survive a pixel sample. The Crush keeps an amber floor and this is a whisper under it. */ --lit-warm:rgba(255,176,32,.014);--lit-flat:transparent}
.hero{padding:150px 0 88px}
@media(max-width:600px){
  .hero{padding:92px 0 56px}
}
/* ── TASK E · TYPE SCALE ──────────────────────────────────────────────
Headline 40px at 390 and 64-72px on a 1280-1440 fold. Measure capped at
~16ch so a headline breaks into deliberate lines instead of running the
full 1180px wrap. Body settles at 17px on a 560px measure — about 62
characters, inside the 60-75 band. */
h1.hero-h{font-size:clamp(2.5rem,5.1vw,4.5rem);line-height:1.03;letter-spacing:-.022em;max-width:16ch}
@media(min-width:1020px){
  .hero h1.hero-h{font-size:clamp(2.5rem,5.1vw,4.5rem)}
  /* the headline needs the wider half of the fold to reach 16ch at 72px */
  .hero-grid{grid-template-columns:minmax(0,1.12fr) minmax(0,.88fr)}
}
.hero-sub{font-size:clamp(1.0625rem,1.35vw,1.125rem);line-height:1.58;max-width:560px;text-wrap:pretty}
.step-p{font-size:1.0625rem;line-height:1.55}
.feat-p,.pain-p{font-size:1.0625rem;line-height:1.58;max-width:560px;color:var(--ink-soft)}
.int-mini{font-size:1.0625rem;line-height:1.58;max-width:560px}
.crush-h{font-size:clamp(2rem,3.6vw,3.25rem);line-height:1.06;letter-spacing:-.018em;max-width:16ch;margin-left:auto;margin-right:auto;text-wrap:balance}
.crush-p{font-size:1.0625rem;line-height:1.58;max-width:560px}
/* #setup carries no h2, so this h3 IS that section's head and owes the
44-52 / 32-38 band every other section head on this page hits. Heading
LEVEL stays h3 — that is document structure; this is only its size. */
.setup-h{font-size:clamp(2rem,3.2vw,3rem)}
.crush-now{font-size:.75rem}
.setup-p{font-size:1.0625rem;line-height:1.58;max-width:560px}
.operators-h{font-size:clamp(2rem,3.6vw,3.25rem);line-height:1.06;letter-spacing:-.018em;max-width:16ch;text-wrap:balance}
.operators-p{font-size:1.0625rem;line-height:1.58;max-width:560px}
/* MONO IS FOR OPERATIONAL DATA — a status, a field, a time, a route, a
quote, a phone number. It is not for sentences. Every rule below was
setting a full sentence in JetBrains Mono, which makes sales copy read
like a machine readout and costs real legibility at these sizes.
#crush-now KEEPS mono and is not in this list: it is a live readout of
the reader's own device clock, which is exactly what mono is for. */
.cta-note{text-transform:none}
.setup-note,.int-muted{font-family:var(--sans);letter-spacing:0;text-transform:none;font-size:.9rem;color:var(--ink-mute);line-height:1.55;max-width:560px}
.crush-calc-link a{font-family:var(--sans);letter-spacing:0;text-transform:none;font-size:.95rem}
/* ── TASK D · DE-CARD ─────────────────────────────────────────────────
Cards survive on exactly the surfaces that are discrete records: the two
consoles, the four stage cards, the FAQ accordion and the callback form.
Everything below was explanatory prose wearing a record's chrome. */
.feat,.setup-card,.crush{background:none;border:0;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}
.pain-card{background:none;border:0;border-radius:0;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}
.feat::before,.feat::after,.crush::before,.crush::after,.setup-card::before{display:none}
.feat,.pain-card{padding:1.15rem 0 0;border-top:1px solid var(--line)}
.feat-grid,.pain-grid{gap:1.75rem 2.5rem}
/* The Crush keeps its centred composition — the card is what RUN 7 removes,
not the layout. Left-aligning it as well left the right half of a 1440
fold empty and read as a mistake rather than as negative space. */
.crush{padding:0;text-align:center}
.crush-h,.crush-p,.crush-now,.ledger{margin-left:auto;margin-right:auto}
.setup-card{padding:0;gap:2rem}
@media(min-width:760px){
  .setup-card{padding:0}
}
/* ── TASK G · THE CRUSH ───────────────────────────────────────────────
The card is gone; the amber moves up to the section, where it belongs.
Ambience, not an object: a low amber wash over black with a hairline top
and bottom, so the scene reads as a change of light rather than as one
more box. The heading's italic goes amber too — this section is about
the hour, not about the system, and cyan means the system.

ACCENT COUNT, corrected after measuring. The first cut left THREE on
screen at rest: amber, green, and cyan — the cyan arriving twice, once as
the CTA (a control, arguably chrome) and once as the section kicker,
which is plain text and cannot be argued as a control. The kicker is
amber here now, so the section carries amber (the hour) and green (taken)
as its content accents, and cyan appears only on the button.

The gradient no longer decays to bare void either: at the old floor the
bottom two-thirds of this section measured the same painted value as the
operator-section tint, which made "amber lives only here" untrue below the
fold of the section. It keeps a residual amber roughly twice that tint's.
No red — nothing here fails. */
/* ── § C · SCENE 2 OF 2 · THE CRUSH ──────────────────────────────────────────
The other place ambient light is allowed, and the only place amber appears
at scene scale. A change of light, not one more box. */
section[data-lit="crush"]{background:radial-gradient(ellipse 90% 60% at 50% 0%,rgba(255,176,32,.15),transparent 72%), linear-gradient(180deg,rgba(255,176,32,.06),rgba(255,176,32,.035));border-top:1px solid rgba(255,176,32,.22);border-bottom:1px solid rgba(255,176,32,.22)}
.crush-h em{color:var(--state-ringing)}
#crush .sec-kicker{color:var(--state-ringing)}
.crush-now{margin:0 auto 1.1rem;max-width:560px}
/* THE THREE CRUSH ROWS ARE GONE, replaced by the dispatch ledger. Their
rules are deleted here rather than left behind: they matched nothing
after the swap, and dead CSS on the homepage is bytes on the critical
path for a component that no longer exists. The lesson they carried —
no colour transition on a state, because label and hue must change on
the same frame — moved with the component and is enforced on .lg-state
and .lg-rail in the SHOWROOM block. */
/* ── THE OPERATOR SECTION ─────────────────────────────────────────────
Uncarded. The rail is mono because those three items are entries on a
record, not a sentence — which is the one place mono is right. */
.operators-block{max-width:760px}
.auth-rail{list-style:none;display:flex;flex-wrap:wrap;align-items:center;gap:.4rem 0;margin:1.7rem 0 0;padding:1.05rem 0 0;border-top:1px solid var(--line);font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);font-variant-numeric:tabular-nums}
.auth-rail li{display:inline-flex;align-items:center;white-space:nowrap}
.auth-rail li+li::before{content:'·';color:var(--neutral);margin:0 .7rem}
@media(max-width:600px){
  /* On a phone this was not a rail. The middle item is wider than a 342px
  row, so it wrapped INSIDE itself and the '·' — which is a li+li::before —
  landed at the START of the next line. Rendered, that is four lines: a
  hanging dot, "17 YEARS IN CHAUFFEURED", an orphaned "TRANSPORTATION" with
  no dot, then another hanging dot. Below 600 it stacks, drops the
  separators (a stack does not need them) and gives up TRACKING rather than
  type size — at 12px/.04em the longest item measures ~292px against the
  312px available at 360, so it clears without breaking mid-phrase and
  without dropping under the 12px floor. */
  .auth-rail{display:grid;gap:.45rem;letter-spacing:.04em}
  .auth-rail li+li::before{content:none}
}
/* ── TASK C · CONSOLE PROOF LOOP ──────────────────────────────────────
The honesty chip replaces a green liveness badge that sat beside an
invented caller on a scripted loop. (Describing the rule, not quoting the
retired string — see the note above the FAQ.) 12px floor on both new
labels, the CLAUDE.md minimum, even though it costs a wrap at 390px. */
.hero-console .hc-honest{font-family:var(--mono);font-size:.75rem;letter-spacing:.08em;text-transform:uppercase;color:var(--neutral);line-height:1.4}
@media(max-width:600px){
  .hero-console .hc-honest{flex-basis:100%;letter-spacing:.04em}
}
/* min-height RESERVES TWO LINES, and it is not cosmetic. This line is set
in JetBrains Mono, which arrives over the network with font-display:swap:
it renders one line in the fallback and two once the real face lands, and
that reflow pushed everything below it down. Measured: page CLS went
0.029 -> 0.071 with this element in, and back to 0.030 with it hidden.
Reserving the taller state costs a little air on wide screens and buys
CLS 0 from this element at every width.

The reservation must include the flex ROW-GAP. The first attempt reserved
two line boxes and the padding but not the .3rem between the rows, so it
came up 4.8px short: the box sat at the 56px floor in the fallback font
and grew to 60.8px the moment the real face wrapped it onto two rows —
a 5px push on everything below it, measured at 135ms. Two lines, one
gap, the padding, the border: that is the whole box. */
.hero-console .hc-result{display:flex;flex-wrap:wrap;align-items:center;gap:.3rem .5rem;font-family:var(--mono);font-size:.75rem;line-height:1.45;letter-spacing:.11em;text-transform:uppercase;color:var(--neutral);margin:0 0 1rem;padding:.6rem .75rem;min-height:calc(2 * 1.45em + .3rem + 1.2rem + 2px);border:1px solid var(--line);border-radius:8px;background:rgba(61,123,255,.05)}
.hero-console .hc-r{color:var(--ink)}
.hero-console .hc-arrow{color:var(--neutral)}
@media(max-width:600px){
  .hero-console .hc-result{letter-spacing:.06em;gap:.25rem .4rem}
  /* At 390 the dots, "Dispatch Console" and "READY FOR DISPATCH" together
  want ~316px of a 310px row, so the chip broke across two lines INSIDE
  its own pill. Letting the title row wrap puts the chip on its own line
  intact, which is what a status badge should look like. */
  .hero-console .console-title{flex-wrap:wrap}
  .hero-console .hc-state{margin-left:0;white-space:nowrap;letter-spacing:.08em}
}
/* State chip. One state on screen at a time, so the section never carries
more than the accent it is actually in plus the console's own cyan. */
/* ── RUN 13 · STATE LAW, APPLIED WHERE IT HAD NEVER REACHED ──────────────────
   § 2's STATE LAW: label text and colour change on the SAME FRAME, and it was
   written after a 450ms crossfade left rows reading one state while painted
   another in 18.6% of frames. RUN 10 gave `transition:none` to the callback
   console's chip and to the dispatch ledger. This chip — scene 1 of GLOW LAW,
   on the homepage fold — kept a 250ms colour transition and nobody re-read the
   law against it.

   index.html's setState() writes textContent and className in one synchronous
   block, so the WORD changes instantly and the COLOUR takes a quarter second
   to follow. For that quarter second the chip reads "Trip captured" in the
   previous state's blue. That is the defect the law names, not an instance of
   the law being satisfied by other means.

   Found by the RUN 13 parity harness rather than by reading: two runs of
   identical code recorded the same element, with the same class, in two
   different colours — which is only possible while a colour is in flight.
   ─────────────────────────────────────────────────────────────────────────── */
.hero-console .hc-state{letter-spacing:.12em;padding:3px 10px;transition:none}
.hero-console .hc-state.ringing{color:var(--state-ringing);background:rgba(255,176,32,.10);border:1px solid rgba(255,176,32,.45)}
.hero-console .hc-state.live,.hero-console .hc-state.quote{color:var(--sky);background:rgba(61,123,255,.12);border:1px solid var(--line-hot);animation:none}
.hero-console .hc-state.captured,.hero-console .hc-state.ready,.hero-console .hc-state.complete{color:var(--state-captured);background:rgba(46,230,168,.12);border:1px solid rgba(46,230,168,.45);animation:none}
/* "fields complete in green" — an inset rule, not a border, so nothing on
the grid moves by a pixel when the state lands. */
.hero-console .c-card,.hero-console .c-row{transition:box-shadow .45s ease}
/* .is-filled, not a blanket .hc-captured descendant: a card goes green when
ITS OWN fields are all in, so nothing is marked complete while it is
still empty. Set from JS after every timeline step. */
.hero-console.hc-captured .c-card.is-filled,.hero-console.hc-captured .c-row.is-filled{box-shadow:inset 2px 0 0 var(--state-captured)}
@media (prefers-reduced-motion:reduce){
  .hero-console .hc-state.ringing{animation:none}
  .hero-console .c-card{transition:none}
}
/* ── TASK F · FOUR-STAGE PROGRESSION RAIL ─────────────────────────────
The stage cards were four islands, each wearing a 4px cyan dot that
encoded nothing. The dot is now the state node and the rail is the
sequence: neutral ahead, cyan on the current stage, green behind it.
The grid is pinned to 1 column below 1020px and 4 above it — the old
auto-fit produced a 2x2 at tablet widths, and a horizontal rail across
a two-row grid draws a line through one row and abandons the other.
Padding is down 20% (1.6rem to 1.28rem). */
/* max-width in the single-column band is LOAD-BEARING. Pinning the grid to
one column fixed the rail, but at 850-1019px it also handed each card the
full 1132px and the paragraph measured 86 characters — well past the
60-75 band. 640px puts it back at ~63ch and matches the 560px measure the
rest of the page uses. */
.steps{position:relative;grid-template-columns:1fr;gap:1rem;padding-left:28px;max-width:640px}
/* --rail-tail is set from JS to the distance between the LAST node and the
bottom of the grid, so the vertical rail stops on the last stop instead
of running 200px past it — the same defect the desktop branch fixes with
right:calc(25% - 17.5px). It falls back to 14px if JS never runs. */
.steps::before,.steps::after{content:'';position:absolute;top:14px;bottom:var(--rail-tail,14px);left:5px;width:1px;pointer-events:none}
.steps::before{background:var(--line)}
/* Default 1, not 0. With JS off the four stages are all true — the system
does all four — so the honest resting state is complete, exactly like the
Crush rows. JS rewinds to 0 on viewport entry and plays forward. */
.steps::after{background:var(--state-captured);opacity:.8;transform:scaleY(var(--rail-p,1));transform-origin:center top;transition:transform .7s cubic-bezier(.4,0,.2,1)}
/* overflow:visible is LOAD-BEARING. The card carried overflow:hidden to clip
the decorative ::before gradient it used to have; that gradient is gone
and the ::after is now the rail's state node, which sits OUTSIDE the card
so it can land on the rail. With the old overflow the nodes rendered and
were clipped to nothing — the rail drew, the states fired, and not one
dot was on screen. Caught in the render gate, not in the CSS. */
.step{padding:1.28rem;overflow:visible}
.step::before{display:none}
.step::after{content:'';position:absolute;top:20px;left:-29px;width:11px;height:11px;border-radius:50%;background:var(--midnight);border:2px solid var(--state-idle);box-shadow:none;transition:background .35s ease,border-color .35s ease,box-shadow .35s ease}
.step.is-current::after{border-color:var(--state-listening);background:var(--state-listening);box-shadow:0 0 0 4px rgba(61,123,255,.20)}
.step.is-done::after{border-color:var(--state-captured);background:var(--state-captured)}
@media(min-width:1020px){
  .steps{grid-template-columns:repeat(4,minmax(0,1fr));padding-left:0;padding-top:26px;max-width:none}
  /* The rail must END on the fourth node, not run past it to the grid edge
  — a line that continues beyond the last stop reads as a progress bar
  that never finished. For a 4-column grid with a 1rem gap the last
  node's centre sits at 3*(col+gap)+5.5px from the left, which reduces
  to exactly 25% - 17.5px from the right at any container width. */
  .steps::before,.steps::after{top:11px;bottom:auto;left:5px;right:calc(25% - 17.5px);width:auto;height:1px}
  .steps::after{transform:scaleX(var(--rail-p,1));transform-origin:left center}
  /* -21/-1 not -20/0: `left`/`top` on an absolutely positioned child are
  measured from the padding edge, which the card's 1px border offsets. */
  .step::after{top:-21px;left:-1px}
}
@media (prefers-reduced-motion:reduce){
  .steps::after{transition:none}
  .step::after{transition:none}
}
/* The old setup label was the longest string on the site and it carried a
hyphen, which is a break opportunity — both homepage setup buttons
rendered it on two lines between 320 and 768px. § F replaces it with a
shorter label that has no hyphen in it, and the decorative waveform that
used to sit inside these buttons is gone, so the same control now has
roughly 90px more room than the string needs. nowrap stays as the belt. */
.btn{white-space:nowrap}
@media(max-width:600px){
  .btn{font-size:.75rem;letter-spacing:.03em}
}
/* The header clearance for #ava-callback moved to the § 1 ANCHOR GUTTER
block in assets/circulant.css — one rule for every anchor target on all
twelve pages. A copy here would have ADDED to it, not replaced it. */
/* The console's own halo pulse animated box-shadow forever, on or off
screen — the same cost this run removed from the JS loop, left running in
CSS. It is gated on the same IntersectionObserver now. */
.hero .console{animation-play-state:paused}
.hero .console.hc-onscreen{animation-play-state:running}
/* The clock line is sans now. Only its FIRST clause is a device readout;
the second ("Dinner and hotel runs.") is editorial, and mono is for
operational data, not for a sentence that happens to sit beside one.
tabular-nums stays so the minute does not jitter as it renders. */
.crush-now{font-family:var(--sans);letter-spacing:.01em;text-transform:none}
/* A pulsing dot means "this is happening now". The sample ticket below is
a finished record that is not happening at all, and it is the only
.live-badge left on the page now that the hero console carries the
honesty chip. The state stays green — it really is captured — and the
animation goes. */
.live-badge::before{animation:none}

/* ══ RUN 10 · SHOWROOM v2.0 ══════════════════════════════════════════════════
   CANONICAL BLOCK. Byte-identical in assets/aic.css and in the embedded <style>
   in index.html, and it is the LAST block in both files. That position is
   load-bearing. The callback form, the button system and the card recipe each
   already existed in two copies that were functionally equivalent by DIFFERENT
   routes rather than textually equal, so a patch applied to one of them left
   the other standing — which is § 1's THREE CSS HOMES trap in its most
   expensive form. One trailing block beats both older copies at equal
   specificity and can be byte-compared by a gate.

   Where a rule here overrides one that lived inside a media query, the media
   query is RESTATED below. A later unconditioned rule wins at every width, and
   that is exactly how "fixed on desktop, broken on a phone" ships.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── B · SURFACE SEPARATION ──────────────────────────────────────────────────
   The acceptance bar for this run is a phone at 50% brightness. --surface on
   --midnight measures 1.07:1, which is why the hairline has always been
   mandatory here; what was missing was the SECOND step. Nested and active
   layers now sit on --surface-2 with a 1px top light, so a field inside a card
   reads as sitting in the card rather than as a hole cut through it. The
   nested layers used to be rgba(7,11,20,.55) — DARKER than their own parent,
   which is the one direction that cannot read as depth on a dark ground. */
.c-card,.tk{background:var(--surface-2);border:1px solid var(--line);
  box-shadow:inset 0 1px 0 rgba(127,178,255,.06)}

/* ── C · BUTTON SYSTEM v2 ────────────────────────────────────────────────────
   The fill changes on hover instead of a brightness filter. filter:brightness()
   lightens the BEVEL and the border along with the fill, so the whole control
   washed out by the same amount rather than reading as a state change; and on
   a control that already carries an inset highlight it flattens the top edge
   that gives the button its shape. An explicit hover fill plus a hotter border
   moves only what should move. */
.btn-primary,.tel-btn,.demo-play-btn,.rail-call,.nav-book{
  background:var(--action-blue);color:#FFFFFF;
  border:1px solid rgba(127,178,255,.30);
  box-shadow:inset 0 1px 0 rgba(127,178,255,.35);
  font-family:var(--sans);letter-spacing:.005em}
.btn-primary,.demo-play-btn,.nav-book,.cb-submit{font-weight:600}
.btn-primary:hover,.tel-btn:hover,.demo-play-btn:hover,.nav-book:hover{
  background:var(--action-blue-hover);border-color:rgba(127,178,255,.55);
  color:#FFFFFF;transform:translateY(-1px);filter:none}
.btn-primary:active,.tel-btn:active,.demo-play-btn:active,.nav-book:active{
  background:#1A4CC2;transform:translateY(0);filter:none}
/* The rail is a fixed bar. A hover lift inside it would slide the control out
   from under the finger that is already on it. */
.rail-call:hover{background:var(--action-blue-hover);border-color:rgba(127,178,255,.55);color:#FFFFFF}
.rail-call:active{background:#1A4CC2;color:#FFFFFF}

/* Heights. 54 desktop, 56 on a phone, where the control is the whole width of
   the screen and the thumb is the pointer. Nav and rail are chrome and stay on
   44: the header is capped at 64px on a phone and 80 on desktop, and a 54px
   control inside 14px of padding would put the desktop bar at 82. */
.btn,.cb-submit,.demo-play-btn,.demo-call-btn{min-height:54px}
@media(max-width:767.98px){.btn,.cb-submit,.demo-play-btn,.demo-call-btn{min-height:56px}}

/* ── C · CTA DE-DUPLICATION ──────────────────────────────────────────────────
   Three filled blue phone surfaces could land in one 390 viewport: the header
   chip, the hero primary and the sticky rail. Measured on the 390 fold before
   this run, two of them sat 40px apart and the eye had no idea which one was
   the page's ask. The header chip is the OUTLINED utility at every width now
   and keeps its digits; the hero keeps THE filled primary; the rail still arms
   only once the hero cluster has left the top of the viewport, which is the
   existing suppression rather than a new one.

   The word "Call" survives down to 380px rather than 1024. Measured at 390:
   123 lockup + 8 + 177 chip + 8 + 44 burger = 360 into 366 available. At 360 it
   does not fit, so the verb drops there and the DIGITS still read — and the
   aria-label still CONTAINS the visible text, which is what voice control
   matches on. */
.nav-cta{background:var(--surface);color:var(--ink);border:1px solid var(--line);
  box-shadow:none;font-weight:500}
.nav-cta:hover{background:var(--surface);color:var(--sky);border-color:var(--sky);filter:none}
@media(max-width:1023.98px){
  .nav-cta{background:var(--surface);color:var(--ink);border-color:var(--line);box-shadow:none}
  .nav-cta:hover{color:var(--sky);border-color:var(--sky);filter:none}
}
/* THE VERB STAYS HIDDEN BELOW 1024, and that is a measurement, not a
   preference. Showing "Call" took the chip from 147px to 177px at 390 and to
   185px at 430 — and 430 is the TIGHTEST width on this site, not 360, because
   below 400 the bar drops to 12px padding AND the burger loses its "Menu"
   label. Measured with the verb on: the nav row ran to 441.7px inside a 414px
   content box on all twelve pages. The digits are what had to survive, and they
   do; the aria-label still carries the full "Call (414) 775-0019" and still
   CONTAINS the visible text, which is what voice control matches on. */

/* ── D · THE CALLBACK CONSOLE ────────────────────────────────────────────────
   The form was a card with a label on it. It is an instrument now, and it says
   what it is doing: a 44px header row carrying the module name and its own
   state, over a nested body on --surface-2. The hot top hairline is the only
   place --line-hot appears on a resting panel, and it marks the one panel on
   the fold that DOES something. */
.cb-form{background:var(--surface);border:1px solid var(--line);
  border-top:1px solid var(--line-hot);border-radius:8px;padding:0}
.cb-head{display:flex;align-items:center;justify-content:space-between;gap:.75rem;
  width:100%;min-height:44px;padding:0 1rem;margin:0;
  background:none;border:0;border-bottom:1px solid var(--line);cursor:pointer;
  font-family:var(--mono);font-size:.75rem;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--sky);text-align:left}
.cb-head:focus-visible{outline:2px solid var(--signal-blue);outline-offset:-2px}
.cb-title{white-space:nowrap}
/* STATE LAW. The dot and the word are one element, so they cannot disagree:
   there is no colour transition on this rule, and the class that repaints it is
   the same class that rewrites the word. A 450ms crossfade once left rows
   reading one state while painted another in 18.6% of frames. */
.cb-status{display:inline-flex;align-items:center;gap:.45rem;flex:none;
  font-family:var(--mono);font-size:.75rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--neutral);white-space:nowrap;transition:none}
.cb-status::before{content:'';width:6px;height:6px;border-radius:50%;
  background:currentColor;flex:none}
.cb-form.is-done .cb-status{color:var(--success-green)}
.cb-body{padding:1rem;background:var(--surface-2);border-radius:0 0 7px 7px;
  box-shadow:inset 0 1px 0 rgba(127,178,255,.06)}
/* THE COLLAPSE IS CSS-FIRST, and that is a MEASURED requirement rather than a
   preference. The first cut shipped the card open and let JS collapse it on
   load. Measured on the homepage at mobile: CLS 0.130 in two of three runs —
   everything below the hero jumped up the instant the script ran, and the
   twelve-page median hid it at 0.000 because the shift did not fire every time.
   An intermittent 0.13 is worse than a steady one; it passes review and fails
   users. CSS now decides the initial state before first paint and JS only ever
   writes an EXPLICIT override, so there is nothing left to shift. */
@media(max-width:767.98px){
  .cb-form:not([data-collapsed="false"]) .cb-body{display:none}
  .cb-form:not([data-collapsed="false"]) .cb-head{border-bottom:0}
}
.cb-form[data-collapsed="true"] .cb-body{display:none}
.cb-form[data-collapsed="true"] .cb-head{border-bottom:0}
/* § D · buttons are Space Grotesk 600. .tel-main is the phone control's label
   and was the one that kept 500. */
.tel-main{font-weight:600}
.cb-sub{margin-bottom:.9rem}

/* 12px FLOOR. These labels rendered at 9.92px — .62rem — which is under the
   hard floor § 3 sets and has been since the form shipped. A probe for it is
   part of this run's gate so it cannot come back. */
.cb-label{font-family:var(--mono);font-size:.75rem;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--neutral)}
.cb-input{min-height:54px;background:var(--input-bg);border:1px solid var(--line);
  border-radius:8px;box-shadow:none;color:var(--ink)}
.cb-input::placeholder{color:var(--neutral)}
.cb-input:focus{outline:none;border-color:var(--signal-blue);
  box-shadow:0 0 0 3px rgba(61,123,255,.15)}

/* THE NATIVE BOX IS GONE. A default checkbox paints a white 20px square, and on
   this ground that was the single brightest object on the fold — brighter than
   the primary CTA, attached to the one control on the page that is not an ask.
   appearance:none plus a clip-path tick keeps the semantics and the keyboard
   behaviour and gives back the hierarchy. */
.cb-consent input{appearance:none;-webkit-appearance:none;position:relative;
  width:20px;height:20px;margin-top:2px;flex:none;cursor:pointer;
  background:var(--surface);border:1px solid #2A3650;border-radius:5px;
  transition:background .12s,border-color .12s}
.cb-consent input:checked{background:var(--action-blue);border-color:var(--action-blue)}
.cb-consent input:checked::after{content:'';position:absolute;inset:0;background:#FFFFFF;
  clip-path:polygon(14% 50%,24% 40%,38% 54%,76% 16%,86% 26%,38% 74%)}
.cb-consent input:focus-visible{outline:2px solid var(--signal-blue);outline-offset:2px}

/* The submit RESTS as a ghost and only takes the fill once both fields validate
   and consent is ticked. Until then the hero's phone control is the one filled
   thing on the fold; after then, the reader has already chosen this path. */
.cb-submit{background:var(--surface);color:var(--ink);border:1px solid var(--line);
  box-shadow:none}
.cb-submit:hover:not(:disabled){background:var(--surface);border-color:var(--sky);
  color:var(--sky);transform:translateY(-1px)}
.cb-form.is-ready .cb-submit{background:var(--action-blue);color:#FFFFFF;
  border-color:rgba(127,178,255,.30);box-shadow:inset 0 1px 0 rgba(127,178,255,.35)}
.cb-form.is-ready .cb-submit:hover:not(:disabled){background:var(--action-blue-hover);
  border-color:rgba(127,178,255,.55);color:#FFFFFF}
.cb-form.is-ready .cb-submit:active{background:#1A4CC2;transform:translateY(0)}

/* ── E · THE DISPATCH LEDGER ─────────────────────────────────────────────────
   The Crush claims three calls land at once and all three get taken. Three rows
   labelled "Captured" asserted the end state without ever showing the middle of
   it. The ledger shows the overlap: 01 finishing while 02 is on the call and 03
   is ringing. Terminal state ships in the markup, so a screen reader, a JS-off
   browser and a reduced-motion reader all get the true resting record and the
   scene only ever rewinds it. */
.ledger{max-width:560px;margin:1.9rem auto 2.1rem;text-align:left;
  background:var(--surface-2);border:1px solid var(--line);border-radius:12px;
  overflow:hidden;position:relative}
.lg-head{display:flex;align-items:center;justify-content:space-between;gap:.75rem;
  min-height:40px;padding:0 .9rem;border-bottom:1px solid var(--line);
  font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--neutral)}
.lg-note{color:var(--neutral);white-space:nowrap}
.lg-row{display:grid;grid-template-columns:2px auto 1fr auto;
  align-items:center;column-gap:.75rem;row-gap:.2rem;
  min-height:52px;padding:.5rem .9rem .5rem 0;
  border-bottom:1px solid var(--line);
  font-family:var(--mono);font-size:.75rem;letter-spacing:.1em;text-transform:uppercase;
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
.lg-row:last-child{border-bottom:0}
.lg-rail{grid-row:1/-1;align-self:stretch;width:2px;background:var(--neutral);
  transition:none}
.lg-time{color:var(--ink-mute)}
.lg-id{color:var(--ink)}
.lg-trip{color:var(--ink-mute);letter-spacing:.06em;text-transform:none;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lg-state{display:inline-flex;align-items:center;gap:.45rem;white-space:nowrap;
  color:var(--neutral);transition:none}
.lg-state::before{content:'';width:6px;height:6px;border-radius:50%;
  background:currentColor;flex:none}
.lg-row.is-ringing .lg-rail,.lg-row.is-ringing .lg-state{color:var(--state-ringing)}
.lg-row.is-ringing .lg-rail{background:var(--state-ringing);opacity:.55}
.lg-row.is-oncall .lg-rail,.lg-row.is-oncall .lg-state{color:var(--state-listening)}
.lg-row.is-oncall .lg-rail{background:var(--state-listening);opacity:.55}
.lg-row.is-captured .lg-rail,.lg-row.is-captured .lg-state{color:var(--state-captured)}
.lg-row.is-captured .lg-rail{background:var(--state-captured);opacity:.55}
.lg-row.is-done .lg-rail,.lg-row.is-done .lg-state{color:var(--state-captured)}
.lg-row.is-done .lg-rail{background:var(--state-captured);opacity:1}
/* ONE active pulse at a time, and only while the scene owns the screen. The
   observer adds .is-live on entry and takes it off on exit, so an offscreen
   ledger animates nothing at all. */
.ledger.is-live .lg-row.is-ringing .lg-state::before{animation:pulse 1s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){.lg-state::before{animation:none!important}}
/* Below 560 the row stacks to two lines: the record on top, the state under it.
   The rail spans both, which is what keeps it reading as one row. */
@media(max-width:559.98px){
  .lg-row{grid-template-columns:2px auto 1fr;padding:.6rem .8rem .6rem 0}
  .lg-state{grid-column:2/-1;margin-top:.15rem}
  .lg-trip{white-space:normal}
}

/* ── I · FOOTER + CHROME ─────────────────────────────────────────────────────
   The mobile footer was one column of 44px rows: twelve links, 620px of
   scroll, and the © line pushed so far down that the document appeared to end
   in empty space. Two columns halves it. The links go --ink because at
   --ink-mute they read as disabled on a phone at half brightness, and a footer
   link is the only navigation left at the bottom of the page. */
.foot-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:900px){.foot-grid{grid-template-columns:repeat(4,minmax(0,1fr))}}
.foot-col a{color:var(--ink)}
.foot-col a:hover{color:var(--sky)}
footer{padding-bottom:32px}
.foot-bottom{padding-top:1.5rem;margin-top:1.5rem}

/* ── D/E · LEDGER GRID, CORRECTED ────────────────────────────────────────────
   Five children per row: rail, time, id, trip, state. The first cut declared
   four columns and CSS Grid silently created an implicit fifth ROW rather than
   erroring, which reads as a row that has quietly grown a second line. */
.lg-row{grid-template-columns:2px auto auto 1fr auto}
/* MEASURED AT 390. The trip cell carried "Airport run, 2 passengers" and wrapped
   inside its own column, which made a stacked row THREE lines instead of two and
   broke the ledger's rhythm on exactly the device this brand is read on. The
   passenger count was the part that had to go: it is already in the console's
   own PASSENGERS field two sections up, and repeating it here bought a wrap. */
@media(max-width:559.98px){
  .lg-row{grid-template-columns:2px auto auto 1fr}
  .lg-state{grid-column:2/-1}
}

/* ── D · MOBILE FOLD ─────────────────────────────────────────────────────────
   390x844 has to carry kicker, headline, ONE line of subhead, the phone control
   and the callback module's header row. The subhead ran five lines on a phone
   and pushed the primary control under the fold. It is one sentence there now
   and the detail returns from 768 up. One DOM, one crawlable paragraph — the
   alternative, a second copy of the sentence hidden at the other breakpoint,
   ships duplicate copy for a crawler to read twice. */
@media(max-width:767.98px){.hero-sub-more{display:none}}

/* ── F · GOOGLE LEAD PROTECTION ──────────────────────────────────────────────
   No card, no accent object, no second colour. This section is a claim about
   what happens AFTER the click, so it is set as prose with the one control the
   whole site uses, and the attribution line closes it. */
.glp-fine{font-family:var(--mono);font-size:.75rem;letter-spacing:.06em;
  line-height:1.6;color:var(--neutral);margin-top:1.5rem;max-width:560px}
#lead-protection .sec-sub{margin-bottom:1.75rem}

/* ── I · FOOTER, MEASURED AND CORRECTED ──────────────────────────────────────
   The two-column footer put the brand block and the Product column in the same
   row, and the NAP paragraph ran straight through the link labels beside it.
   The brand block takes a full row at EVERY width now — it did so from 900 up
   already, and the rule simply never covered the widths where it mattered
   most. A footer that overlaps itself on a phone is not a compact footer. */
.foot-brand{grid-column:1/-1}
@media(min-width:900px){.foot-brand{padding-bottom:1.25rem;border-bottom:1px solid var(--line)}}
.foot-col a{display:flex;align-items:center;padding:.35rem 0}
.foot-nap{max-width:38ch}

/* ── G · THE INTEGRATION TABLE ───────────────────────────────────────────────
   A real <table> with real header cells, because the content genuinely is
   tabular: three facts about each of four systems. A screen reader can say
   "Limo Anywhere, dispatcher action" and then read the cell. A card grid would
   have looked the same and carried none of that, and an answer engine reading
   the page gets a labelled row instead of three loose sentences.

   It scrolls inside its OWN container under 720px. The page body never scrolls
   sideways — that is a gate, and a wide table is the classic way to fail it. */
.tbl-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;
  border:1px solid var(--line);border-radius:12px;background:var(--surface);
  box-shadow:var(--raise)}
.tbl{width:100%;border-collapse:collapse;min-width:620px;text-align:left}
.tbl-cap{caption-side:top;padding:.85rem 1rem;border-bottom:1px solid var(--line);
  font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--neutral);text-align:left}
.tbl th,.tbl td{padding:1rem;border-bottom:1px solid var(--line);vertical-align:top}
.tbl thead th{font-family:var(--mono);font-size:.75rem;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--neutral);background:var(--surface-2);
  box-shadow:inset 0 1px 0 rgba(127,178,255,.06);white-space:nowrap}
.tbl tbody th{font-family:var(--sans);font-size:1rem;font-weight:600;color:var(--ink);
  white-space:nowrap}
.tbl td{font-size:.95rem;color:var(--ink-soft);line-height:1.55;max-width:34ch}
.tbl tbody tr:last-child th,.tbl tbody tr:last-child td{border-bottom:0}

/* ── H · THE BOOKING INSET ───────────────────────────────────────────────────
   The scheduling widget is a white iframe on a midnight page and there is no
   way to restyle the inside of it. Left bare it read as the site ending and
   something else starting. A dark panel above it, in the site's own mono, ends
   the page's voice deliberately — so the white box reads as an inset the page
   put there rather than as a seam. The iframe, its loader and its routing are
   untouched: § 10 trap 15 is why. */
.book-inset{display:flex;flex-wrap:wrap;align-items:baseline;gap:.35rem 1rem;
  margin:0 0 1rem;padding:.9rem 1rem;
  background:var(--surface-2);border:1px solid var(--line);
  border-top:1px solid var(--line-hot);border-radius:8px;
  box-shadow:inset 0 1px 0 rgba(127,178,255,.06)}
.book-inset b{font-family:var(--mono);font-size:.75rem;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--sky)}
.book-inset span{font-size:.95rem;color:var(--ink-soft)}

/* ── § 3 · THE 12px TYPE FLOOR, ENFORCED ─────────────────────────────────────
   The brief named ONE violation — .cb-label at .62rem. A probe was written for
   it, and the probe found NINE MORE that had been live for as long as the pages
   have. Measured, in rendered pixels:

     .tk-label   9.60   .calc-k     9.60   .calc-label 9.92
     .hc-state  10.88*  .ticket-bar 10.88  .card-num  11.20
     .console-title 11.20  .calc-note 11.20  .foot-bottom 11.20
     .vs-a      11.52   .tk-sub     11.84
     (* .6rem in one home and .7rem in the other — the same label at two sizes,
        which is the THREE CSS HOMES trap wearing a typographic hat.)

   Every one of them is mono, uppercase, wide-tracked operational text: field
   names, ticket labels, calculator keys, the copyright line. That is exactly
   the category the floor exists for — a dispatcher reads these at 2AM on a
   cracked screen, and 9.6px of tracked uppercase is a smear.

   All of them go to .75rem = 12px. That is the floor, not a target, and the
   gate now fails the run if anything renders under it on any of twelve pages at
   any of six viewports. This block is the ONE place the floor is set, so the
   next rule that wants to be small has to argue with a single line of CSS.
   ────────────────────────────────────────────────────────────────────────── */
.card-num,.ticket-bar,.tk-label,.tk-sub,.vs-a,
.calc-label,.calc-k,.calc-note,.legal-meta,
.foot-bottom,.console-title,.hc-state,
.hero-console .hc-state{font-size:.75rem}

/* ── I · THE MOBILE FOOTER, SIZED FROM A MEASUREMENT ─────────────────────────
   The two-column grid on its own put 84 wrapped labels on the site. Measured at
   360: the column was 144px and the longest label ("Works with your software",
   "After-hours limo dispatch") ran 169px. Three numbers had to move together,
   and the fix is all three rather than a font size alone:

     footer .wrap padding   1.5rem -> 1rem   (+16px of usable row)
     .foot-grid column gap  1.5rem -> .5rem  (+16px)
     .foot-col link         14px   -> 13px   (169px label -> 157px)

   Which lands 157px into a 160px column at 360, and 157 into 175 at 390. 13px
   is above the 12px floor with a pixel to spare and is the size a footer link
   should be anyway — it is secondary navigation, not body copy. Re-measured
   after the change: zero wrapped labels at 360, 390 and 430.
   ────────────────────────────────────────────────────────────────────────── */
@media(max-width:599.98px){
  footer .wrap{padding:0 .75rem}
  .foot-grid{gap:1.5rem .375rem}
  .foot-col a{font-size:.8125rem}
}
/* RE-MEASURED, and the first cut missed by ONE PIXEL. At 1rem padding and a
   .5rem gap the column was 160px and the wrapped label's two rects summed to
   157 — which looks like it fits and does not, because the sum of the rects
   EXCLUDES the space character the break consumed. The true single-line width
   was ~161. The rects tell you where the text went, not how wide it wanted to
   be; when a label wraps at all, add the space back before believing the
   number. 12px padding and a 6px gap give a 165px column against 161px of
   label, and the probe reads zero wraps at 360, 390 and 430. */

/* ══ RUN 11 · TACTILE CUT ════════════════════════════════════════════════════
   CANONICAL BLOCK. Byte-identical in assets/aic.css and in the embedded <style>
   in index.html, and it is now the LAST block in both. Same reasoning as RUN
   10's block directly above: a trailing block beats every older copy at equal
   specificity and can be byte-compared by a gate.

   Selectors that live on only one of the two hosts ship in both copies anyway.
   .console / .step / .feat / .crush / .setup-card / .demo-play-bar are
   homepage-only; .card--stage / .ticket-card / .calc are aic.css-only.
   Splitting the block to save those bytes would give up the one property that
   makes the pair checkable at all. Byte-identity is the guard. A shorter file
   is not.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── § 2 · PRIMARY CONTROL v3 ────────────────────────────────────────────────
   The signature is an 8px chamfer on the upper-right corner. Everything about
   the structure below exists because of ONE measured fact:

     clip-path is applied AFTER filter and it clips EVERYTHING the element
     paints — its outline and its box-shadow included.

   Read as pixels rather than as spec prose, on a 200x60 fixture over --midnight:
   an outline at offset +4 rendered 7,11,20 (the page) with clip-path on and
   61,123,255 (the ring) with it off. A box-shadow below the box: gone. A
   filter:drop-shadow below the box: also gone — filter runs first, so the
   shadow it produces is inside the region clip-path then throws away.

   A control that is chamfered by clipping ITSELF therefore has no elevation and
   NO FOCUS RING, and a removed focus ring is banned outright. So the chamfer
   moves off the host:

     HOST      unclipped. Carries the label, the exterior elevation as a
               drop-shadow, and the sitewide 2px --signal-blue focus ring, which
               survives because nothing clips it.
     ::before  clipped. Carries the fill, the 1px border, the top bevel and the
               bottom depth, so all four follow the chamfer instead of ignoring
               it. z-index:-1 puts it behind the label inside the stacking
               context the host's own filter creates.

   Re-read as pixels after the change: ring 61,123,255 at +4 outside the host,
   drop-shadow present below it, fill 30,86,214, top bevel row 63,118,228,
   bottom depth row 23,67,167, and the top-right corner cut to background.

   THE ELEVATION IS NEUTRAL BLACK. § 4 GLOW LAW allows ambient light in exactly
   two scenes and neither of them is a button; a coloured halo here would be the
   rule this brand has broken most often. 0 8px 24px rgba(0,0,0,.35) is --raise
   geometry at a lighter alpha, which is what a control gets against a panel.

   .rail-call and .nav-book are deliberately NOT in this list. They are chrome —
   the same object on every page inside a fixed bar — and they keep the RUN 10
   button v2 recipe: no chamfer, no exterior elevation. .btn-ghost is untouched.
   ────────────────────────────────────────────────────────────────────────── */
.btn-primary,.tel-btn,.demo-play-btn{
  position:relative;background:none;border:0;border-radius:10px;box-shadow:none;
  color:#FFFFFF;font-family:var(--sans);letter-spacing:.005em;
  filter:drop-shadow(0 8px 24px rgba(0,0,0,.35));
  transition:transform 160ms cubic-bezier(.2,.8,.2,1),
             filter 160ms cubic-bezier(.2,.8,.2,1)}
.btn-primary::before,.tel-btn::before,.demo-play-btn::before{
  content:'';position:absolute;inset:0;z-index:-1;
  background:var(--action-blue);border:1px solid rgba(127,178,255,.45);
  border-radius:10px;
  box-shadow:inset 0 1px 0 rgba(127,178,255,.35),inset 0 -2px 0 rgba(0,0,0,.22);
  clip-path:polygon(0 0,calc(100% - 8px) 0,100% 8px,100% 100%,0 100%);
  transition:background 160ms cubic-bezier(.2,.8,.2,1),
             border-color 160ms cubic-bezier(.2,.8,.2,1)}
/* filter IS RESTATED HERE AND IT HAS TO BE. RUN 10's hover rule sets
   filter:none to retire the old brightness() lift, and it matches the same
   three selectors at the same specificity. A hover rule that only declares
   transform therefore inherits nothing and loses filter to that earlier
   :none — measured on the live page, the elevation vanished the moment the
   pointer landed and came back when it left. The cascade does not merge two
   rules; it resolves each PROPERTY independently. */
.btn-primary:hover,.tel-btn:hover,.demo-play-btn:hover{
  color:#FFFFFF;transform:translateY(-1px);
  filter:drop-shadow(0 8px 24px rgba(0,0,0,.35))}
.btn-primary:hover::before,.tel-btn:hover::before,.demo-play-btn:hover::before{
  background:var(--action-blue-hover);border-color:rgba(127,178,255,.60)}
/* PRESS 70ms, RELEASE 160ms. The pair is the whole point: a control that leaves
   as slowly as it arrives reads as a slider, not as something that was pushed.
   The 2px drop and the shadow collapsing from 24px of blur to 8px are one
   gesture — the button moves toward the page and its gap to the page closes. */
.btn-primary:active,.tel-btn:active,.demo-play-btn:active{
  transform:translateY(2px) scale(.985);
  filter:drop-shadow(0 2px 8px rgba(0,0,0,.35));
  transition-duration:70ms}
.btn-primary:active::before,.tel-btn:active::before,.demo-play-btn:active::before{
  background:#1A4CC2;transition-duration:70ms}
/* The play control's second state is a ghost. Its fill and border live on the
   ::before now, so the rule that paints that state has to move with them or the
   button stays filled blue while claiming to be playing. */
.demo-play-btn[data-state="playing"]{filter:none}
/* ...and the label with them. The primary's :hover rule sets color:#FFFFFF at
   (0,2,0) and comes later than the playing rule at the same weight, so with the
   pointer still resting on the control it had just started — which is where the
   pointer always is — the ghost rendered a WHITE label. Legible at 15.7:1, but
   a ghost's hover in this system goes --sky, and a state that only misbehaves
   while the pointer is on it is a state nobody sees misbehaving. */
.demo-play-btn[data-state="playing"]:hover{color:var(--sky)}
.demo-play-btn[data-state="playing"]::before{
  background:var(--surface);border-color:var(--line-hot);
  box-shadow:none}
@media (prefers-reduced-motion:reduce){
  .btn-primary,.tel-btn,.demo-play-btn,
  .btn-primary::before,.tel-btn::before,.demo-play-btn::before{transition:none}
  .btn-primary:hover,.tel-btn:hover,.demo-play-btn:hover,
  .btn-primary:active,.tel-btn:active,.demo-play-btn:active{transform:none}
  /* colour states only — the elevation holds at its resting value rather than
     snapping between two shadows with the transition switched off */
  .btn-primary:active,.tel-btn:active,.demo-play-btn:active{
    filter:drop-shadow(0 8px 24px rgba(0,0,0,.35))}
}

/* ── § 3 · THE RAIL RESERVE, MEASURED ────────────────────────────────────────
   The bar is 1 + 8 + 56 + 8 = 73px and body.rail-on was paying back 72. One
   pixel of the last line sat under the rail's own top hairline for as long as
   the rail has existed. Controls were already 56px and the safe-area inset was
   already in the padding; this is the only number that was wrong. */
@media (max-width:879.98px){
  body.rail-on{padding-bottom:calc(73px + env(safe-area-inset-bottom,0px))}
}

/* ── § 4 · THE DEMO PAIR ─────────────────────────────────────────────────────
   Two controls with different labels and no width rule render at two different
   widths, and the pair reads as a primary control with a stray link beside it
   rather than as a choice. They are the same choice: hear the call, or make
   one. Same width, same height, stacked on the site's own CTA measure — this is
   .cta-stack's geometry (column, 1rem gap, 520px cap), because the hero already
   established it and a second idiom for the same job is one idiom too many.
   56px at every width here, which is the phone value of button v2 rather than
   its 54px desktop value; a two-control choice is worth the two pixels. */
.demo-play-bar{display:flex;flex-direction:column;flex-wrap:nowrap;gap:1rem;
  max-width:520px;margin:0 0 1.4rem}
/* padding-block:0 with min-height and align-items:center pins both to
   exactly 56. Left on .95rem the ghost measured 58 against the filled one's 56,
   because its content box is taller by the arrow's line box — two pixels of
   difference between two controls sitting one above the other, which is
   visible precisely because they are stacked and the same width. */
.demo-play-bar>.demo-play-btn,.demo-play-bar>.demo-call-btn{
  width:100%;justify-content:center;min-height:56px;
  padding-top:0;padding-bottom:0}

/* ── § 5 · THE CALLBACK DRAWER ───────────────────────────────────────────────
   The header row was a 44px button that looked like a title. It is the control
   that opens the one module on the fold that does something, so it gets a 64px
   row and a chevron that says which way it goes.

   THE CHEVRON FOLLOWS THE RENDERED STATE, NOT THE ATTRIBUTE. aria-expanded is
   written by JS from what CSS actually painted (RUN 10), so a rotation keyed to
   it would point the wrong way for one frame on every load and would point the
   wrong way forever with scripting off. These selectors are the same ones that
   drive the collapse, so the glyph and the body cannot disagree.

   It is an inline SVG rather than a text glyph. The two faces on this site are
   self-hosted subsets and a metric-matched fallback silently steals characters
   the real face does not carry; an arrow drawn in markup inherits currentColor
   and cannot go missing. */
.cb-head{min-height:64px;gap:.6rem}
.cb-title{margin-right:auto}
.cb-chev{display:inline-flex;align-items:center;flex:none;color:var(--neutral);
  transform:rotate(90deg);
  transition:transform 160ms cubic-bezier(.2,.8,.2,1)}
.cb-chev svg{width:12px;height:12px;display:block}
@media (max-width:767.98px){
  .cb-form:not([data-collapsed="false"]) .cb-chev{transform:rotate(0deg)}
}
.cb-form[data-collapsed="true"] .cb-chev{transform:rotate(0deg)}
@media (prefers-reduced-motion:reduce){.cb-chev{transition:none}}
/* STATE LAW, third state. STANDING BY --neutral -> CALLING NOW --success-green
   -> NOT SENT --miss-red, and the colour and the word change on the same frame
   because the dot and the label are one element with transition:none on it.
   Miss-red is failure and only failure; it is never "attention". Measured on
   --surface: 5.26:1, over the AA body floor. The one-line explanation of what
   went wrong is the note under the form, in sentences, not in this chip. */
.cb-form.is-err .cb-status{color:var(--miss-red)}

/* ── § 6 · CONTAINER TIGHTEN ─────────────────────────────────────────────────
   An open FAQ row was outlined in blue on all four sides, which made the row
   the loudest object in the section and said nothing about which row it was.
   The border goes back to --line and the open state is a 3px rail down the left
   edge instead: it marks position rather than perimeter. It is an INSET shadow,
   so it paints nothing outside the box and stacks no elevation ladder — a
   hairline drawn with the shadow property, which is the distinction RUN 10's
   budget probe already splits.

   Radius: every container over 12px comes to 10px, which is the radius the
   button system, the rail and the trip-ticket cells already use. Pills are not
   containers and keep their full round — .badge, .int-pill and .hc-state are
   status chips, and a chip at 10px reads as a small box rather than as a state.
   Nothing already at or under 12px is touched. */
details[open]{border-color:var(--line);box-shadow:inset 3px 0 0 var(--signal-blue)}
/* MEASURE, AND ch IS NOT A CHARACTER. The answers had no cap at all, so on a
   1440 desktop they ran the full 1180px wrap — about 110 characters a line
   against the 60-75 band § 9 and CLAUDE.md APPENDIX A both set.

   The first cut used 64ch on the reasoning that it lands on the site's 560px
   prose measure. Measured, it rendered 623.6px and 79-84 characters a line —
   still over the band. 1ch is the advance of "0", and Space Grotesk's zero is
   markedly wider than its average glyph, so a ch value overshoots real text by
   about a third here. 52ch measures ~506px and 64-68 characters, which is the
   same CHARACTER measure the 17px body gets from its 560px box. The measure
   was never 560 pixels; 560 was the number of pixels that produced 62
   characters at 17px, and at 15.2px the same count needs a narrower box.

   52ch then read 58 characters on one page against a 60 floor — a knife-edge
   miss, because the count is content-dependent and one page's first answer runs
   longer words than another's. 55ch measures ~536px and 61-70 characters across
   all nine FAQ pages, which is margin on both ends of the band rather than a
   number tuned until one page scraped in.

   The brief asked for ~34ch. That is roughly half the ratified measure and
   would have broken the same two rules from the other side, so the cap is the
   character count this site already uses and the pixel result is stated here. */
details > div{max-width:55ch}
.card--stage,#faq .card,.ticket-card,.calc,
.console,.step,.feat,.crush,.setup-card{border-radius:10px}

/* ══ RUN 12 · ALPHA ══════════════════════════════════════════════════════════
   Byte-identical in assets/aic.css and the embedded <style> in index.html, and
   the LAST block in both. It carries four selectors the homepage does not
   render — .answer, .page-updated, .tbl td.st and .answer p a — and that is
   deliberate, for the reason § 1 of DESIGN-SYSTEM.md gives: splitting a shared
   block to save the bytes of the selectors one host does not use gives up the
   one property that makes the pair checkable at all.

   ── THE DIRECT ANSWER ──────────────────────────────────────────────────────
   The opening 40-60 words are the whole job of an answer-engine page, so they
   get a container that says "this is the answer" without inventing a new idiom:
   the 3px rail RUN 11 gave an open FAQ row, on the same surface-plus-hairline
   every other component uses to separate from the canvas. It costs a section
   nothing against the two-accent cap, because the gate normalises --sky and
   --action-blue onto --signal-blue and counts the three as ONE accent.

   Radius 10px is the § 9 container cap, not a choice. No shadow: a resting
   panel on this brand separates with a hairline and a step of surface. */
.answer{background:var(--surface);border:1px solid var(--line);
  border-left:3px solid var(--signal-blue);border-radius:10px;
  padding:1.15rem 1.25rem;margin:0 0 1.75rem;max-width:600px}
.answer p{font-size:1.0625rem;color:var(--ink);line-height:1.58;margin:0;
  text-wrap:pretty}
.answer p + p{margin-top:.7rem}
/* An inline link inside prose needs a signal that is not colour — the same rule
   the INLINE PROSE LINKS block above sets, extended to this container. It lives
   here rather than in that block so RUN 12 owns one contiguous diff. */
.answer p a{text-decoration:underline;text-underline-offset:.18em;
  text-decoration-thickness:1px}

/* ── THE VISIBLE LAST-UPDATED STAMP ─────────────────────────────────────────
   Mono, because § 3 gives times to the machine-readout face. .75rem because
   that is the 12px floor and this is operational text, not body copy. --neutral
   because the stamp is metadata about the page and names no state. */
.page-updated{font-family:var(--mono);font-size:.75rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--neutral);margin:0 0 1.75rem}

/* ── THE STATUS COLUMN, DELIBERATELY UNPAINTED ──────────────────────────────
   Mono per § 3: status belongs to the readout face. What it does NOT get is a
   colour, and that is the load-bearing decision in this block.

   Every value in this column names a STAGE OF SETUP — reviewed, scoped,
   available after verification — not a state that is true on screen right now.
   STATE LAW gives --success-green only to an action that has actually
   succeeded, beside a label naming it. A green row here would read as "this
   connection is live", which is a claim no row on this site is entitled to make
   until a real acceptance test passes. A three-tier colour ladder would make
   that claim eleven times in one glance, and it would look like evidence.

   So the column is --ink-soft throughout: 9.78:1 on --surface, well over the AA
   body floor, and it says exactly what the words say and nothing more. */
.tbl td.st{font-family:var(--mono);font-size:.75rem;letter-spacing:.04em;
  color:var(--ink-soft);line-height:1.6;max-width:32ch}

/* ── A TWO-COLUMN TABLE HAS NO BUSINESS SCROLLING ON A PHONE ────────────────
   `.tbl` carries `min-width:620px` inside a `.tbl-wrap{overflow-x:auto}`. That
   is a reasonable floor for the three- and four-column tables it was written
   for, and it is plainly wrong for two columns: photographed at 390 before this
   rule, the hub's status column — the entire point of the page — was off-screen
   to the right, and the only thing a reader saw was a list of platform names.

   342px of content at 390 is ample for a platform name beside a 12px mono
   status once both are allowed to wrap. `min-width:0` and slightly tighter
   cell padding land the whole table inside the viewport with no scroll at all,
   and the table keeps its real display:table semantics — which is why this is
   not the display:block "stacked card" trick. That trick drops the table role
   out of the accessibility tree in every major engine, and a status table whose
   rows no longer associate with their column heading is a worse outcome than
   the scroll it replaces.

   `min-width:0` alone was not enough and the measurement said why. `.tbl th`
   carries `white-space:nowrap` in BOTH the head and the row-header, which is
   right on a desktop fact table and is what kept the hub 425px wide inside a
   340px box at 390: the platform column was holding 242px so that "Santa Cruz
   (GroundWidgets)" could stay on one line. Measured columns were 242 + 182.
   Letting the header wrap on a phone — and only on a phone — is what actually
   closes it. A two-line platform name is not a defect; an invisible status
   column is. */
.tbl--pair{min-width:0}
@media(max-width:639.98px){
  .tbl--pair th,.tbl--pair td{padding:.85rem .7rem}
  .tbl--pair thead th,.tbl--pair tbody th{white-space:normal}
  .tbl--pair td.st{max-width:none}
}

/* ── AND WHERE A TABLE GENUINELY MUST SCROLL, SAY SO AND LET A KEYBOARD DO IT ─
   The three-column tables cannot honestly fit three columns of prose into
   342px, so they still scroll. Two things were missing and both are defects
   rather than polish:

   1. `overflow-x:auto` creates a SCROLLABLE REGION, and a scrollable region
      that is not focusable cannot be scrolled without a pointer. Adding
      tabindex="0" and a region role is the fix WCAG asks for, and it is why
      the focus style below exists — a new tab stop with no visible focus would
      trade one failure for another.
   2. Nothing told a reader there was anything to the right. The clipped column
      read as broken rather than as scrollable. The hint appears only at the
      widths where the table actually overflows its wrap: content width is
      viewport minus 48px of .wrap padding, and the table floor is 620, so the
      overflow begins below 668. */
.tbl-hint{display:none}
@media(max-width:667.98px){
  .tbl-hint{display:block;font-family:var(--mono);font-size:.75rem;
    letter-spacing:.08em;color:var(--neutral);margin:.7rem 0 1.25rem}
}
.tbl-wrap[tabindex]:focus-visible{outline:2px solid var(--signal-blue);
  outline-offset:2px}

/* ── A LINK INSIDE AN FAQ ANSWER, ON BOTH HEADS ─────────────────────────────
   THE THREE CSS HOMES TRAP, FOUND AGAIN AND IN THE SAME RULE FAMILY RUN 9
   FIXED. `details > div a` was declared in assets/aic.css and nowhere else, so
   on the eleven pages that load it an FAQ link is blue AND underlined, and on
   the homepage — which carries its own embedded copy of the shell — the base
   `a{text-decoration:none}` won and colour was the only thing marking the link.
   The homepage FAQ has carried one such link since the section shipped.

   RUN 9 fixed exactly this for .sec-sub, .card-p, .vs-b and the rest, and
   listed the selectors it covered; `details` was not among them and nothing
   since has looked. Lighthouse still reads 100 there, which is why no probe
   caught it — the audit is not the rule.

   Redeclaring it HERE rather than editing line 803 is deliberate: this block is
   the one that is byte-identical in both homes by law, so a rule placed in it
   cannot land on one head and miss the other. It is later in aic.css than the
   original and identical to it, so that file's rendered result does not move. */
details > div a{color:var(--signal-blue);text-decoration:underline;
  text-underline-offset:.18em;text-decoration-thickness:1px}

/* ── RUN 13 · THE TWO VALUES THE HOMEPAGE KEEPS ──────────────────────────────
   Merging two heads meant meeting the places where they genuinely disagreed
   rather than merely differed in wording. Two of them would have moved the
   homepage if aic.css's value had simply won:

       .sec-sub    margin-bottom   2.5rem  (home)   1.25rem  (the other 15)
       .cta-stack  margin-bottom   3rem    (home)   1rem     (the other 15)

   That is the homepage's original standalone rhythm, from before there were
   other pages to be consistent with. RUN 13 is housekeeping: it removes the
   drift SOURCE, and it is not licensed to restyle the page on the way past. So
   the rendered result is preserved and the disagreement is written down here
   instead of living in a second file where nobody can see both values at once.

   A scope is not a fork. A fork is two copies that can drift apart; this is one
   copy, in the shared home, next to the value it differs from.

   Whether the homepage should keep a wider rhythm than the content pages is a
   real design question with a real answer, and it belongs to a design run.
   ─────────────────────────────────────────────────────────────────────────── */
body.home .sec-sub{margin-bottom:2.5rem}
body.home .cta-stack{margin:0 0 3rem}

/* ── AND ONE RULE THAT WAS ALREADY DEAD, KEPT DEAD ──────────────────────────
   index.html carried `@media(min-width:1020px){.cta-stack{margin-bottom:0}}`,
   and it had not applied to anything for a long time: a LATER `.cta-stack`
   rule in the same file set the `margin` SHORTHAND, which resets
   margin-bottom, and a media query adds no specificity to break a tie that
   source order has already decided.

   Migrating it faithfully is what resurrected it — the move put it after the
   shorthand instead of before, and the homepage hero lost 48px at desktop,
   which walked the whole page up 20px. The parity harness caught it; nothing
   else would have, because both the rule and the shorthand are correct in
   isolation and the file reads exactly as it did before.

   MOVING CSS CHANGES WHICH RULES ARE DEAD. A rule that lost a source-order tie
   in its old home can win it in the new one, and it arrives looking like a
   faithful copy. It is not restored here, because it was not doing anything
   when the run started and this run does not change how the page looks.
   ─────────────────────────────────────────────────────────────────────────── */
