/* =====================================================================
   ti-v16.css — REFINEMENT LAYER.  Loads last on every page.

   Purpose: not new decoration. This consolidates the four things the
   audit found wrong across v8–v15 and makes them systematic:

     1. TYPE     Bricolage Grotesque restored as the display face, with
                 optical tracking that tightens as size grows. The old
                 system-sans stack was why the site read as generic.
     2. SURFACE  10+ arbitrary radii and 88 unique shadows collapsed to
                 a 4-step radius scale and a 4-step elevation scale,
                 assigned by role rather than by whim.
     3. MOTION   Hover/transition timing pulled to 180-220ms on one
                 shared easing curve. Scroll choreography is unchanged —
                 it was already good, it was just dead behind a 404.
     4. A11Y     Visible focus for keyboard users. The site had 4 focus
                 rules in total, which is a real defect, not a polish item.

   Design system reference: Minimalism & Swiss Style (grid discipline,
   type hierarchy, restrained motion) applied to the existing dark
   navy + turquoise palette from ti-dark-v13, which was already sound.
   ===================================================================== */

:root{
  /* ---- motion: one curve, three speeds -------------------------------
     A single decelerating curve reads as "one hand made this". Sub-200ms
     for anything under the cursor, so the interface feels answered
     rather than animated. */
  --ease:cubic-bezier(.2,.7,.3,1);
  --ease-out-soft:cubic-bezier(.33,1,.68,1);
  --t-fast:160ms;   /* colour / opacity under the cursor */
  --t-base:220ms;   /* transforms, borders, shadows      */
  --t-slow:420ms;   /* entrances and layout shifts       */

  /* ---- radius: 4 steps, assigned by role ---------------------------- */
  --r-xs:4px;    /* chips, tags, tight inline marks   */
  --r-sm:10px;   /* buttons, inputs, small surfaces   */
  --r-md:16px;   /* cards, panels                     */
  --r-lg:24px;   /* hero-scale surfaces, feature slabs*/
  --r-pill:999px;

  /* ---- elevation: 4 steps ------------------------------------------
     On a dark ground, shadow alone is nearly invisible, so each level
     pairs a shadow with a hairline of light along the top edge. That
     top hairline is what actually reads as "raised" in dark UI. */
  --e-0:none;
  --e-1:0 1px 0 rgba(255,255,255,.04), 0 2px 8px rgba(0,0,0,.28);
  --e-2:0 1px 0 rgba(255,255,255,.05), 0 10px 28px rgba(0,0,0,.36);
  --e-3:0 1px 0 rgba(255,255,255,.06), 0 24px 60px rgba(0,0,0,.46);
  --e-glow:0 10px 30px rgba(53,201,164,.26);

  /* ---- focus ring ---------------------------------------------------- */
  --focus:0 0 0 2px var(--bg,#0a1420), 0 0 0 4px var(--green,#35c9a4);
}

/* =====================================================================
   1 — TYPOGRAPHY
   Bricolage is a wider, higher-contrast grotesque than SF Pro. The old
   flat -.032em tracking was tuned for SF and leaves Bricolage cramped at
   display sizes and gappy at small ones. Tracking is therefore set per
   step: tighter as the type grows, opening back up for body and labels.
   ===================================================================== */

h1,h2,h3,.brand .word,footer .fb{
  font-family:var(--display) !important;
  font-variation-settings:normal;
  text-wrap:balance;               /* no orphaned last word in a headline */
}

/* hero — the one place the type is allowed to be loud.
   Sized to the column, not to the viewport: the dashboard artwork begins
   at roughly 50vw, so the headline is capped to stay clear of it rather
   than being set as large as the screen would allow and colliding. */
.hero h1{
  font-size:clamp(2.4rem,4.15vw,3.55rem) !important;
  line-height:1.045 !important;
  letter-spacing:-.03em !important;
  font-weight:700 !important;
  /* The headline is three commands and a payoff. Balancing the lines
     would even out their lengths and break that structure mid-sentence,
     so this one opts out and is sized to let each clause hold its line. */
  text-wrap:normal !important;
}
/* Only cap the hero column where the dashboard artwork actually sits
   beside it. Below that the hero stacks, and a percentage cap here would
   crush the headline into a column too narrow to set words in. */
/* The text column sits inside a centred .wrap, so as the viewport grows its
   LEFT edge moves right too (x=130 at 1440, x=370 at 1920) while the artwork
   only starts around 52vw. A percentage cap alone therefore still collided at
   1920. 560px is the width that clears the artwork at every size from 1000px
   up, measured rather than guessed. */
@media (min-width:1000px){
  .hero .hero-inner{max-width:min(41%,560px) !important}
}

/* The panel CTA belongs to the mobile disclosure menu only. Without this it
   also renders in the desktop tab row, where "Book a call" already sits as a
   button — the same action twice, three inches apart. */
.tabs .tab-cta{display:none}
@media (max-width:900px){ .tabs .tab-cta{display:flex} }
/* never hyphenate or break inside a word in display type */
.hero h1,h1,h2,h3{overflow-wrap:normal !important;word-break:normal !important;hyphens:none !important}

/* section heads */
h2{
  font-size:clamp(1.85rem,3.3vw,2.85rem) !important;
  line-height:1.07 !important;
  letter-spacing:-.026em !important;
  font-weight:700 !important;
}

/* card and sub heads — tracking opens up as size drops */
h3{
  font-size:clamp(1.05rem,1.5vw,1.28rem) !important;
  line-height:1.28 !important;
  letter-spacing:-.011em !important;
  font-weight:600 !important;
}

/* the accented clause inside a headline. Weight is what separates it,
   not just colour, so it still reads in monochrome or for a user who
   cannot separate the green from the white. */
h1 em,h2 em,h3 em{
  font-weight:700 !important;
  letter-spacing:inherit !important;
}

/* body: Inter against a Bricolage display is the real contrast pair —
   two different skeletons, not two weights of one font. */
body{
  font-size:1.0625rem;
  line-height:1.66;
  letter-spacing:.001em;
}
.hero .lede,.section .lede,.h2row p{
  font-size:1.06rem !important;
  line-height:1.62;
  max-width:52ch;                  /* measure, not container width */
}

/* mono labels: the third voice. Wide tracking is the whole point of a
   label — it should read as instrument marking, not as small body text. */
.eyebrow,.split-lbl,.mono-note{
  font-feature-settings:"tnum" 1;
  letter-spacing:.24em;
}

/* tabular figures wherever numbers carry meaning, so columns of money
   line up and a changing counter does not jitter its own width. */
.kpi b,.plan .price,.anchor .big,.counter-band h2,.found-facts b,
.dash-charts *,.stat b,.stat-num{
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1;
}

/* =====================================================================
   2 — SURFACE
   Radius by role. A card and a chip should not share a radius; that
   sameness across every surface is a large part of what reads as
   template output.
   ===================================================================== */

.card,.door,.bcard,.rcard,.plan,.lstage-card,.tb-founder,.tb-security,
.contrast .panel,.station{
  border-radius:var(--r-md);
  box-shadow:var(--e-1);
  transition:transform var(--t-base) var(--ease),
             box-shadow var(--t-base) var(--ease),
             border-color var(--t-base) var(--ease);
}

.btn,.cta,input,select,textarea{border-radius:var(--r-sm)}
.tag,.chip,.pill,.builton .lbl{border-radius:var(--r-pill)}
.hero .shot,.dash-frame,.engine-shot{border-radius:var(--r-lg)}

/* lift on hover — small. 4px is felt; 12px is a trampoline. */
.card:hover,.door:hover,.bcard:hover,.rcard:hover,.plan:hover{
  transform:translateY(-4px);
  box-shadow:var(--e-2);
  border-color:var(--line-green,rgba(53,201,164,.38));
}

/* =====================================================================
   3 — MOTION
   Everything under the cursor answers in under 200ms on one curve.
   ===================================================================== */

a,button,.btn,.cta,.tabs a,.door,.card,summary,.faq summary{
  transition:color var(--t-fast) var(--ease),
             background-color var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease),
             opacity var(--t-fast) var(--ease),
             transform var(--t-base) var(--ease),
             box-shadow var(--t-base) var(--ease);
}

.cta,.btn.solid{box-shadow:var(--e-glow)}
.cta:hover,.btn.solid:hover{transform:translateY(-1px)}
.cta:active,.btn.solid:active{transform:translateY(0)}

/* nav underline grows from the left rather than fading in — direction
   carries the meaning that a fade does not. */
.tabs a{position:relative;border-bottom-color:transparent !important}
.tabs a::after{
  content:"";position:absolute;left:0;bottom:-1px;height:2px;width:100%;
  background:var(--green,#35c9a4);
  transform:scaleX(0);transform-origin:left;
  transition:transform var(--t-base) var(--ease);
}
.tabs a:hover::after,.tabs a.active::after{transform:scaleX(1)}

/* =====================================================================
   4 — ACCESSIBILITY
   The site had four focus rules. Keyboard users could not see where
   they were. This is the fix, applied once, globally.
   ===================================================================== */

:where(a,button,summary,input,select,textarea,[tabindex]):focus-visible{
  outline:none;
  box-shadow:var(--focus);
  border-radius:var(--r-xs);
  /* a focus ring is feedback, not decoration: it appears on the frame the
     key is pressed. Inheriting the 220ms shadow transition made it fade
     in, which reads as lag exactly where a keyboard user needs certainty. */
  transition:none !important;
}
/* never remove the outline without replacing it */
:focus:not(:focus-visible){outline:none}

/* skip link — present but out of the way until tabbed to */
.skip-link{
  position:absolute;left:-9999px;top:0;z-index:200;
  padding:.7rem 1.1rem;border-radius:0 0 var(--r-sm) 0;
  background:var(--green,#35c9a4);color:#06121b;font-weight:600;
}
.skip-link:focus{left:0}

/* =====================================================================
   4b — CONTRAST REGRESSION FROM THE DARK REPAINT
   The cream-on-green pairings date from v8, when --green was the deep
   #1d5f4a and cream on it was correct. ti-dark-v13 repointed --green to
   the bright turquoise #35c9a4 but did not revisit the foregrounds it
   was paired with, leaving the primary "Book a call" button at 1.84:1 —
   below the 4.5:1 floor, on the single most important control on the
   site. Anything filled with the brand green now takes the dark ink,
   which measures 7.6:1.
   ===================================================================== */

.cta,.btn.solid,.btn[class*="solid"],.plan.feat .go,.fstep i,
.engine-card .ec-go,.pillar .p-fee,.station .fee,.skip-link,
.site-head .btn,a.btn[href*="call"],.cta-band .btn{
  color:#06121b !important;
}
/* keep the ink dark through hover/focus, where the fill only brightens */
.cta:hover,.btn.solid:hover,.plan.feat .go:hover,
.site-head .btn:hover,.cta:focus-visible,.btn.solid:focus-visible{
  color:#06121b !important;
}

/* spreadsheet mock: the column headers were #78808c on #f4f5f7 = 3.66:1
   at 10px, which is below the floor for text this small. Darkened to
   5.5:1. The workbook stays light on purpose — that is the design brief. */
table.sheet th{color:#5b6472 !important}

/* ---------------------------------------------------------------------
   The white "top-light" card wash.
   ti-v9-polish paints rgba(255,255,255,.55) over the top 38% of every
   card. That was written for the cream v8 theme. Under the dark theme
   those same cards carry light text, so the wash bleached the top third
   of each one and took the pricing tiers with it — "The Audit", "£300",
   "£750" and "£250" were all rendering near-white on near-white. Only
   .plan.feat had ever been corrected, which is why the featured tier was
   the one readable card on the page.

   A top sheen is still the right idea on a dark surface; it just has to
   be a sheen (5.5%) rather than a wash (55%).
   --------------------------------------------------------------------- */
.pillar,.sys,.t-item,.plan,.lstage-card,.pcard,.cnote,.t-card,
.col-card,.fstep,.v{
  background-image:linear-gradient(180deg,
                   rgba(255,255,255,.055), rgba(255,255,255,0) 42%) !important;
}

/* =====================================================================
   5 — LEGIBILITY FIX (the pinned "gap" scene)
   The diverging curves were drawn straight through the subhead, so the
   sentence became unreadable exactly where the two lines cross it. A
   soft radial scrim sits between the artwork and the copy: the curves
   still read as continuous, the words sit clearly on top.
   ===================================================================== */

.scene.gap .stage{position:relative;z-index:2}
.scene.gap .stage::before{
  content:"";position:absolute;z-index:-1;
  inset:-2.5rem -4rem;
  background:radial-gradient(58% 62% at 50% 44%,
             rgba(10,20,32,.92) 0%,
             rgba(10,20,32,.78) 42%,
             rgba(10,20,32,0) 78%);
  pointer-events:none;
}
.scene.gap .gap-sub{
  color:var(--ink,#eaf2f7);
  max-width:46ch;margin-inline:auto;
}

/* =====================================================================
   6 — RHYTHM
   Even spacing on every section is one of the tells. The section scale
   below is deliberate, and .section-tight / .section-loose exist so the
   page can breathe unevenly where the argument changes pace.
   ===================================================================== */

.section{padding-block:clamp(4rem,8vh,7rem)}
.section-tight{padding-block:clamp(2.5rem,4vh,3.5rem)}
.section-loose{padding-block:clamp(6rem,12vh,10rem)}

/* the £0 counter is the emotional beat of the page — give it room and
   let it sit off the regular grid. */
.counter-band{padding-block:clamp(5rem,10vh,8.5rem)}
.counter-band h2{letter-spacing:-.04em !important}

/* =====================================================================
   7 — REDUCED MOTION
   Honour the preference completely: no transitions, no transforms, and
   crucially every scroll-revealed element forced visible, so a user who
   asks for stillness still gets the whole page.
   ===================================================================== */

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    scroll-behavior:auto !important;
  }
  .reveal,.pillar,.l3,.tb-founder,.tb-security,.gap-sub,.gap-facts span,
  .scene h2,.scene p{
    opacity:1 !important;transform:none !important;
  }
  .grain,body::after{animation:none !important}
}

/* =====================================================================
   8 — NO-JS FALLBACK
   If GSAP fails to load, elements that the timeline would have revealed
   must not stay invisible. This is what the 404 should have failed into.
   ===================================================================== */

.no-js .reveal,.no-js .pillar,.no-js .l3,
.no-js .gap-sub,.no-js .gap-facts span{opacity:1 !important;transform:none !important}

/* =====================================================================
   9 — RESPONSIVE
   ===================================================================== */

@media (max-width:900px){
  .hero h1{font-size:clamp(2.1rem,8.2vw,2.9rem) !important;letter-spacing:-.026em !important}
  h2{letter-spacing:-.02em !important}
  .section{padding-block:clamp(3rem,6vh,4.5rem)}
  .scene.gap .stage::before{inset:-1.5rem -1.5rem}
}
@media (max-width:520px){
  body{font-size:1rem}
  .hero .lede,.section .lede{font-size:1rem !important}
  /* wide label tracking becomes unreadable at phone sizes */
  .eyebrow,.split-lbl{letter-spacing:.16em;font-size:.62rem}
}

/* Touch targets. The nav tabs rendered 33px tall on a phone, under the
   44px floor. The tap area is grown with padding and a negative margin
   so the row's visual rhythm is unchanged — the target grows, the
   design does not move. */
@media (pointer:coarse),(max-width:900px){
  .tabs a{
    padding-block:.62rem;
    margin-block:-.28rem;
    min-height:44px;
    display:inline-flex;align-items:center;
  }
  .brand{min-height:44px;display:flex;align-items:center}
  .site-head .btn,.cta{min-height:44px;display:inline-flex;align-items:center}
  .faq summary{min-height:48px}

  /* The tab row scrolls horizontally on a phone — Data and Pricing sit
     past the right edge — but nothing said so. A soft mask on the
     trailing edge makes the overflow visible instead of implied, and
     momentum scrolling makes it feel native. */
  .tabs{
    -webkit-overflow-scrolling:touch;
    scroll-snap-type:x proximity;
    mask-image:linear-gradient(90deg,#000 0,#000 88%,transparent 100%);
    -webkit-mask-image:linear-gradient(90deg,#000 0,#000 88%,transparent 100%);
    scrollbar-width:none;
  }
  .tabs::-webkit-scrollbar{display:none}
  .tabs a{scroll-snap-align:start}
  /* once scrolled to the end there is nothing more to hint at */
  .tabs.at-end{mask-image:none;-webkit-mask-image:none}
}

/* =====================================================================
   10 — HERO PROOF
   £277,000 was the strongest fact on the site and it sat at 75% page
   depth behind a scroll-driven counter, so a visitor who did not scroll
   saw the figure "£0". It now states itself above the fold.
   ===================================================================== */

.hero .hero-proof{
  display:flex;align-items:baseline;gap:.85rem;flex-wrap:wrap;
  margin:1.5rem 0 .2rem;padding-top:1.25rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.08));
  position:relative;
}
/* the gold tick is the structural marker for a verified figure — it is the
   same mark used on section eyebrows, so gold consistently means "this is
   a fact, not a claim" rather than being decorative trim */
.hero .hero-proof::before{
  content:"";position:absolute;top:-1px;left:0;width:2.4rem;height:2px;
  background:var(--gold,#c9a25c);
}
.hero .hero-proof b{
  font-family:var(--display);font-weight:700;
  font-size:clamp(1.7rem,2.5vw,2.15rem);letter-spacing:-.03em;
  color:var(--green,#35c9a4);line-height:1;
  font-variant-numeric:tabular-nums;
}
.hero .hero-proof span{
  font-size:.86rem;line-height:1.45;color:var(--muted,#6d8296);max-width:34ch;
}

/* =====================================================================
   11 — LEAD MAGNET + FORM
   Deliberately asymmetric (7:5). Every other band on this page is a
   centred or evenly-split grid, so this one breaks the rhythm at the
   point the page stops explaining and starts asking.
   ===================================================================== */

.magnet{background:var(--bg2,#0d1a29);border-block:1px solid var(--line-soft)}
.magnet-grid{
  display:grid;grid-template-columns:7fr 5fr;gap:clamp(2rem,5vw,4.5rem);
  align-items:start;
}
/* Its own label class rather than .eyebrow — that one is a live-status badge
   with a pulsing dot, which would be claiming something is happening here. */
.mg-label{
  display:inline-block;
  font-family:var(--mono);font-size:.64rem;letter-spacing:.22em;
  text-transform:uppercase;color:var(--gold,#c9a25c);
}
.magnet-copy h2{margin-top:.6rem}
.magnet-copy .lede{margin-top:1rem;max-width:48ch}

/* the twelve checks — numerals in gold mono, so the list reads as an
   instrument panel rather than a bulleted feature list */
.checks{
  list-style:none;counter-reset:chk;margin-top:1.9rem;
  display:grid;grid-template-columns:1fr 1fr;gap:.55rem 1.6rem;
}
.checks li{
  counter-increment:chk;position:relative;padding-left:2.1rem;
  font-size:.895rem;line-height:1.5;color:var(--ink-dim,#9fb3c4);
}
.checks li::before{
  content:counter(chk,decimal-leading-zero);
  position:absolute;left:0;top:.16em;
  font-family:var(--mono);font-size:.62rem;letter-spacing:.08em;
  color:var(--gold,#c9a25c);
}

/* form card */
.magnet-form{
  background:var(--card,#12212f);
  border:1px solid var(--line,rgba(160,200,220,.14));
  border-radius:var(--r-md);
  padding:clamp(1.5rem,2.4vw,2.1rem);
  box-shadow:var(--e-2);
  position:sticky;top:6rem;
}
.magnet-form h3{margin-bottom:.5rem}
/* --muted (#6d8296) measures 4.11:1 on the card — under the 4.5 floor for
   body text. These two use --ink-dim, which clears it. */
.mf-note{font-size:.85rem;line-height:1.5;color:var(--ink-dim);margin-bottom:1.4rem}
.magnet-form .field{margin-bottom:1.05rem}
.magnet-form label{
  display:block;font-family:var(--mono);font-size:.63rem;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink-dim);margin-bottom:.45rem;
}
.magnet-form label i{font-style:normal;color:var(--muted);letter-spacing:.1em}
.magnet-form input{
  width:100%;padding:.78rem .9rem;
  background:var(--bg,#0a1420);
  border:1px solid var(--line);
  border-radius:var(--r-sm);
  color:var(--ink,#eaf2f7);font-family:var(--sans);font-size:.95rem;
  transition:border-color var(--t-fast) var(--ease),
             box-shadow var(--t-fast) var(--ease);
}
.magnet-form input::placeholder{color:#4d6076}
.magnet-form input:hover{border-color:rgba(160,200,220,.26)}
/* A border colour change alone is not enough of a focus indicator — it fails
   for anyone who cannot separate the two colours. Keyboard focus gets the same
   ring every other control uses; pointer focus keeps the quieter border. */
.magnet-form input:focus{border-color:var(--green)}
.magnet-form input:focus-visible{
  outline:none;border-color:var(--green);box-shadow:var(--focus);
}
.magnet-form input.is-invalid{border-color:var(--bad,#a8543f)}
.magnet-form .err{
  display:block;min-height:1.05rem;margin-top:.35rem;
  font-size:.78rem;line-height:1.35;color:#e08a72;
}
.mf-submit{width:100%;justify-content:center;margin-top:.35rem;padding:.85rem 1rem;cursor:pointer}
/* a <button> does not get a pointer cursor by default; every other clickable
   thing on this page does, and the inconsistency is felt even if not noticed */
button,[role="button"],summary{cursor:pointer}
button:disabled{cursor:not-allowed}

/* Visually hidden but available to screen readers. Used to restore heading
   order on pages that jump h1 -> h3, without changing the visual design. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}
.mf-submit[data-busy]{opacity:.75;cursor:progress}
.mf-status{margin-top:.8rem;font-size:.84rem;line-height:1.45;color:var(--green-bright,#5fe0bf)}
.mf-privacy{margin-top:1rem;font-size:.78rem;line-height:1.55;color:var(--ink-dim)}
.mf-privacy a{color:var(--ink-dim);text-decoration:underline;text-underline-offset:2px}
.mf-privacy a:hover{color:var(--green)}

@media (max-width:900px){
  .magnet-grid{grid-template-columns:1fr;gap:2.2rem}
  .magnet-form{position:static}
  .checks{grid-template-columns:1fr}
}

/* =====================================================================
   12 — GOLD AS STRUCTURE, NOT TRIM
   A near-black ground with one bright accent is a recognised default
   look. The palette here is kept (it is the logo, and it carries across
   the client cards and outreach) but the gold is promoted from
   decoration to a consistent structural signal: it marks section
   openings and verified figures. Two accents with distinct jobs read as
   a decision; one accent reads as a template.
   ===================================================================== */

.section .head h2::before,.h2row h2::before,.year .head h2::before{
  background:var(--gold,#c9a25c) !important;   /* was a fading gradient */
  width:2.4rem !important;height:2px !important;border-radius:0 !important;
}
.eyebrow{color:var(--gold,#c9a25c)}
.counter-band h2{color:var(--green-bright,#5fe0bf) !important}

/* =====================================================================
   12b — PER-PRODUCT ACCENT ("the twang")
   Each product page keeps the same theme but carries one distinguishing
   accent, taken from the actual tool it represents rather than picked to
   look nice: n8n's coral on the automations page, a ledger gold on the
   spreadsheet audit, connection cyan on data. Dashboards keeps the house
   turquoise because it is the flagship.

   The accent is confined to STRUCTURAL marks — section rules, eyebrows,
   numerals. Buttons stay brand green everywhere, so "the green thing is
   the action" never stops being true. Every value below was measured on
   all three dark surfaces; the lowest is 5.99:1.

   Excel's own #1D6F42 is the obvious choice for the audit page and is
   not used: it measures 2.65:1 on the card, and the lifted version sits
   too close to the brand turquoise to read as a different accent.
   ===================================================================== */

:root{--accent:var(--gold,#c9a25c)}
body.door-xl  {--accent:#c9a25c}   /* ledger gold — the audit          */
body.door-auto{--accent:#F4738F}   /* n8n coral, lifted for contrast   */
body.door-dash{--accent:#35c9a4}   /* house turquoise — the flagship   */
body.door-data{--accent:#1FC0E7}   /* connection cyan                  */

/* the marks that carry it */
.section .head h2::before,.h2row h2::before,.year .head h2::before{
  background:var(--accent) !important;
}
body[class*="door-"] .eyebrow,
body[class*="door-"] .mg-label,
body[class*="door-"] .svc-meta span,
body[class*="door-"] .step i,
body[class*="door-"] .door .num,
body[class*="door-"] .rcard .r-eyebrow,
body[class*="door-"] .lstage-card .step{
  color:var(--accent);
}
body[class*="door-"] .checks li::before{color:var(--accent)}
/* the hairline above a card picks it up too, at low opacity */
body[class*="door-"] .pillar::before,
body[class*="door-"] .gap-facts span::before{
  background:linear-gradient(90deg,transparent,var(--accent),transparent);
  opacity:.55;
}

/* =====================================================================
   13 — GUIDELINE COMPLIANCE
   Items flagged by the Web Interface Guidelines audit.
   ===================================================================== */

/* never animate a layout property; never blanket-animate everything */
*{transition-property:inherit}
.door,.card,.plan,.pillar,.bcard,.rcard,.btn,.cta,.tabs a,.faq summary,
.magnet-form input,.chip,.gap-facts span{
  transition-property:color,background-color,border-color,opacity,transform,box-shadow;
}

/* tap behaviour */
a,button,summary,input,select,textarea,[role="button"]{
  touch-action:manipulation;
  -webkit-tap-highlight-color:rgba(53,201,164,.18);
}
/* anchored headings must not hide under the fixed header */
[id]{scroll-margin-top:5.5rem}
/* full-bleed bands on notched phones */
.site-head,footer,.contact,.magnet{
  padding-left:max(0px,env(safe-area-inset-left));
  padding-right:max(0px,env(safe-area-inset-right));
}
/* modals/overflow regions should not chain their scroll to the page */
.tabs,.sheet-wrap,.hd-body{overscroll-behavior:contain}

/* =====================================================================
   14 — MOBILE NAVIGATION
   The tab row scrolled horizontally, which left Data and Pricing off
   screen with only a fade to suggest it. Below 900px it becomes a
   disclosure panel instead: everything reachable, nothing hidden.
   ===================================================================== */

.nav-toggle{display:none}

@media (max-width:900px){
  .nav-toggle{
    display:inline-flex;align-items:center;justify-content:center;
    width:44px;height:44px;margin-left:auto;
    background:transparent;border:1px solid var(--line);border-radius:var(--r-sm);
    color:var(--ink);cursor:pointer;
  }
  .nav-toggle span{position:relative;display:block;width:17px;height:1.5px;background:currentColor}
  /* the bars move on transform only — animating `top` would relayout the
     button on every frame of a 220ms transition */
  .nav-toggle span::before,.nav-toggle span::after{
    content:"";position:absolute;left:0;top:0;width:17px;height:1.5px;background:currentColor;
    transition:transform var(--t-base) var(--ease);
  }
  .nav-toggle span::before{transform:translateY(-5.5px)}
  .nav-toggle span::after{transform:translateY(5.5px)}
  .nav-toggle[aria-expanded="true"] span{background:transparent}
  .nav-toggle[aria-expanded="true"] span::before{transform:translateY(0) rotate(45deg)}
  .nav-toggle[aria-expanded="true"] span::after{transform:translateY(0) rotate(-45deg)}

  .site-head .wrap{grid-template-columns:1fr auto;gap:.6rem}
  /* AUDIT 2026-08-19: index uses .btn, the other 8 pages use .cta, which
     ti-v16.css:382 forces to inline-flex at <=900px. Hiding only .btn left a
     stranded pill on 8 pages and a duplicate CTA once the drawer opened. */
  .site-head .btn{display:none}
  /* the bar behind the brand row is 82% opaque by default; with the panel
     open the whole header needs to be solid or the hero reads through it */
  .site-head:has(.tabs.open){background:var(--bg,#0a1420);box-shadow:var(--e-2)}

  .tabs{
    grid-column:1 / -1;
    display:grid;grid-template-columns:1fr;gap:0;
    overflow:hidden;max-height:0;
    mask-image:none;-webkit-mask-image:none;
    transition:max-height var(--t-slow) var(--ease);
    /* The panel must be opaque. The header's own background only paints
       behind the original single row, so without this the menu opens as
       transparent text sitting on top of the hero headline. */
    background:var(--bg,#0a1420);
  }
  .tabs.open{max-height:28rem;padding-bottom:1rem}
  .tabs a{
    padding:.85rem .2rem;min-height:48px;
    border-bottom:1px solid var(--line-soft) !important;
  }
  .tabs a::after{display:none}
  .tabs a.active{color:var(--green)}
  /* the booking CTA belongs in the panel on a phone, not hidden */
  .tabs .tab-cta{
    margin-top:.9rem;background:var(--green);color:#06121b !important;
    border-radius:var(--r-sm);justify-content:center;font-weight:600;
    border-bottom:0 !important;
  }
}

/* =====================================================================
   15 — REMOVE ONE ACCESSORY
   The page carried grain twice: a .grain element animating a 200%x200%
   fixed layer on a 900ms infinite loop, and a second static grain on
   body::after. Two grains do not read as more texture, they read as
   noise, and the animated one repaints the whole viewport forever for
   an effect nobody can consciously see. The static layer stays; the
   animated duplicate goes.
   ===================================================================== */

.grain{display:none !important}

/* =====================================================================
   16 — FORM CONTROLS INHERIT THE TYPE SYSTEM
   Browsers default form controls to their own font. Without this the
   one place a visitor types looks like it belongs to a different site.
   ===================================================================== */

input,select,textarea,button{font-family:inherit;font-size:inherit;color:inherit}

/* =====================================================================
   17 — TWO SWEEPS THAT ANIMATED LAYOUT
   Both of these moved by animating `left`/`right`, which relayouts on
   every frame. Same visual, driven by transform instead — the compositor
   handles it and the main thread stays free.
   ===================================================================== */

/* nav underline: the old rule slid `right` from 100% to 0 */
.tabs a::after{transition:transform var(--t-base) var(--ease) !important}

/* card shine sweep (used on the Data page): slid `left` from -80% to 120%.
   The element is 60% of the card wide, so the same travel is 333% of its
   own width. */
.pcard::after{
  left:-80%;
  transform:translateX(0) skewX(-18deg);
  transition:transform .55s ease !important;
}
.pcard:hover::after{left:-80%;transform:translateX(333%) skewX(-18deg)}

/* =====================================================================
   17b — LIVE DEMO, PROMOTED
   The one thing on the dashboards page that is a working product rather
   than a description of one. It sat three sections down; it now sits
   with the headline, and the illustration beside it is a way in too.
   ===================================================================== */

.demo-call{
  display:flex;align-items:center;gap:1rem;flex-wrap:wrap;
  margin:1.6rem 0 .4rem;padding-top:1.4rem;
  border-top:1px solid var(--line-soft);
}
.demo-call .demo-go{
  display:inline-flex;align-items:center;gap:.5rem;
  padding:.8rem 1.15rem;white-space:nowrap;
}
.demo-call .demo-go svg{fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round}
.demo-call .demo-note{font-size:.82rem;line-height:1.45;color:var(--ink-dim);max-width:28ch}

/* the illustration itself opens the demo */
.dash{position:relative}
.dash .dash-open{
  position:absolute;inset:0;z-index:5;
  display:flex;align-items:flex-end;justify-content:center;
  padding-bottom:1.4rem;
  background:linear-gradient(180deg,rgba(10,20,32,0) 55%,rgba(10,20,32,.72) 100%);
  opacity:0;transition:opacity var(--t-base) var(--ease);
  border-radius:inherit;
}
.dash .dash-open span{
  font-family:var(--mono);font-size:.68rem;letter-spacing:.16em;text-transform:uppercase;
  color:#06121b;background:var(--green,#35c9a4);
  padding:.5rem .9rem;border-radius:var(--r-pill);
  transform:translateY(6px);transition:transform var(--t-base) var(--ease);
}
.dash:hover .dash-open,.dash .dash-open:focus-visible{opacity:1}
.dash:hover .dash-open span,.dash .dash-open:focus-visible span{transform:translateY(0)}
/* on touch there is no hover, so it is simply always legible */
@media (pointer:coarse){
  .dash .dash-open{opacity:1}
  .dash .dash-open span{transform:none}
}

/* =====================================================================
   17c — THE CONNECTION LOOP (data page)
   The diagram already said the right thing — three systems joining into
   one answer — but it drew once and then sat still, so the point landed
   only for whoever happened to be looking at that moment. It now runs as
   a loop: each system wakes in turn, the lines join them, the answer
   lights, it holds long enough to read, then goes again.

   The loop only runs once the block is in view (.in is set by the
   existing reveal observer), so it costs nothing while off-screen.
   ===================================================================== */

@keyframes ti-node-wake{
  0%,8%   {opacity:.34;transform:translateY(2px)}
  16%,74% {opacity:1;  transform:translateY(0)}
  86%,100%{opacity:.34;transform:translateY(2px)}
}
@keyframes ti-join{
  0%,18%  {stroke-dashoffset:1;opacity:.25}
  46%,74% {stroke-dashoffset:0;opacity:1}
  88%,100%{stroke-dashoffset:1;opacity:.25}
}
@keyframes ti-answer{
  0%,46%  {opacity:.42;transform:translateY(4px);border-color:var(--line-soft)}
  58%,76% {opacity:1;  transform:translateY(0);  border-color:var(--accent,#1FC0E7)}
  90%,100%{opacity:.42;transform:translateY(4px);border-color:var(--line-soft)}
}

.flow-mini.in .fm-node{
  animation:ti-node-wake 7s var(--ease) infinite;
  will-change:opacity,transform;
}
.flow-mini.in .fm-node:nth-child(2){animation-delay:.42s}
.flow-mini.in .fm-node:nth-child(3){animation-delay:.84s}

.flow-mini.in .fm-lines path{
  stroke-dasharray:1;
  animation:ti-join 7s var(--ease) infinite !important;
}
.flow-mini.in .fm-lines path:nth-child(2){animation-delay:.42s !important}
.flow-mini.in .fm-lines path:nth-child(3){animation-delay:.84s !important}

.flow-mini .fm-out{
  border:1px solid var(--line-soft);border-radius:var(--r-sm);
  padding:.85rem 1rem;transition:border-color var(--t-base) var(--ease);
}
/* ti-answer removed 2026-08-18: it looped the whole panel, so the scene
   emptied and refilled. The panel is now static; only the trace repeats. */

/* stillness when asked for: everything sits in its finished, connected state */
@media (prefers-reduced-motion:reduce){
  .flow-mini.in .fm-node,
  .flow-mini.in .fm-lines path,
  .flow-mini.in .fm-out{animation:none !important;opacity:1 !important;transform:none !important}
  .flow-mini.in .fm-lines path{stroke-dashoffset:0 !important}
  /* panel stays grey under reduced motion too */
}

/* =====================================================================
   17d — WORKFLOW PLAYER
   The old diagrams were flat SVG exports. They scrolled sideways into
   dead space, and the Anthropic and Structured Output nodes appeared to
   float unconnected — because n8n attaches those through ai_* ports, not
   main, and the exporter only drew main.

   This is generated from the live n8n graph over the API, so the nodes,
   the ports and the order are the real ones. Sub-nodes hang beneath their
   chain node on dashed links, exactly as n8n draws them. The canvas takes
   its aspect ratio from the graph's own bounds, so it fits — there is no
   empty space to scroll into.
   ===================================================================== */

.wfp{
  border:1px solid var(--line);
  border-radius:var(--r-md);
  background:var(--bg,#0a1420);
  overflow:hidden;
  box-shadow:var(--e-1);
}

/* window bar, same idiom as the other framed artefacts on the site */
.wfp-bar{
  display:flex;align-items:center;gap:.5rem;
  padding:.6rem .85rem;
  background:var(--card,#12212f);
  border-bottom:1px solid var(--line);
}
.wfp-dot{width:8px;height:8px;border-radius:50%;background:rgba(160,200,220,.22)}
.wfp-title{
  margin-left:.5rem;font-family:var(--mono);font-size:.68rem;
  letter-spacing:.1em;color:var(--ink-dim);white-space:nowrap;
}
.wfp-sub{
  font-family:var(--mono);font-size:.6rem;letter-spacing:.1em;
  color:var(--muted);white-space:nowrap;
}
.wfp-run{
  margin-left:auto;flex:none;
  display:inline-flex;align-items:center;gap:.4rem;
  padding:.42rem .8rem;border-radius:var(--r-pill);
  background:var(--accent,#F4738F);color:#0a1420;
  border:0;font-family:var(--mono);font-size:.62rem;
  letter-spacing:.14em;text-transform:uppercase;font-weight:600;
  cursor:pointer;
  transition:filter var(--t-fast) var(--ease),transform var(--t-base) var(--ease);
}
.wfp-run:hover{filter:brightness(1.08)}
.wfp-run:active{transform:translateY(1px)}
.wfp-run[disabled]{opacity:.55;cursor:progress}

/* the canvas takes its shape from the graph, so nothing is scrolled into
   empty space. n8n's faint dot grid, at low contrast. */
.wfp-canvas{
  position:relative;container-type:inline-size;
  aspect-ratio:var(--vw) / var(--vh);
  background-image:radial-gradient(rgba(160,200,220,.10) 1px,transparent 1px);
  background-size:22px 22px;
}
.wfp-wires{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.wfp-edge{
  fill:none;stroke:rgba(160,200,220,.30);stroke-width:1.6;
  stroke-linecap:round;
}
.wfp-edge.is-sub{stroke-dasharray:4 4;stroke:rgba(244,115,143,.42)}

/* nodes: an icon chip with the name under it, which is how n8n reads */
/* centred on its own coordinate, and never wider than the tightest gap
   between two nodes in the graph — otherwise the labels collide */
/* Centred horizontally on its coordinate, but NOT vertically: a node is a
   chip with a label under it, so centring the whole block puts its middle
   below the chip — and the wires, which are drawn at the coordinate, then
   run straight through the label text. Offsetting by half a chip instead
   puts the chip's centre on the coordinate, so wires meet chips and the
   labels sit clear underneath. */
.wfp-node{
  position:absolute;
  width:7.5cqw;max-width:98px;min-width:52px;
  transform:translate(-50%,calc(var(--chip-h,42px) / -2));
  display:flex;flex-direction:column;align-items:center;text-align:center;gap:.38rem;
}
.wfp-canvas{--chip-h:clamp(34px,3.6cqw,52px)}
.wfp-chip{
  display:grid;place-items:center;
  width:3.6cqw;min-width:34px;max-width:52px;aspect-ratio:1;
  border-radius:9px;
  background:var(--card,#12212f);
  border:1px solid var(--line);
  box-shadow:var(--e-1);
  transition:border-color var(--t-base) var(--ease),
             box-shadow var(--t-base) var(--ease),
             transform var(--t-base) var(--ease);
}
/* the real n8n marks: brand icons keep their own colours, the structural
   ones are drawn with currentColor and take the node's tone */
/* Sized from the chip token rather than as a percentage of the chip box:
   the percentage resolved against an unclamped width in some contexts and
   produced 520px icons spilling across the canvas. overflow:hidden is the
   belt-and-braces so a mis-sized mark can never escape its chip again. */
.wfp-chip{overflow:hidden}
/* ti-v12 sets `.wf-canvas img{min-width:520px}` for the old flat diagram
   exports. min-width wins over both width and max-width, so every node
   icon inside a player was forced to 520px and clipped by its chip. */
.wf-canvas .wfp img,.wfp-chip img,.wfp-chip svg{
  min-width:0 !important;min-height:0 !important;
  width:calc(var(--chip-h,42px) * .56) !important;
  height:calc(var(--chip-h,42px) * .56) !important;
  display:block;object-fit:contain;border-radius:0;
}
.wfp-chip svg{color:var(--ink-dim,#9fb3c4)}
.wfp-node.is-trigger .wfp-chip svg{color:var(--green,#35c9a4)}
.wfp-node.is-logic   .wfp-chip svg{color:var(--gold,#c9a25c)}
.wfp-node.is-ai      .wfp-chip svg,
.wfp-node.is-sub     .wfp-chip svg{color:#F4738F}
/* triggers are rounded down one side in n8n — the shape says "starts here" */
.wfp-node.is-trigger .wfp-chip{border-radius:9px 9px 9px 22px}
.wfp-node.is-sub .wfp-chip{width:2.9cqw;min-width:28px;border-style:dashed}

.tone-blue   .wfp-chip{color:#6aa9e0}
.tone-green  .wfp-chip{color:var(--green,#35c9a4)}
.tone-steel  .wfp-chip{color:#9fb3c4}
.tone-gold   .wfp-chip{color:var(--gold,#c9a25c)}
.tone-coral  .wfp-chip{color:#F4738F}

/* node names were small enough to squint at; n8n sets them at a readable
   size and so does this now */
.wfp-name{
  font-size:clamp(.7rem,1.28cqw,.86rem);line-height:1.28;
  color:var(--ink);font-weight:500;letter-spacing:-.005em;
}
.wfp-node.is-sub .wfp-name{color:var(--ink-dim);font-size:clamp(.62rem,1.1cqw,.76rem)}

/* ---- running ---- */
.wfp-node.is-running .wfp-chip{
  border-color:var(--green,#35c9a4);
  box-shadow:0 0 0 3px rgba(53,201,164,.16),var(--e-2);
  transform:translateY(-2px);
}
.wfp-node.is-done .wfp-chip{
  border-color:rgba(53,201,164,.55);
  box-shadow:0 0 0 1px rgba(53,201,164,.18);
}
.wfp-node.is-done .wfp-chip svg{color:var(--green,#35c9a4)}

.wfp-edge.is-live{
  stroke:var(--green,#35c9a4);
  stroke-dasharray:1;stroke-dashoffset:1;
  animation:wfp-flow .5s var(--ease) forwards;
}
@keyframes wfp-flow{to{stroke-dashoffset:0}}
.wfp-edge.is-done{stroke:rgba(53,201,164,.5);stroke-dasharray:none}
.wfp-edge.is-sub.is-done{stroke-dasharray:4 4;stroke:rgba(244,115,143,.6)}

@media (prefers-reduced-motion:reduce){
  .wfp-edge.is-live{animation:none;stroke-dashoffset:0}
  .wfp-node.is-running .wfp-chip{transform:none}
}

/* A narrow canvas cannot carry eight labels without them touching. Below
   this width the names step down, and below that they go entirely — the
   shape of the graph still reads, which is what a small diagram is for. */
/* Node width is sized to the densest graph on the page: the ten-node
   triage flow has its two closest nodes 8.1% of the canvas apart, so a
   node wider than that guarantees the labels touch. Below the widths
   here the names step down and then go, leaving the shape — which is
   all a diagram this small needs to say. */
@container (max-width: 900px){
  .wfp-name{font-size:.6rem;line-height:1.18;letter-spacing:0}
}
@container (max-width: 420px){
  .wfp-name{display:none}
}

/* On a phone a ten-node graph cannot be shown whole and still be read.
   Rather than shrink it into mush, the canvas keeps a legible minimum
   and scrolls sideways — into the rest of the diagram, not into empty
   space, which is what the old export did. */
@media (max-width:700px){
  /* the player scrolls, not the canvas: the canvas positions its nodes
     absolutely, so it is the element that has to hold the width */
  .wfp{
    overflow-x:auto;overscroll-behavior-x:contain;
    -webkit-overflow-scrolling:touch;scrollbar-width:thin;
  }
  /* the minimum scales with the graph — a five-node flow needs far less
     room than a ten-node one, and --vw already carries that complexity */
  .wfp-canvas{min-width:calc(var(--vw) * 0.46px)}
  .wfp-bar{position:sticky;left:0}
  .wfp-name{display:block;font-size:.58rem}
  /* say so, rather than leaving them to discover it */
  .wfp-bar::after{
    content:"swipe";margin-left:.6rem;
    font-family:var(--mono);font-size:.55rem;letter-spacing:.16em;
    text-transform:uppercase;color:var(--muted);
  }
}

/* =====================================================================
   17e — REVEAL CARDS
   Every explanatory card on the site showed a label, a title and a full
   paragraph at once, so a section of six cards asked the reader to take
   in six paragraphs before deciding whether any of it mattered.

   The face of the card is now the number and the title, at a size you
   can read across the room. The paragraph arrives when you want it —
   hover on a mouse, tap or Enter anywhere else. The whole card is the
   control, so there is no small target to hit.
   ===================================================================== */

.step,.bcard,.pillar,.rstep,.v{
  position:relative;cursor:pointer;
  border-radius:var(--r-md);
  transition:border-color var(--t-base) var(--ease),
             transform var(--t-base) var(--ease),
             box-shadow var(--t-base) var(--ease);
}
/* Pointing at a card tints the whole surface in that page's accent, so
   which one you are on is unmistakable rather than a one-pixel border
   change. The tint is a wash at 7% — enough to read as "this one",
   not enough to fight the text sitting on it. */
.step::before,.bcard::before,.pillar::before,.rstep::before,.v::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  border-radius:inherit;
  background:var(--accent,#c9a25c);
  opacity:0;transition:opacity var(--t-base) var(--ease);
}
/* lift the content above the tint — but not the icon, which is absolutely
   positioned and would be knocked back into flow by position:relative */
.step > *:not(.s-ico),.bcard > *:not(.s-ico),.pillar > *:not(.s-ico),
.rstep > *:not(.s-ico),.v > *:not(.s-ico){position:relative;z-index:1}
.s-ico{z-index:2}

.step:hover,.bcard:hover,.pillar:hover,.rstep:hover,.v:hover,
.step.is-open,.bcard.is-open,.pillar.is-open,.rstep.is-open,.v.is-open{
  border-color:var(--accent,#c9a25c);
}
.step:hover::before,.bcard:hover::before,.pillar:hover::before,
.rstep:hover::before,.v:hover::before,
.step.is-open::before,.bcard.is-open::before,.pillar.is-open::before,
.rstep.is-open::before,.v.is-open::before{opacity:.07}

/* the numeral and icon come up to full strength with it */
.step:hover .s-num,.step.is-open .s-num{filter:brightness(1.12)}
.step:focus-visible,.bcard:focus-visible,.pillar:focus-visible,.rstep:focus-visible,.v:focus-visible{box-shadow:var(--focus);outline:none}

/* the face: a big numeral, then the label, then the title */

.step > i,.pillar > .p-step,.rstep > i,.v > i{
  display:flex;align-items:baseline;gap:.55rem;
  font-style:normal;margin-bottom:.55rem;
}
.step .s-num,.pillar .s-num,.rstep .s-num,.v .s-num{
  font-family:var(--display);font-weight:700;
  font-size:clamp(1.9rem,2.6vw,2.5rem);line-height:.92;
  letter-spacing:-.04em;color:var(--accent,#c9a25c);
  font-variant-numeric:tabular-nums;
}
.step .s-lbl,.pillar .s-lbl,.rstep .s-lbl,.v .s-lbl{
  font-family:var(--mono);font-size:.66rem;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-dim);
}
.step > b,.pillar > h3,.rstep > b,.v > b{
  display:block;font-family:var(--display);font-weight:700;
  font-size:clamp(1.02rem,1.35vw,1.18rem);line-height:1.25;
  letter-spacing:-.015em;color:var(--ink);
  padding-right:2.2rem;   /* clears the affordance */
}

/* The card's own icon, top right.
   These rules are keyed to the class, not to a parent — when they were
   written as ".step .s-ico" the same span appended to a .bcard matched
   nothing, so it laid out as a full-width inline element and its SVG
   rendered at 318x318 with the default black fill. That was the black
   triangle sitting in the middle of every card on this section, and the
   reason those cards were 481px tall. */
.s-ico{
  position:absolute;top:1.15rem;right:1.15rem;
  width:20px;height:20px;color:var(--accent,#c9a25c);opacity:.55;
  pointer-events:none;
  transition:opacity var(--t-base) var(--ease),transform var(--t-base) var(--ease);
}
.s-ico svg{
  width:100%;height:100%;display:block;
  fill:none !important;stroke:currentColor;
  stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;
}
.step:hover .s-ico,.step.is-open .s-ico,.pillar:hover .s-ico,.pillar.is-open .s-ico,.rstep:hover .s-ico,.rstep.is-open .s-ico,.v:hover .s-ico,.v.is-open .s-ico{opacity:1;transform:translateY(-1px)}

/* the detail, folded away until asked for */
.step > p,.bcard > p,.bcard > .eg,.pillar > p,.rstep > p,.v > p{
  display:grid;grid-template-rows:0fr;
  opacity:0;margin-top:0;
  transition:grid-template-rows var(--t-slow) var(--ease),
             opacity var(--t-base) var(--ease),
             margin-top var(--t-slow) var(--ease);
}
.step > p > *,.bcard > p > *,.pillar > p > *,.rstep > p > *,.v > p > *{min-height:0}
/* grid-template-rows animation needs an inner wrapper; the JS adds one */
.r-in{overflow:hidden}

.step.is-open > p,.bcard.is-open > p,.bcard.is-open > .eg,.pillar.is-open > p,.rstep.is-open > p,.v.is-open > p{
  grid-template-rows:1fr;opacity:1;margin-top:.85rem;
}
@media (hover:hover) and (pointer:fine){
  .step:hover > p,.bcard:hover > p,.bcard:hover > .eg,.pillar:hover > p,.rstep:hover > p,.v:hover > p{
    grid-template-rows:1fr;opacity:1;margin-top:.85rem;
  }
}

/* the affordance — a plus that becomes a minus */
.step::after,.bcard::after,.pillar::after,.rstep::after,.v::after{
  content:"";position:absolute;right:1.2rem;bottom:1.15rem;
  width:11px;height:11px;
  background:
    linear-gradient(var(--ink-dim),var(--ink-dim)) center/11px 1.5px no-repeat,
    linear-gradient(var(--ink-dim),var(--ink-dim)) center/1.5px 11px no-repeat;
  opacity:.5;transition:transform var(--t-base) var(--ease),opacity var(--t-base) var(--ease);
}
.step.is-open::after,.bcard.is-open::after,.pillar.is-open::after,.rstep.is-open::after,.v.is-open::after{transform:rotate(45deg);opacity:.9}
@media (hover:hover) and (pointer:fine){
  .step:hover::after,.bcard:hover::after,.pillar:hover::after,.rstep:hover::after,.v:hover::after{transform:rotate(45deg);opacity:.9}
}

/* bcard face: the tag and the heading carry it */
/* This is the line that says what the card is about — "Never auto-sends",
   "Read-only by default". At 9.9px it was the smallest text on the page
   while carrying the most weight. */
.bcard > .tag{
  font-family:var(--mono);font-size:.78rem;letter-spacing:.16em;
  text-transform:uppercase;color:var(--accent,#c9a25c);font-weight:500;
  background:transparent !important;border:0 !important;padding:0 !important;
  display:block;margin-bottom:.75rem;
}
/* these three carry their own meaning: a promise, a limit, an assurance */
.bcard.m > .tag{color:var(--green-bright,#5fe0bf)}
.bcard.o > .tag{color:var(--gold-bright,#e0bd7d)}
.bcard > h3{padding-right:2.2rem}

/* stillness: no folding, everything simply readable */
@media (prefers-reduced-motion:reduce){
  .step > p,.bcard > p,.bcard > .eg,.pillar > p,.rstep > p,.v > p{
    grid-template-rows:1fr !important;opacity:1 !important;margin-top:.85rem !important;
    transition:none !important;
  }
  .step::after,.bcard::after,.pillar::after,.rstep::after,.v::after{display:none}
}

/* A category label with no numeral IS the card's marker — it does the job
   the big numeral does elsewhere, so it gets weight to match. At .72rem it
   was the smallest thing on a card it was supposed to be leading. */
.step .s-lbl-solo{
  font-size:.86rem;
  letter-spacing:.14em;
  font-weight:600;
  color:var(--accent,#c9a25c);
}


/* =====================================================================
   17f — THE SECTION SYSTEM
   Every band on every page is one of three grounds and one of two
   layouts. That is the whole vocabulary. It is deliberately small:
   a site reads as designed when the same handful of decisions recur,
   and as generated when each section invents its own treatment.

     GROUND
       (default)      the page itself — narrative sections
       .sec-raised    lifted one step — alternating emphasis
       .sec-recessed  sunk, for framed artefacts: diagrams, mockups,
                      anything that should look placed ON the page

     LAYOUT
       (default)      heading full width, content beneath
       .section-split heading pinned left, content running past it

   No section carries background animation. The motion budget belongs
   to things that carry meaning: the entrance order, the workflow
   players, and the four-moves sequence.
   ===================================================================== */

/* Targeted by class, not by element: some pages use <section class="section">
   and others (pricing, parts of data) use plain divs with the same classes.
   Keying off the element would have left those pages outside the system. */
:is(.section,.svc-hero,.cta-band,.magnet,.faq-band,.route2,.counter-band){
  position:relative;isolation:isolate;
}
:is(.section,.cta-band,.magnet,.faq-band,.route2,.counter-band){
  border-top:1px solid var(--line-soft);
}
/* the first band on a page needs no seam above it */
main > :first-child,main > :first-child > :first-child{border-top:0}

/* --- ground 1: the page (default) — nothing to declare -------------- */

/* --- ground 2: raised ---------------------------------------------- */
.sec-raised,.section.alt,.magnet,.faq-band{background:var(--bg2,#0d1a29)}
.sec-raised::before,.section.alt::before,.magnet::before,.faq-band::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-image:radial-gradient(rgba(160,200,220,.075) 1px,transparent 1px);
  background-size:26px 26px;
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 18%,#000 82%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 18%,#000 82%,transparent);
}

/* --- ground 3: recessed, for framed artefacts ---------------------- */
.sec-recessed,.ground-canvas{
  background:#081019;
  box-shadow:inset 0 1px 0 rgba(0,0,0,.5), inset 0 -1px 0 rgba(255,255,255,.02);
}
.sec-recessed::before,.ground-canvas::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(78% 60% at 50% 46%, rgba(160,200,220,.035), transparent 74%);
}

/* the ruled band is retired — it is simply a raised section now */
.ground-ruled{background:var(--bg2,#0d1a29)}
.ground-ruled::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-image:radial-gradient(rgba(160,200,220,.075) 1px,transparent 1px);
  background-size:26px 26px;
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 18%,#000 82%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 18%,#000 82%,transparent);
}

/* one accent tick opens a raised band, and only a raised band */
.sec-raised::after,.section.alt::after,.faq-band::after{
  content:"";position:absolute;top:0;left:0;right:0;height:1px;z-index:-1;
  background:linear-gradient(90deg,transparent,var(--accent,#c9a25c),transparent);
  opacity:.26;
}

/* =====================================================================
   17h — LABELS THAT OUTRANK THEIR OWN CONTENT
   A recurring fault across the older layers: the label naming a thing
   was set smaller than the sentence explaining it, so the eye met the
   explanation before it knew what was being explained. A label can be
   quiet — mono, tracked, muted — without being the smallest thing in
   the block. Floor is 0.72rem, and the ones doing real naming work sit
   above their own body copy in the reading order.
   ===================================================================== */

.svc-meta span,.dash-kpis span,.kpi span,
.found-facts span > b ~ span,.station .lbl,.builton .lbl{
  font-size:.72rem !important;
  letter-spacing:.15em;
  color:var(--ink-dim) !important;
  margin-bottom:.35rem;
}

/* The two headings under each workflow. Mono uppercase made them read as
   machine labels when they are actually the two things a buyer most wants
   to hear — how it sounds like them, and what it will never do. Set in the
   display face, at the page's accent, so they read as claims. */
.wf-facts span{
  font-family:var(--display) !important;
  font-size:.94rem !important;
  font-weight:700;
  letter-spacing:-.012em !important;
  text-transform:none !important;
  color:var(--accent,#F4738F) !important;
  margin-bottom:.45rem;
}
.wf-facts b{font-size:.92rem;line-height:1.55;color:var(--ink-dim);font-weight:400}

/* the player's own title was set as small as its metadata */
.wfp-title{font-size:.86rem !important;letter-spacing:.02em;color:var(--ink) !important}
.wfp-sub{font-size:.66rem}

/* =====================================================================
   17i — ONE ACCENT PER PAGE, INCLUDING THE HEADLINES
   The emphasised clause in every heading was locked to the brand green.
   On the automations page that put green inside a headline sitting six
   inches above coral cards and a coral run button — two accent colours
   competing for the same job.

   The emphasis now takes the page's accent, so each product page reads
   as one decision rather than a house colour with a theme bolted on.
   Dashboards is unaffected: its accent already is the green. Measured
   on the page ground: coral 6.79, gold 7.77, cyan 8.60, green 8.86.
   ===================================================================== */

body[class*="door-"] h1 em,
body[class*="door-"] h2 em,
body[class*="door-"] h3 em{color:var(--accent) !important}

/* and the things that sat green beside them */
body[class*="door-"] .note b,
body[class*="door-"] .h2row h2 em,
body[class*="door-"] .lede-note b{color:var(--accent) !important}

/* the primary button stays brand green on every page — the action colour
   is the one thing that must not shift from page to page */

/* =====================================================================
   17j — ENTRANCE
   Two changes to the existing reveal. First the timing: 0.75s is slow
   enough that the movement itself becomes the thing you notice, so it
   drops to 0.5s, inside the 400-600ms the motion spec calls for.
   Second the order: each element carries a delay set from its position
   in its own section, so a band resolves the way it reads instead of
   arriving as one slab.
   ===================================================================== */

.reveal{
  transition-duration:.5s !important;
  transition-timing-function:var(--ease-out-soft,cubic-bezier(.33,1,.68,1)) !important;
  transition-delay:var(--rd,0ms) !important;
  transform:translateY(18px);
  will-change:opacity,transform;
}
.reveal.in{transform:none}
/* once it has arrived, stop reserving a compositor layer for it */
.reveal.in{will-change:auto}

/* headings get a shade more travel — they are the beat you land on */
.head > h2.reveal,.h2row h2.reveal{transform:translateY(26px)}

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

/* =====================================================================
   17k — ONE SECTION THAT BREAKS THE GRID
   Every band on this page is the same shape: full-width heading, then a
   row of boxes. Four of those in a row is what makes a page read as
   generated rather than composed.

   This one splits 5:7 and pins its heading while the cards move past it,
   so the section is read as "here is the menu" rather than another slab.
   It is the only section on the page built this way, which is the point.
   ===================================================================== */

@media (min-width:1000px){
  .section-split > .wrap{
    display:grid;grid-template-columns:5fr 7fr;
    gap:clamp(2.5rem,5vw,5rem);align-items:start;
  }
  .section-split .head{
    position:sticky;top:7rem;margin:0;
    padding-right:1rem;
  }
  .section-split .head h2{font-size:clamp(1.9rem,2.9vw,2.5rem) !important}
  .section-split .steps{grid-template-columns:1fr 1fr !important;margin:0}
}
/* the sticky column needs the band to be tall enough to travel */
.section-split{padding-block:clamp(4.5rem,9vh,8rem)}

/* =====================================================================
   17l — INLINE CTA BARS
   The page ran 5,948px between the header button and the next chance to
   act — 91% of its own height with no way forward. These sit at the two
   points where someone is most likely to be convinced: straight after
   the working diagrams, and after the list of jobs. Each carries the
   scope note, so the answer to "is that £750 each?" travels with the ask
   instead of waiting in a FAQ.
   ===================================================================== */

/* Roomier than it was — it was reading as squeezed. And the button is the
   same green pill as the header, deliberately: the green thing is how you
   get in touch, everywhere on the site, so it is recognised rather than
   read each time. */
.cta-inline{
  display:flex;align-items:center;gap:1.3rem 1.8rem;flex-wrap:wrap;
  margin:2.6rem 0 .5rem;padding:1.75rem 1.9rem;
  background:var(--card,#12212f);
  border:1px solid var(--line);
  border-left:2px solid var(--accent,#F4738F);
  border-radius:var(--r-md);
}
.cta-inline .btn.solid{
  padding:.8rem 1.5rem;border-radius:var(--r-sm);
  font-size:.95rem;font-weight:600;
  background:var(--green,#35c9a4);color:#06121b !important;
  box-shadow:var(--e-glow);
}
/* inside a split band the wrap is a grid, so the bar has to be told to
   span it — otherwise it drops into the narrow heading column */
.section-split > .wrap > .cta-inline{grid-column:1 / -1;margin-top:2.8rem}
.cta-inline p{margin:0;font-size:1rem;line-height:1.45;color:var(--ink);flex:1 1 22ch}
.cta-inline .btn{flex:none;white-space:nowrap}
.cta-inline .ci-note{
  flex:1 1 100%;order:3;
  font-family:var(--mono);font-size:.68rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);
}
@media (min-width:760px){
  .cta-inline .ci-note{flex:0 1 auto;order:0;text-align:right}
}

/* =====================================================================
   17m — COMMON QUESTIONS
   Uses the reveal card already on the page rather than introducing a
   new component: question on the face, answer on demand. A sceptic
   opens the two that worry them and ignores the rest.
   ===================================================================== */

.faq-band .qa{grid-template-columns:repeat(3,1fr)}

/* The question is the card. It used to sit as a small label above a bold
   summary, which put the answer in front of the thing being asked — the
   wrong way round for a FAQ. The question now carries the weight, in the
   page accent, and the answer waits underneath. */
.faq-band .qa .step > b{
  font-size:clamp(1.06rem,1.45vw,1.26rem) !important;
  line-height:1.3;
  color:var(--accent,#F4738F) !important;
  padding-right:2.4rem;
}
.faq-band .qa .step > p{font-size:.95rem}
.faq-band .qa .step{padding-block:1.5rem}
@media (max-width:900px){.faq-band .qa{grid-template-columns:1fr}}
@media (min-width:901px) and (max-width:1180px){.faq-band .qa{grid-template-columns:1fr 1fr}}

/* =====================================================================
   17n — THE FOUR MOVES, PERFORMED
   This section's whole job is to teach a sequence: trigger, fetch,
   decide, act. So the sequence runs. A line fills across each card in
   turn and the numeral brightens with it, on a slow loop, so the shape
   is demonstrated rather than asserted.

   This is where the motion budget went after the background traces came
   out. Motion that performs the content earns its place; motion that
   decorates the space behind it does not.
   ===================================================================== */

.moves-seq .step{overflow:hidden}
.moves-seq .step .s-seq{
  position:absolute;left:0;top:0;height:2px;width:100%;
  transform:scaleX(0);transform-origin:left;
  background:linear-gradient(90deg,var(--accent,#F4738F),transparent);
  pointer-events:none;
}
@keyframes ti-seq{
  0%      {transform:scaleX(0)}
  9%      {transform:scaleX(1)}
  42%     {transform:scaleX(1)}
  52%,100%{transform:scaleX(0)}
}
@keyframes ti-seq-num{
  0%,3%    {opacity:.55}
  9%,42%   {opacity:1}
  52%,100% {opacity:.55}
}
.moves-seq.seq-on .step .s-seq{animation:ti-seq 8s var(--ease) infinite}
.moves-seq.seq-on .step .s-num{animation:ti-seq-num 8s var(--ease) infinite}
.moves-seq.seq-on .step:nth-child(1) .s-seq,
.moves-seq.seq-on .step:nth-child(1) .s-num{animation-delay:0s}
.moves-seq.seq-on .step:nth-child(2) .s-seq,
.moves-seq.seq-on .step:nth-child(2) .s-num{animation-delay:.75s}
.moves-seq.seq-on .step:nth-child(3) .s-seq,
.moves-seq.seq-on .step:nth-child(3) .s-num{animation-delay:1.5s}
.moves-seq.seq-on .step:nth-child(4) .s-seq,
.moves-seq.seq-on .step:nth-child(4) .s-num{animation-delay:2.25s}

/* pointing at a card takes it out of the loop — you are reading it now */
.moves-seq .step:hover .s-seq,.moves-seq .step.is-open .s-seq{
  animation:none;transform:scaleX(1);
}
.moves-seq .step:hover .s-num,.moves-seq .step.is-open .s-num{animation:none;opacity:1}

@media (prefers-reduced-motion:reduce){
  .moves-seq .s-seq,.moves-seq .s-num{animation:none !important}
  .moves-seq .s-seq{transform:scaleX(1)}
}

/* The page label — "Door 03 · Automations" — is what tells a visitor
   which page they landed on, and it was set at 0.7rem, smaller than any
   body text near it. It now leads the hero the way it should. */
.svc-hero .eyebrow,.hero .eyebrow{
  font-size:.92rem !important;
  letter-spacing:.2em;
  font-weight:600;
  color:var(--accent,#F4738F) !important;
  margin-bottom:1.1rem;
  display:inline-block;
}

/* =====================================================================
   17p — HANDOVER STRIP
   "Built on my bench" was a whole band saying what the trust section's
   third card already said. Its four steps live here instead, set as a
   run rather than another row of boxes.
   ===================================================================== */

.basis:has(> .bcard:nth-child(2):last-child){grid-template-columns:1fr 1fr}

.handover{
  margin-top:clamp(2.2rem,4vw,3.4rem);
  padding-top:clamp(1.8rem,3vw,2.6rem);
  border-top:1px solid var(--line-soft);
}
.ho-lead{
  font-family:var(--display);font-weight:700;
  font-size:clamp(1.1rem,1.6vw,1.35rem);letter-spacing:-.018em;
  color:var(--ink);margin-bottom:1.5rem;
}
.handover .ho-steps{
  display:grid;grid-template-columns:repeat(4,1fr);gap:0;
  align-items:start;      /* otherwise each step drops to its own baseline */
}
.handover .ho-steps .step{
  background:transparent !important;
  border:0 !important;border-left:1px solid var(--line-soft) !important;
  border-radius:0 !important;box-shadow:none !important;
  padding:.2rem 1.4rem;
}
.handover .ho-steps .step:first-child{border-left:0 !important;padding-left:0}
.handover .ho-steps .step::before{display:none}
/* no plus on the run — it sat on top of the step title */
.handover .ho-steps .step::after{display:none}
.handover .ho-steps .step > p{margin-top:.4rem}
.handover .ho-steps .step .s-ico{display:none}
.handover .ho-steps .step .s-num{font-size:clamp(1.4rem,1.9vw,1.75rem)}
.handover .ho-steps .step > b{font-size:.98rem;padding-right:1.4rem}

@media (max-width:900px){
  .handover .ho-steps{grid-template-columns:1fr 1fr}
  .handover .ho-steps .step{padding:1rem;border-left:0 !important;
    border-top:1px solid var(--line-soft) !important}
}
@media (max-width:560px){ .handover .ho-steps{grid-template-columns:1fr} }

/* =====================================================================
   17q — THE RUN COUNT
   The one number on the page that cannot be argued with — how often
   these things have actually run, read live from the n8n instance that
   runs them. No client required to say it.
   ===================================================================== */

.run-stat{
  display:flex;align-items:baseline;gap:.9rem;flex-wrap:wrap;
  margin:1.6rem 0 0;padding-top:1.3rem;
  border-top:1px solid var(--line-soft);position:relative;
}
.run-stat::before{
  content:"";position:absolute;top:-1px;left:0;width:2.4rem;height:2px;
  background:var(--accent,#F4738F);
}
.run-stat b{
  font-family:var(--display);font-weight:700;
  font-size:clamp(1.85rem,2.7vw,2.35rem);line-height:1;
  letter-spacing:-.035em;color:var(--accent,#F4738F);
  font-variant-numeric:tabular-nums;
}
.run-stat span{font-size:.88rem;line-height:1.5;color:var(--ink-dim);max-width:40ch}
.run-stat i{font-style:normal;color:var(--ink);font-variant-numeric:tabular-nums}

/* =====================================================================
   18 — MAIL CHOOSER
   Anchored to whichever mail link was clicked. Deliberately plain: it is
   a routing decision, not a moment.
   ===================================================================== */

.mail-pop{
  position:absolute;z-index:300;min-width:16rem;
  background:var(--card,#12212f);
  border:1px solid var(--line,rgba(160,200,220,.14));
  border-radius:var(--r-md);box-shadow:var(--e-3);padding:.55rem;
}
.mail-pop .mp-head{
  font-size:.74rem;line-height:1.4;color:var(--ink-dim);
  padding:.4rem .6rem .55rem;border-bottom:1px solid var(--line-soft);margin-bottom:.35rem;
}
.mail-pop .mp-head b{color:var(--ink);font-weight:500}
.mail-pop .mp-opt,.mail-pop .mp-copy{
  display:block;width:100%;text-align:left;
  padding:.55rem .6rem;border-radius:var(--r-sm);
  background:transparent;border:0;
  font-size:.88rem;color:var(--ink);cursor:pointer;
  transition:background-color var(--t-fast) var(--ease),color var(--t-fast) var(--ease);
}
.mail-pop .mp-opt:hover,.mail-pop .mp-copy:hover{background:rgba(160,200,220,.08)}
.mail-pop .mp-copy{
  color:var(--ink-dim);font-family:var(--mono);font-size:.68rem;
  letter-spacing:.1em;text-transform:uppercase;
  border-top:1px solid var(--line-soft);border-radius:0 0 var(--r-sm) var(--r-sm);
  margin-top:.35rem;padding-top:.65rem;
}

/* print: the one place the dark theme is wrong */
@media print{
  body{background:#fff;color:#000}
  .grain,.tiv9-frame,.site-head,canvas,.bg-fx{display:none !important}
}

/* =====================================================================
   19 — THE EXCEL WINDOW
   The old mockup was a generic three-dot window frame with a table in
   it, and it scrolled sideways because the table carried min-width:660.
   This is Excel: the green title bar, the name box and formula bar, the
   A/B/C column band, the row gutter, the sheet tabs along the bottom.

   Excel's own green lives HERE rather than as the page accent — as an
   accent it either fails contrast (#1D6F42 measures 2.65:1 on the card)
   or, lifted far enough to pass, becomes indistinguishable from the
   brand turquoise. In the artefact it is doing what it should: telling
   you which program this is.

   It runs the audit on a loop, the same idiom as the workflow players:
   a scan passes down the sheet and each fault lights as it is found.
   ===================================================================== */

.xl{
  border-radius:var(--r-md);overflow:hidden;
  background:#fff;color:#1f2329;
  border:1px solid rgba(120,200,185,.20);
  box-shadow:0 30px 70px rgba(0,0,0,.5);
  font-family:var(--mono);
}

/* title bar — Excel's green */
.xl-title{
  display:flex;align-items:center;gap:.55rem;
  padding:.5rem .8rem;background:#217346;color:#fff;
}
.xl-mark{
  flex:none;display:grid;place-items:center;width:17px;height:17px;
  border-radius:3px;background:#fff;color:#217346;
  font-family:var(--display);font-weight:700;font-size:.66rem;line-height:1;
}
.xl-title span{font-size:.68rem;letter-spacing:.04em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.xl-count{
  margin-left:auto;flex:none;font-style:normal;font-size:.6rem;
  letter-spacing:.1em;text-transform:uppercase;
  background:rgba(255,255,255,.16);padding:.2rem .5rem;border-radius:var(--r-xs);
}
.xl-count b{font-variant-numeric:tabular-nums}

/* name box + formula bar */
.xl-formula{
  display:flex;align-items:center;gap:.5rem;
  padding:.32rem .5rem;background:#f8f9fa;border-bottom:1px solid #d8dbe0;
  font-size:.62rem;color:#3b414a;
}
.xl-namebox{
  min-width:3.4rem;padding:.16rem .4rem;background:#fff;
  border:1px solid #d0d4da;border-radius:2px;text-align:center;
}
.xl-fx{font-style:italic;color:#6b727c;padding:0 .2rem}
.xl-formula-val{color:#1f2329;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* the grid */
.xl-grid{position:relative;overflow:hidden;background:#fff}
.xl-sheet{border-collapse:collapse;width:100%;table-layout:fixed;font-size:.68rem}
.xl-sheet th,.xl-sheet td{
  border:1px solid #e3e6ea;padding:.34rem .5rem;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* the A/B/C band and the row gutter are Excel's own grey */
.xl-sheet thead th{
  background:#f1f3f5;color:#5b6169;font-weight:500;
  text-align:center;font-size:.6rem;padding:.24rem;
  border-color:#d8dbe0;
}
.xl-corner{width:2.1rem}
.xl-rn{
  width:2.1rem;background:#f1f3f5;color:#5b6169;
  text-align:center;font-size:.6rem;border-color:#d8dbe0;
}
.xl-txt{text-align:left}
.xl-num{text-align:right;font-variant-numeric:tabular-nums}
.xl-dim{color:#8a9099}
.xl-hdr td{background:#fafbfc;color:#3b414a}
.xl-total td{background:#fafbfc;border-top:2px solid #c9ced5}

/* a flagged cell, before it has been found */
.xl-flag{position:relative;transition:background-color var(--t-base) var(--ease),
         box-shadow var(--t-base) var(--ease),color var(--t-base) var(--ease)}
.xl-pin{
  position:absolute;top:2px;right:2px;width:13px;height:13px;
  display:grid;place-items:center;border-radius:50%;
  background:#c0392b;color:#fff;font-size:.52rem;font-style:normal;
  transform:scale(0);transition:transform var(--t-base) cubic-bezier(.34,1.56,.64,1);
  z-index:2;
}
.xl.is-found-1 .xl-f1,.xl.is-found-2 .xl-f2,.xl.is-found-3 .xl-f3{
  background:#fdecea;color:#a5281b;font-weight:600;
  box-shadow:inset 0 0 0 2px #c0392b;
}
.xl.is-found-1 .xl-f1 .xl-pin,
.xl.is-found-2 .xl-f2 .xl-pin,
.xl.is-found-3 .xl-f3 .xl-pin{transform:scale(1)}

/* the scan that passes down the sheet while it audits */
.xl-scan{
  position:absolute;left:0;right:0;height:2px;top:0;
  background:linear-gradient(90deg,transparent,#217346,transparent);
  opacity:0;pointer-events:none;
}
.xl.is-scanning .xl-scan{animation:xl-sweep 1.5s cubic-bezier(.4,0,.2,1) forwards}
@keyframes xl-sweep{
  0%  {transform:translateY(0);opacity:0}
  10% {opacity:.9}
  90% {opacity:.9}
  100%{transform:translateY(var(--sh,300px));opacity:0}
}

/* sheet tabs, and the control */
.xl-tabs{
  display:flex;align-items:center;gap:.15rem;
  padding:.3rem .5rem;background:#f1f3f5;border-top:1px solid #d8dbe0;
}
.xl-tab{
  font-size:.6rem;color:#5b6169;padding:.24rem .6rem;
  border:1px solid transparent;border-radius:3px 3px 0 0;
}
.xl-tab.is-on{background:#fff;border-color:#d8dbe0;border-bottom-color:#fff;color:#217346;font-weight:600}
.xl-new{color:#8a9099}
.xl-run{
  margin-left:auto;flex:none;
  padding:.3rem .7rem;border:0;border-radius:var(--r-pill);
  background:#217346;color:#fff;cursor:pointer;
  font-family:var(--mono);font-size:.58rem;letter-spacing:.12em;
  text-transform:uppercase;font-weight:600;
  transition:filter var(--t-fast) var(--ease);
}
.xl-run:hover{filter:brightness(1.12)}
.xl-run[data-busy]{opacity:.7;cursor:progress}

@media (prefers-reduced-motion:reduce){
  .xl-scan{display:none}
  .xl-pin{transition:none}
}

/* =====================================================================
   19b — THE README PANEL MATCHES THE WORKBOOK
   The mock had a green active tab that read as an unexplained third
   colour on a gold-accented page. It is meant to be the same workbook as
   the hero, so the ARTEFACT now wears real Excel chrome and the green
   means "this is Excel", exactly as it does up there.

   Scope matters here. `.readme` is not the workbook — it is a two-column
   band, artefact on the left and the explanation on the right. An earlier
   pass painted the whole band white, which put the cream heading and the
   muted eyebrow onto white and dropped them to 2.38:1 and 1.17:1. So the
   band stays on the dark ground and only the sheet inside it goes white.

   The columns are also pinned. They were `auto 1fr`, and `auto` is sized
   by content: on dashboards the briefing strip is one long unbroken row,
   so it claimed 954px and left the text column 40px — one word per line
   and a 3,329px section. minmax() gives the text a floor it cannot fall
   through, on either page.
   ===================================================================== */

.readme{
  background:#08131f;
  border:1px solid var(--line-soft);
  border-radius:var(--r-md);overflow:hidden;
  box-shadow:0 24px 56px rgba(0,0,0,.42);
  grid-template-columns:minmax(0,1.05fr) minmax(19rem,.95fr);
  align-items:center;
  padding:clamp(1.1rem,2.4vw,1.9rem);
}
@media (max-width:820px){ .readme{grid-template-columns:1fr} }

/* ---- the artefact: an actual Excel sheet ---- */
.readme .tabstrip{display:flex;gap:.15rem;align-items:flex-end;padding:0 .35rem}
.readme .tabstrip span{
  font-family:var(--mono);font-size:.6rem;color:#5b6169;
  padding:.3rem .75rem;border:1px solid transparent;
  border-radius:4px 4px 0 0;background:#e8eaed;
}
.readme .tabstrip span.on{
  background:#fff;border-color:#d8dbe0;border-bottom-color:#fff;
  color:#217346;font-weight:700;
}
.readme .tabsheet{
  background:#fff;border:1px solid #d8dbe0;color:#2a3038;
  border-radius:0 6px 6px 6px;padding:1.05rem 1.2rem 1.2rem;
  box-shadow:0 10px 30px rgba(0,0,0,.28);
}
.readme .tabsheet b,.readme .tabsheet strong{
  display:block;font-family:var(--mono);font-size:.6rem;letter-spacing:.16em;
  text-transform:uppercase;color:#217346;margin:1.05rem 0 .4rem;
}
.readme .tabsheet > b:first-child,
.readme .tabsheet > strong:first-child{margin-top:0}
.readme .tabsheet u{text-decoration:none;color:#217346;font-weight:600}
.readme .tabsheet code,.readme .tabsheet .mono{
  font-family:var(--mono);font-size:.68rem;color:#2a3038;
}
/* dashboards puts the strip in on its own with no tab row above it */
.readme > .tabsheet{border-radius:6px}

/* =====================================================================
   19c — --muted WAS A SHADE UNDER THE FLOOR, EVERYWHERE
   #6d8296 measured 4.41:1 on the page ground and 4.11:1 on a card,
   against a 4.5:1 requirement. It is the footer tagline on six pages and
   the basis labels on dashboards, so one token carried most of the site's
   contrast debt. Verified with axe-core, not by eye.

   #78899c is the smallest lift that clears it: five pages go to zero
   contrast violations and the site total drops 48 -> 35. Deliberately the
   minimum, so the colour stays the quiet grey-blue it was drawn as.
   ===================================================================== */

:root{ --muted:#78899c; }

/* =====================================================================
   19d — CARDS THAT CONTAIN A LINK ARE NOT BUTTONS
   Three cards in "the rungs below this one" hold a real link to another
   page. They were also being given role="button" and a tab stop, which
   nests one control inside another: two keyboard stops, neither of them
   self-explanatory. The JS now leaves those alone, and since there is no
   button to press, their text simply shows.
   ===================================================================== */

.rcard.rcard-static{cursor:default}
.rcard.rcard-static .r-body{max-height:none;opacity:1;overflow:visible}
.rcard.rcard-static .r-more{color:var(--green-bright)}
@media (hover:hover){
  .rcard.rcard-static:hover{transform:none;box-shadow:none}
  .rcard.rcard-static:hover::after{opacity:0}
  .rcard.rcard-static:hover .r-eyebrow,
  .rcard.rcard-static:hover .r-title,
  .rcard.rcard-static:hover .r-body,
  .rcard.rcard-static:hover .r-more{color:inherit}
  .rcard.rcard-static:hover .r-title{color:var(--ink)}
  .rcard.rcard-static:hover .r-more{color:var(--green-bright)}
  .rcard.rcard-static a:hover{text-decoration:underline}
}

/* =====================================================================
   19e — THE MOCKS WERE THE LAST CONTRAST DEBT
   Every remaining failure sat inside the two artefacts on the homepage:
   the dashboard mock and the before/after workbook. Small type (8-11px)
   in muted greys, between 2.49:1 and 4.47:1 where 4.5:1 is the floor.

   These are demo chrome, so the instinct is to let them off. But they are
   the largest thing on the page and the first thing read, and "it is only
   a mock" is not a defence anyone can see. Each value below is the least
   change that clears its own measured background, so the mocks keep
   reading as quiet supporting detail rather than becoming loud.
   ===================================================================== */

/* --- on the dark card #18223a --- */
.mcard .c-lbl span,
.src-chip,
#askSrc{color:#8b9bb0}
.l-cur,.l-prev{color:#8b9bb0}

/* --- on the deep page ground #0a1420 / #0b111d --- */
.a-cap{color:#808b9b}
.m-meta{color:#808b9b}
.url{color:#808b9b}

/* --- inside the light "before" workbook --- */
.ba-sheet .bs-head span{color:#5a6675}
.ba-sheet .bs-head span:nth-child(4){color:#8f4433}
.ba-sheet .bs-bar{color:#74544a}
.ba-before .ba-tag{color:#8f4433}

/* .legend span (0,1,1) outranks a bare .l-cur (0,1,0), so match its weight */
.legend .l-cur,.legend .l-prev,.legend span{color:#8b9bb0}

/* ti-v12.css:520 sets .mock .spark-foot (0,2,0). Same weight, later file. */
.mock .spark-foot{color:#8b9bb0}

/* A static card has nothing left to reveal, so the "More" cue is a lie. */
.rcard.rcard-static .r-more{display:none}

/* =====================================================================
   20 — THE EXCEL WINDOW, ACTUALLY BEHAVING LIKE EXCEL
   The old loop swept a bar down the sheet and lit three cells. It read as
   "a table with a green bar", because none of what actually identifies
   Excel was there. What identifies Excel is the selection: a heavy green
   rectangle with a fill handle, the Name Box tracking it, the column and
   row headers tinting to match, the formula bar showing that cell's real
   formula, and a status bar totting up Sum/Average/Count as you go.

   So the cursor now travels the sheet and the chrome follows it. The
   faults are found by landing on them, not by lighting up on a timer.
   ===================================================================== */

/* the selection rectangle */
.xl-cursor{
  position:absolute;left:0;top:0;width:0;height:0;
  border:2px solid #217346;border-radius:1px;
  pointer-events:none;opacity:0;z-index:4;
  transition:opacity 140ms linear,
             left 380ms cubic-bezier(.22,.61,.36,1),
             top  380ms cubic-bezier(.22,.61,.36,1),
             width 380ms cubic-bezier(.22,.61,.36,1),
             height 380ms cubic-bezier(.22,.61,.36,1);
}
.xl-cursor.is-on{opacity:1}
/* Excel's fill handle: the small square on the bottom-right corner */
.xl-handle{
  position:absolute;right:-4px;bottom:-4px;width:6px;height:6px;
  background:#217346;border:1px solid #fff;border-radius:1px;
}
/* landing on a fault tints the selection red rather than green */
.xl-cursor.is-bad{border-color:#c0392b}
.xl-cursor.is-bad .xl-handle{background:#c0392b}

/* the note that appears beside a found fault */
.xl-callout{
  position:absolute;z-index:5;pointer-events:none;
  max-width:15rem;padding:.4rem .55rem;
  background:#fffbe6;border:1px solid #e0d089;border-left:3px solid #c0392b;
  border-radius:3px;box-shadow:0 6px 18px rgba(0,0,0,.18);
  font-family:var(--mono);font-size:.6rem;line-height:1.45;color:#4a3f2a;
  opacity:0;transform:translateY(4px);
  transition:opacity 180ms var(--ease),transform 180ms var(--ease);
}
.xl-callout.is-on{opacity:1;transform:translateY(0)}

/* the headers tint with the selection, exactly as Excel does */
.xl-sheet thead th.is-hot{background:#d3e8dd;color:#134a2c;font-weight:700}
.xl-rn.is-hot{background:#d3e8dd;color:#134a2c;font-weight:700}

/* the status bar */
.xl-status{
  display:flex;align-items:center;gap:.8rem;
  padding:.32rem .7rem;background:#f1f3f5;border-top:1px solid #d8dbe0;
  font-family:var(--mono);font-size:.58rem;color:#4a5058;
}
.xl-state{font-weight:600;min-width:6.5rem}
.xl-state.is-busy{color:#217346}
.xl-stats{
  margin-left:auto;font-variant-numeric:tabular-nums;color:#3b414a;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.xl-zoom{padding-left:.6rem;border-left:1px solid #d8dbe0;color:#6b727c}

/* the formula bar gets a caret while it retypes */
.xl-formula-val.is-typing::after{
  content:"";display:inline-block;width:1px;height:.78em;
  margin-left:1px;background:#1f2329;vertical-align:-.08em;
  animation:xl-caret .9s steps(1,end) infinite;
}
@keyframes xl-caret{0%,49%{opacity:1}50%,100%{opacity:0}}

@media (max-width:560px){
  .xl-callout{max-width:11rem;font-size:.55rem}
  .xl-status{font-size:.53rem;gap:.5rem}
  .xl-state{min-width:5rem}
  .xl-zoom{display:none}
}
@media (prefers-reduced-motion:reduce){
  .xl-cursor{transition:opacity 120ms linear}
  .xl-callout{transition:none}
  .xl-formula-val.is-typing::after{animation:none}
}

/* =====================================================================
   21 — SAME CARD, SAME SHAPE, DIFFERENT ARRANGEMENT
   Two earlier attempts were wrong. The first replaced the cards with
   bespoke layouts; the card was never the problem. The second kept the
   card but let one span two columns per section, which changed its shape
   — a wide card next to narrow ones reads as a different component.

   So the card's proportions are now fixed everywhere: a three-column
   track, one card per column, no spans. Only position varies.

   This also fixes something that predates all of it. `.steps` is
   repeat(auto-fit,minmax(230px,1fr)), so a three-card band drew three
   columns and a four-card band drew four — the four-card section's cards
   were narrower than everyone else's. Pinning to three columns and
   wrapping the fourth makes every card on the page the same size.
   ===================================================================== */

/* every carded band runs on the same three-column track */
.steps.steps-stagger,
.steps.steps-3plus1,
.steps.steps-rise{grid-template-columns:repeat(3,1fr)}

/* ---- descending: the faults, in the order they are found ----------- */
.steps.steps-stagger > :nth-child(2){margin-top:2.2rem}
.steps.steps-stagger > :nth-child(3){margin-top:4.4rem}

/* ---- ascending: the mirror of it, so two staggers do not repeat ---- */
.steps.steps-rise > :nth-child(1){margin-top:4.4rem}
.steps.steps-rise > :nth-child(2){margin-top:2.2rem}

/* ---- three, then one indented under them -------------------------- */
.steps.steps-3plus1 > :nth-child(4){grid-column:2}

@media (max-width:900px){
  .steps.steps-stagger,
  .steps.steps-3plus1,
  .steps.steps-rise{grid-template-columns:1fr}
  .steps.steps-stagger > *,
  .steps.steps-rise > *{margin-top:0 !important}
  .steps.steps-3plus1 > :nth-child(4){grid-column:auto}
}

/* .basis carried a 1.1rem gap while .steps used 1rem, so the basis cards
   came out a pixel narrower than every other card. One track, one gap. */
.basis{gap:1rem}

/* =====================================================================
   ===================================================================
   v17 PASS — 2026-08-17
   Reference-led rebuild. Sources: Linear (Insights + Startups) and
   Apple Store, supplied by OT and read at full length.

   This block loads last inside the last stylesheet, so it is the final
   word on everything it touches. Nine changes, each traceable to a
   thing those two sites do that this site was doing the opposite of.
   ===================================================================
   ===================================================================== */

/* ---------------------------------------------------------------------
   1 — TYPE SYSTEM.  Archivo replaces Bricolage Grotesque AND Inter.

   Bricolage is a recognisable generated-site display face; Inter is
   banned by ~/CLAUDE.md as a sole text face. Both are gone.

   The references do not use a display face at all: Apple sets SF Pro and
   Linear sets Inter, one family from hero to footnote, and get their
   personality from scale, weight and value instead. Archivo follows that
   structure. It is a grotesque with tight apertures and genuinely good
   tabular figures, which matters on a site whose job is showing numbers.
   IBM Plex Mono stays as the utility face for eyebrows and basis tags.
   --------------------------------------------------------------------- */
:root{
  --display:'Archivo','Helvetica Neue',Helvetica,Arial,sans-serif;
  --sans:'Archivo','Helvetica Neue',Helvetica,Arial,sans-serif;
  --serif:'Archivo','Helvetica Neue',Helvetica,Arial,sans-serif;
  --mono:'IBM Plex Mono',ui-monospace,'SF Mono',Menlo,monospace;

  /* the quiet half of a two-tone headline. 6.45:1 on --bg, so it clears
     AA on its own without relying on its size. */
  --ink-quiet:#8f99ab;
  /* one flat ground. See 4. */
  --bg2:#0a1420;
}
body,p,li,td,th,input,textarea,select,button{
  font-family:var(--sans);
}
/* Archivo's figures are proportional by default. Every table, price,
   count-up and ledger on this site wants them lining up. */
.num,.price,.plan .price,#foundNum,.found-facts b,table,.tabsheet,
.stat,.stat b,.pnum,.counter-band h2{
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1;
}

/* ---------------------------------------------------------------------
   2 — SCALE.  Measured against the references rather than guessed.

   Linear sets its hero near 72px and its section heads near 57px over a
   17px body. This site was at 56.8 / 45.6 over 17, so display type was
   running roughly 25% small and headings competed with paragraphs.
   Tracking is retuned for Archivo, which sets narrower than Bricolage
   and can therefore take a tighter line without closing up.
   --------------------------------------------------------------------- */
.hero h1{
  font-size:clamp(2.65rem,4.9vw,4.35rem) !important;   /* -> 70px */
  line-height:1.02 !important;
  letter-spacing:-.032em !important;
  font-weight:700 !important;
}
h2{
  font-size:clamp(2rem,3.9vw,3.35rem) !important;      /* -> 54px */
  line-height:1.04 !important;
  letter-spacing:-.028em !important;
  font-weight:700 !important;
}
h3{
  font-size:clamp(1.08rem,1.55vw,1.32rem) !important;
  line-height:1.3 !important;
  letter-spacing:-.012em !important;
  font-weight:600 !important;
}

/* ---------------------------------------------------------------------
   3 — HEADLINE EMPHASIS.  ** REVERTED 2026-08-17, same day. **

   This block used to grey out all 48 <em> clauses site-wide on the theory
   that Apple and Linear carry emphasis with value rather than colour.

   That was wrong, and OT rejected it on sight. The reasoning was
   self-contradicting: the same analysis noted that Apple colours text
   heavily ("two-hour delivery" green, "Trade in your current device"
   purple) and that Linear gives each use-case eyebrow its own accent.
   Neither site is monochrome. Reading "they use value contrast" as
   "remove the brand colour" threw away the site's character and left the
   homepage flat.

   The brand green on the second clause is KEPT. Do not grey it out again.
   The original cascade owns this now:
     ti-dark-v13.css:38   h1 em,h2 em,h3 em -> --green
     ti-v16.css:1309      door pages        -> --accent
     ti-cards-v14.css:150 .cta-band h2 em   -> --green-bright

   Only the non-colour parts of the rule survive, because those were never
   the objection: an <em> should not be italic in a headline, and it should
   not change weight or tracking mid-sentence.
   --------------------------------------------------------------------- */
h1 em,h2 em,h3 em,
body[class*="door-"] h1 em,
body[class*="door-"] h2 em,
body[class*="door-"] h3 em,
body[class*="door-"] .h2row h2 em,
.cta-band h2 em,.gap h2 em,.readme h2 em{
  font-weight:inherit !important;
  font-style:normal !important;
  letter-spacing:inherit !important;
}

/* ---------------------------------------------------------------------
   4 — ONE FLAT GROUND, SECTIONS SEPARATED BY RULE.

   Linear runs flat near-black for its entire 9,790px with exactly one
   exception, a soft glow behind the final CTA. This site had six
   different section grounds plus a gradient hero plus a dot grid.

   Sections still need to be distinguishable, so the job the ground was
   doing passes to a hairline, which is how both references do it. The
   three-ground vocabulary in the README collapses to one ground and one
   rule; --bg2 is now equal to --bg so every legacy rule that referenced
   it goes flat without needing to be found and edited.
   --------------------------------------------------------------------- */
.sec-raised,.section.alt,.magnet,.faq-band,.counter-band,#found,
.trustband,.section.contrast,.tiv9-washed,.builton{
  background:var(--bg) !important;
}
main > section + section,
main > .pin-spacer + section{
  border-top:1px solid rgba(160,200,220,.10);
}
/* the hero's radial wash and dot grid, both removed */
.hero,.svc-hero{background:var(--bg) !important}
.hero::after{display:none !important}
/* the faint dot grid that marked a raised section */
.sec-raised::before,.section.alt::before{display:none !important}

/* ---------------------------------------------------------------------
   5 — PROOF RATIO.

   Linear sets a proof stat near 60px against an 11px caption, roughly
   5:1, so the number carries the section and the caption only qualifies
   it. Here the intended size was already written inline on the element
   as clamp(2.6rem,7vw,5.2rem) — but the generic h2 rule above carries
   !important, which beats a plain inline style, so it was rendering at
   2.85rem. The design was right and the cascade was eating it.
   --------------------------------------------------------------------- */
.counter-band h2,#found h2{
  font-size:clamp(3.2rem,8vw,6rem) !important;         /* -> 96px */
  line-height:.95 !important;
  letter-spacing:-.042em !important;
  font-weight:700 !important;
}
.counter-band .lede,#found .lede{
  font-size:.95rem !important;
  line-height:1.5 !important;
  color:var(--ink-quiet) !important;
  max-width:44ch !important;
}
.found-facts{
  margin-top:2.4rem !important;
  color:var(--muted) !important;
  font-size:.82rem !important;
}
.found-facts b{color:var(--ink) !important;font-weight:600 !important}

/* the hero's inline proof figure gets the same treatment, one step down */
.hero .proof-num,.hero .stat b{
  font-variant-numeric:tabular-nums;
  letter-spacing:-.03em;
}

/* ---------------------------------------------------------------------
   6 — TOOL LOGOS.  ** GREYSCALE REVERTED 2026-08-17, same day. **

   This block used to greyscale all nine logos, restoring colour only on
   hover, because Linear renders its logo wall flat white and Apple
   desaturates its integration tiles.

   Reverted with the rest of the colour strip-out. Those two do it because
   they are showing OFF-brand logos as social proof, where nine competing
   colours would fight the page. Here the strip is saying "this runs on the
   tools you already own", and Excel green, Xero cyan and Stripe purple are
   exactly how a business owner RECOGNISES those tools at a glance. The
   colour is carrying the meaning, so removing it removed the point.

   The logos are full colour again. The only thing kept is the label
   colour, which was a measured contrast fix, not an aesthetic one:
   the span carried opacity:.85 which composited --muted to 4.04:1.
   See block 14.
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   7 — BODY COPY RECEDES.

   Both references set body text markedly quieter than their headings.
   Here the lede sat close to heading brightness, so the two competed and
   nothing led. Measure is capped near 60ch, which is where both
   references sit.
   --------------------------------------------------------------------- */
.hero .lede,.section .lede,.h2row p,.sec-intro,.lede{
  color:var(--ink-quiet) !important;
  font-size:1.0625rem !important;
  line-height:1.62 !important;
  max-width:56ch;
}

/* ---------------------------------------------------------------------
   8 — SECTION EYEBROW.

   Linear opens most sections with a small label and a hairline running
   the full width beneath it ("Why startups love Linear", "Linear Startup
   Program"). Apple does the same inside cards in tiny caps. This site had
   a short gold dash doing that job silently. The dash becomes a labelled
   rule wherever a section supplies .eyebrow text.
   --------------------------------------------------------------------- */
.section > .wrap > .eyebrow,
.section > .wrap > .lbl{
  display:block;
  font-family:var(--mono);
  font-size:.68rem;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--muted);
  padding-bottom:.9rem;
  margin-bottom:2.2rem;
  border-bottom:1px solid rgba(160,200,220,.10);
}

/* ---------------------------------------------------------------------
   9 — IMAGERY BREAKS ITS CONTAINER.

   Nothing on either reference is politely contained. Linear's product
   panels run off the right edge of the page; its feature cards crop their
   screenshots mid-table; Apple's carousels cut the fourth card in half and
   its product shots break the bottom edge of their cards.

   This site framed every artefact square-on inside browser chrome, which
   reads as "a screenshot of a thing" rather than the thing. The hero
   artwork now oversizes and runs past the right edge; card media crops.
   --------------------------------------------------------------------- */
@media (min-width:1000px){
  .hero .hero-art,.hero .scene,.hero figure{
    width:min(62vw,1020px) !important;
    max-width:none !important;
    margin-right:calc(-1 * clamp(2rem,9vw,10rem)) !important;
  }
}
.card-media,.step-media,.bcard-media{
  overflow:hidden;
  border-radius:var(--r-md);
}
.card-media > img,.step-media > img,.bcard-media > img{
  width:112%;
  max-width:none;
  display:block;
}

/* ---------------------------------------------------------------------
   10 — THE WORKED WALKTHROUGH.  #firstbuild on index.html.

   Built directly on the Linear "How teams use Insights" pattern, which was
   the strongest thing in either reference: a small label with a rule under
   it, the customer's own question set in quote marks, the situation in two
   sentences, then numbered steps carrying the real figures.

   Structure is doing work here, not decorating. The numbers are a genuine
   sequence, so they are an <ol> and they are numbered. The dotted rule down
   the left is the thread between them, which is the one thing that tells
   you these are four moves in one job rather than four separate claims.

   No cards. Both references separate this kind of content with rules and
   space alone, and a box around each step would turn a sequence into a
   set of tiles.
   --------------------------------------------------------------------- */
.walk{padding-block:clamp(4rem,8vw,7rem)}

.walk-eyebrow{
  display:block;
  font-family:var(--mono);
  font-size:.68rem;
  letter-spacing:.15em;
  text-transform:uppercase;
  color:var(--muted);
  padding-bottom:1rem;
  margin-bottom:clamp(2rem,4vw,3.4rem);
  border-bottom:1px solid rgba(160,200,220,.12);
}

/* Asymmetric on purpose: the question column is narrower than the work
   column, because the question is short and the work is the substance.
   An even 50/50 split would imply they carry equal weight. */
.walk-grid{
  display:grid;
  grid-template-columns:minmax(0,0.85fr) minmax(0,1.15fr);
  gap:clamp(2rem,5vw,4.5rem);
  align-items:start;
}
@media (max-width:900px){
  .walk-grid{grid-template-columns:1fr;gap:2.4rem}
}

/* The question is the owner's, so it is set as speech. It gets the two-tone
   treatment like every other heading on the site — the <em> rule above
   already colours the second clause, so nothing extra is needed here. */
.walk-q{
  font-size:clamp(1.7rem,2.9vw,2.5rem) !important;
  line-height:1.12 !important;
  letter-spacing:-.026em !important;
  margin:0 0 1.4rem;
  text-wrap:balance;
}
.walk-sit{
  color:var(--ink-quiet);
  font-size:1rem;
  line-height:1.62;
  max-width:44ch;
  margin:0;
}

/* ---- the numbered work ---- */
.walk-steps{
  list-style:none;
  margin:0;
  padding:0 0 0 2.6rem;
  position:relative;
  counter-reset:walkstep;
}
/* the thread. Sits behind the numerals and stops short at both ends so it
   reads as a connection between steps, not a border on the column. */
.walk-steps::before{
  content:"";
  position:absolute;
  left:.62rem;
  top:.7rem;
  bottom:.9rem;
  border-left:1px dotted rgba(160,200,220,.28);
}
.walk-steps li{
  counter-increment:walkstep;
  position:relative;
  padding-bottom:1.9rem;
}
.walk-steps li:last-child{padding-bottom:0}
.walk-steps li::before{
  content:counter(walkstep);
  position:absolute;
  left:-2.6rem;
  top:.08rem;
  width:1.3rem;
  height:1.3rem;
  display:grid;
  place-items:center;
  font-family:var(--mono);
  font-size:.7rem;
  color:var(--muted);
  background:var(--bg);   /* punches a hole in the dotted thread */
}
.walk-steps li p{
  margin:0;
  font-size:1rem;
  line-height:1.62;
  color:var(--ink-quiet);
  max-width:52ch;
}
/* the figure is the point of each step, so it takes the ink */
.walk-steps li p b{
  color:var(--ink);
  font-weight:600;
  font-variant-numeric:tabular-nums;
}
/* Basis tag. House rule: every pound figure says where it came from. */
.walk-steps .basis{
  display:inline-block;
  margin-top:.6rem;
  font-family:var(--mono);
  font-size:.58rem;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--muted);
  border:1px solid rgba(160,200,220,.18);
  border-radius:var(--r-xs);
  padding:.16rem .45rem;
}

/* The caveat is the most valuable sentence in the section and the one a
   sceptical reader is looking for, so it is set apart rather than tucked
   under. Quiet, but not hidden. */
.walk-caveat{
  margin:2.4rem 0 0;
  padding-top:1.6rem;
  border-top:1px solid rgba(160,200,220,.10);
  color:var(--muted);
  font-size:.9rem;
  line-height:1.58;
  max-width:44ch;
}

/* ---------------------------------------------------------------------
   11 — LEFTOVERS FROM THE FIRST PASS, caught on screenshot review.
   --------------------------------------------------------------------- */

/* The hero's proof figure was set at body scale, so the single strongest
   number on the site was smaller than the paragraph above it.

   ** COLOUR REVERTED 2026-08-17 ** — the size increase is kept, the colour
   change is not. £277,000 is brand green again. Making it white was part of
   the same strip-out OT rejected, and green on the site's biggest number is
   the clearest possible case of colour marking a specific claim, which is
   the thing Apple actually does. */
.hero .hero-proof{align-items:baseline}
.hero .hero-proof b,.hero .proof b,.hero .proofline b{
  font-size:clamp(1.9rem,3.2vw,2.6rem) !important;
  font-weight:700 !important;
  letter-spacing:-.03em !important;
  line-height:1 !important;
  font-variant-numeric:tabular-nums;
}
.hero .hero-proof span,.hero .proof small,.hero .proofline small{
  color:var(--muted) !important;
  font-size:.8rem !important;
  line-height:1.45 !important;
}

/* The scene/gap slab kept its own ground and a teal wash. Both go: the
   page runs one field from header to footer now. */
.scene,.scene.build,.gap,#gap,.pin-spacer,
.section.contrast,.tiv9-washed,.trustband{
  background:var(--bg) !important;
}
.scene::before,.scene::after,.gap::before,.gap::after{display:none !important}

/* Feature cards lose the box. Both references separate this kind of grid
   with rule and space; a border plus a radius plus a tinted fill on every
   tile is what makes a grid read as a component library rather than a page.
   The icon keeps its accent, because there it marks a specific door. */
.doors .step,.doors .bcard,.four-doors .step{
  background:transparent !important;
  border:0 !important;
  border-top:1px solid rgba(160,200,220,.12) !important;
  border-radius:0 !important;
  box-shadow:none !important;
  padding-inline:0 !important;
  padding-top:1.5rem !important;
}

/* ---------------------------------------------------------------------
   12 — THE HERO ARTEFACT BREAKS THE EDGE.

   Nothing on either reference is politely contained. Linear's product
   panels cascade off the right edge of the page; Apple's card carousels
   cut the fourth card in half; both crop screenshots mid-table so you
   read the shape of a real tool rather than a tidy picture of one.

   This hero framed the dashboard square-on inside browser chrome at
   min(620px,41vw), fully visible, fully legible, which is exactly what
   makes it read as "a screenshot" instead of "the product". It now runs
   larger and past the right edge of the viewport, so it is obviously a
   window onto something that continues.

   The frame keeps its radius on the left edge only. A rounded corner on
   the side that leaves the screen is what gives the game away.
   --------------------------------------------------------------------- */
@media (min-width:1241px){
  .hero-demo{
    right:auto !important;
    left:52% !important;
    width:min(880px,54vw) !important;
    transform:translateY(-52%) !important;
  }
  .hero-demo .hd-frame{
    border-radius:var(--r-md) 0 0 var(--r-md) !important;
    border-right:0 !important;
  }
  /* the hero must not clip it, and the page must not gain a scrollbar */
  .hero{overflow:hidden}
}

/* ---------------------------------------------------------------------
   13 — DEMO LABELLING SURVIVES THE CROP.

   House rule: demo data is labelled as demo data on every mock. The crop
   above moves the frame's right edge off screen, and the "sample company"
   caption sat in the footer bar at that end. It is pulled left so it
   stays on screen at every width the crop applies to.
   --------------------------------------------------------------------- */
@media (min-width:1241px){
  .hero-demo .hd-foot{
    justify-content:flex-start !important;
    gap:1.2rem;
    padding-left:1rem;
  }
}

/* ---------------------------------------------------------------------
   14 — CONTRAST DEFECTS FOUND BY AXE AFTER THE REPASS.

   Both were real and both were permanent resting states, not the
   mid-reveal false positives the README warns about. Verified by reading
   computed opacity after a full scroll and a 2.5s settle.

   (a) .builton labels. The span carries opacity:.85, which composites
       --muted #78899c down to an effective #687789 on the page ground:
       4.04:1, under the 4.5 minimum. Opacity on a text element is a trap
       for exactly this reason — the colour you set is never the colour
       that ships. The recession is done with the colour itself instead.

   (b) .lstage-card on pipeline.html carries a permanent opacity:.45 to
       mark a stage as inactive. That drags the turquoise .step label to
       an effective #1d655b, 2.56:1, and the .fee line to 2.7:1. Fading a
       whole card is the wrong tool: it takes the text down with it. The
       card keeps its recession through ground and border, and its text
       stays readable.
   --------------------------------------------------------------------- */
.builton .row span{
  opacity:1 !important;
  color:var(--muted,#78899c) !important;    /* 4.55:1 on --bg, measured */
}

.lstage-card{
  opacity:1 !important;
  background:rgba(255,255,255,.018) !important;
  border-color:rgba(160,200,220,.09) !important;
}
/* The green is BACK on the stage label. The defect was never the colour,
   it was the .45 opacity underneath it: #35c9a4 faded to an effective
   #1d655b, 2.56:1. At full opacity the same green measures 7.4:1 on this
   ground, so the fix and the colour are not in conflict. Keep opacity:1. */
.lstage-card .step{
  color:var(--green,#35c9a4) !important;
  opacity:1 !important;
}
.lstage-card .fee{
  color:var(--muted,#78899c) !important;
  opacity:1 !important;
}
/* the active stage is what the accent is for, and at full opacity the
   turquoise clears AA on its own */
[data-stage].is-active .lstage-card,
[data-stage][data-active] .lstage-card{
  background:rgba(53,201,164,.05) !important;
  border-color:rgba(53,201,164,.22) !important;
}
[data-stage].is-active .lstage-card .step,
[data-stage][data-active] .lstage-card .step{
  color:var(--green,#35c9a4) !important;
}

/* ---------------------------------------------------------------------
   15 — THE LAST BACKGROUND ANIMATION.

   canvas.tiv9-live is a full-page animated wave field, 1440 x 3214 on
   pipeline. The README records that every per-SECTION background effect
   was tried and deleted for making the page read as generated; this one
   survived because it is page-level rather than section-level, so it was
   never in scope for that cull.

   It is the same problem at a larger size, and it is the "background
   stuff" the reference sites conspicuously do not have: Linear runs one
   flat field for its entire length, and Apple runs flat #F5F5F7. Hidden
   rather than deleted, so the canvas and its script stay intact and this
   is one line to reverse if it is wanted back.
   --------------------------------------------------------------------- */
canvas.tiv9-live{display:none !important}

/* =====================================================================
   ===================================================================
   v18 — OT's homepage brief, 2026-08-17
   Every block below is one thing he asked for, in the order he said it.
   ===================================================================
   ===================================================================== */

/* ---------------------------------------------------------------------
   A — THE MONO LABEL FONT GOES.

   OT screenshotted the hero pills (FIXED PRICES, AGREED UP FRONT / OWNED
   BY YOU / NO LOCK-IN / READ-ONLY CONNECTIONS) and asked for that face to
   be removed everywhere it appears, replaced with something more premium.

   It was IBM Plex Mono, and he is right about it. A monospace on a UI
   label reads "terminal", "developer tool", "config file". Neither
   reference site uses one for chrome: Apple's card eyebrows (EDUCATION,
   BUSINESS, IPHONE 14) are SF Pro uppercase with tracking, and Linear's
   section labels are its regular sans. The premium move is not another
   font, it is fewer of them.

   So --mono now resolves to Archivo, and every label picks that up in one
   move, including the ones declared inline in index.html.

   Plex Mono is NOT deleted. It is re-applied below to the artefacts where
   monospace carries meaning rather than styling: the Excel window, the
   workflow frames, the dashboard chrome. In those, characters lining up
   in a column is the point.
   --------------------------------------------------------------------- */
:root{
  --mono:'Archivo','Helvetica Neue',Helvetica,Arial,sans-serif;
  --data-mono:'IBM Plex Mono',ui-monospace,'SF Mono',Menlo,monospace;
}

/* Labels now read as small caps rather than code: a touch heavier, a
   little tighter than mono tracking, and no longer full-width glyphs. */
.eyebrow,.builton .lbl,.bcard .tag,.rcard .r-eyebrow,.rcard .r-tag,
.door .num,.step i,.rstep i,.snote .basis-chip,.legend span,
.svc-meta span,.cta-band .sub,.pnum em,.tools > i,.wf-facts span,
.nd i,.src.plain,.fm-out b,.hero .trust span,.mg-label,
.hm-chrome span,.hd-foot span,.hd-foot a,.dash-chrome span,
.scroll-cue span,.found-facts span,.station .lbl,.ba-tag,
.p-step,.pillars .p-step,.walk-eyebrow,.walk-steps .basis,
.section > .wrap > .eyebrow,.section > .wrap > .lbl{
  font-family:var(--sans) !important;
  font-weight:600 !important;
  letter-spacing:.085em !important;
  font-feature-settings:"tnum" 1;
}

/* ---- where monospace still means something ---- */
.sheet-bar em,.sheet-bar span,table.sheet,.tabstrip span,
.xl-namebox,.xl-fx,.xl-formula,.xl-sheet,.xl-grid,.xl-status,
.xl-rn,.xl-num,.xl-txt,.xl-count,
.wf-frame-bar em,.wf-frame-bar span,.wf-state,
.dash-kpis span,.dash-kpis s,.dash-charts .dc-lbl{
  font-family:var(--data-mono) !important;
  font-weight:400 !important;
  letter-spacing:.02em !important;
}

/* The hero pills carried mono's wide tracking to stop it looking cramped.
   Archivo does not need it, and the pills tighten up and read as chips
   rather than as printout. */
.hero .trust span{
  font-size:.7rem !important;
  letter-spacing:.09em !important;
  padding:.42rem .8rem !important;
}

/* ---------------------------------------------------------------------
   B — THE FOUR DOOR CARDS LINE UP.

   OT: the faint rule under the app icons sits higher on the Dashboards
   card than on the other three, and that card looks cut.

   Diagnosed rather than nudged. `.door .from` (ti-v12.css:68) carries
   `margin-top:auto`, `flex-wrap:wrap` and `border-top`. On three cards the
   price and the link fit on one line. On Dashboards the price is
   "£1,500 + £250/mo", which is too wide beside "The dashboard ->", so it
   wrapped to a second row. Because the foot is bottom-pinned, growing it
   taller pushed its own border-top UP by a row. That rule IS the faint line
   he means, and one wrapped card is the entire reason it sat higher.

   Two fixes, both structural:
     1. the foot never wraps, so it is always exactly one row tall
     2. the paragraph absorbs the difference in sentence length, so the icon
        row and the rule sit at the same height on all four regardless of
        whether the copy runs to four lines or five
   --------------------------------------------------------------------- */
.doors{align-items:stretch}
.door{height:100%}

/* the paragraph takes the slack. Everything below it is then bottom-locked
   and therefore level across the row. */
.door > p{flex:1 1 auto}

/* icon row sits directly above the rule at a fixed distance, not at
   whatever height the paragraph happened to end. */
.door .with{
  margin-top:1.15rem !important;
  margin-bottom:0 !important;
  min-height:1.35rem;
  align-items:center;
}

/* one row, always. This is the fix. */
.door .from{
  flex-wrap:nowrap !important;
  row-gap:0 !important;
  padding-top:1rem !important;
  min-height:2.4rem;
  align-items:baseline;
}
.door .from b{
  white-space:nowrap;
  font-size:.92rem !important;   /* was .96 — buys the Dashboards card its line */
}
.door .go{white-space:nowrap}

/* The Dashboards price is the long one. Splitting the retainer onto its own
   quieter mark keeps the whole thing on one line and reads better than a
   single run-on figure. */
.door .from b .mo{
  font-weight:500;
  font-size:.82em;
  color:var(--muted);
  letter-spacing:0;
}

/* ---------------------------------------------------------------------
   C — THE TOOL STACK: BIGGER, FULL WIDTH, AND MOVING.

   OT wants the logos larger, filling the row so the band has two clear
   lines of space above and below, and drifting slowly side to side on a
   loop.

   Built as a true marquee rather than a keyframed nudge: the track holds
   the ten tools TWICE and translates exactly -50%, so the second copy is
   under the cursor the moment the first leaves. That is what makes a
   marquee seamless — animating one copy back and forth visibly reverses.
   The duplicate is aria-hidden so a screen reader hears the list once.

   It runs full-bleed, edge to edge, and fades out at both ends so items
   arrive and leave rather than being clipped by the viewport.

   Motion: 64s for a full cycle, which is slow enough to read as drift.
   It pauses on hover, and stops entirely under prefers-reduced-motion —
   where the track resets to a centred wrap so nothing is unreachable.
   --------------------------------------------------------------------- */
.builton{
  padding-block:clamp(3rem,5.5vw,4.5rem) !important;   /* the air above/below */
  overflow:hidden;
}
.builton .wrap{max-width:none !important;padding-inline:0 !important}

.builton .lbl{
  margin-bottom:2.2rem !important;
  font-size:.7rem !important;
  opacity:.85;
}

.builton .row{
  display:flex !important;
  flex-wrap:nowrap !important;
  justify-content:flex-start !important;
  align-items:center;
  gap:0 !important;
  width:max-content;
  animation:ti-marquee 64s linear infinite;
  will-change:transform;
}
/* No hover pause. OT: "when a mouse hover is over it the whole thing pauses,
   get rid of that so when you're just looking at it it works." He is right —
   the band sits mid-page, so a cursor resting anywhere near it froze the one
   thing on the page that is supposed to be moving, which reads as a bug. */

.builton .row span{
  flex:0 0 auto;
  gap:.7rem !important;
  padding-inline:clamp(1.6rem,3vw,3rem);
  font-size:1.12rem !important;      /* was .86rem */
  font-weight:500;
  letter-spacing:-.01em;
}
.builton .row img{
  width:34px !important;             /* was 19px */
  height:34px !important;
}

/* the ends fade instead of cutting */
.builton{
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%);
}

@keyframes ti-marquee{
  from{transform:translate3d(0,0,0)}
  to{transform:translate3d(-50%,0,0)}
}

@media (prefers-reduced-motion:reduce){
  .builton .row{
    animation:none;
    flex-wrap:wrap !important;
    justify-content:center !important;
    width:auto;
    gap:1rem 2rem !important;
  }
  .builton .row [aria-hidden="true"]{display:none}
  .builton{-webkit-mask-image:none;mask-image:none}
}
@media (max-width:700px){
  .builton .row span{font-size:.98rem !important;padding-inline:1.2rem}
  .builton .row img{width:28px !important;height:28px !important}
}

/* ---------------------------------------------------------------------
   D — THE FIRST BUILD: BIGGER STEP NUMBERS.
   OT likes this section and wants 1-4 on the right reading larger.
   --------------------------------------------------------------------- */
.walk-steps{padding-left:3.2rem}
.walk-steps::before{left:.95rem}
.walk-steps li::before{
  left:-3.2rem;
  top:-.06rem;
  width:1.9rem;
  height:1.9rem;
  font-size:1.02rem;          /* was .7rem */
  font-weight:600;
  color:var(--ink);
  letter-spacing:-.02em;
}
@media (max-width:900px){
  .walk-steps{padding-left:2.6rem}
  .walk-steps::before{left:.75rem}
  .walk-steps li::before{left:-2.6rem;width:1.6rem;height:1.6rem;font-size:.92rem}
}

/* ---------------------------------------------------------------------
   E — "WHAT MONDAY LOOKS LIKE": THE STANDFIRST AND THE TAGS.

   OT: the text under the heading is compressed and the rag is ugly.

   Cause: the v17 pass capped every .lede at 56ch. That is a sensible
   measure for a left-aligned column, but this section is centred, so a
   narrow measure forces three short lines whose ragged ends read as an
   accident rather than a shape. Widened here, and the browser is asked to
   even the lines out rather than filling greedily and leaving a stub.
   --------------------------------------------------------------------- */
.contrast .ba-head .lede,.ba-head .lede{
  max-width:72ch !important;
  margin-inline:auto;
  text-wrap:balance;
  font-size:1.08rem !important;
  line-height:1.6 !important;
}

/* The before/after tags were bare uppercase text floating above a heading.
   They now read as what they are: a state label on a panel. Same shape both
   sides, colour doing the work — warm on the manual one, brand on the
   built one — so the pair is legible at a glance without reading a word. */
.ba-tag{
  display:inline-flex !important;
  align-items:center;
  gap:.5rem;
  padding:.34rem .7rem .34rem .6rem !important;
  border-radius:var(--r-pill);
  font-size:.64rem !important;
  font-weight:600 !important;
  letter-spacing:.11em !important;
  text-transform:uppercase;
  border:1px solid currentColor;
  background:color-mix(in srgb, currentColor 10%, transparent);
  margin-bottom:1rem !important;
}
/* the dot: a state light, not decoration */
.ba-tag::before{
  content:"";
  width:.42rem;height:.42rem;
  border-radius:50%;
  background:currentColor;
  flex:0 0 auto;
}

/* ---------------------------------------------------------------------
   F — A LITTLE AMBIENT DEPTH.

   OT: with the wave canvas gone the page reads very plain, and he would
   like something subtle back.

   Deliberately NOT the thing that was removed. No canvas, no per-section
   effect, no moving lines behind text. This is one fixed, very low
   contrast glow that sits behind everything and never moves: a faint lift
   at the top of the page where the hero is, and a second at the very
   bottom behind the closing CTA, which is exactly what Linear does on its
   own final band and the only place either reference lets a background
   speak.

   Fixed attachment means it does not animate on scroll, costs nothing, and
   cannot be caught mid-transition by an accessibility audit.
   --------------------------------------------------------------------- */
/* Superseded 2026-08-19 by block BI at the end of this file, which keeps
   these two gradients, tints them from the page's own accent, and gives
   them a very slow drift. Left here so the history reads straight. */
body::before{
  content:"";
  position:fixed;
  inset:0;
  z-index:0;
  pointer-events:none;
}
main,.site-head,footer{position:relative;z-index:1}

/* ---------------------------------------------------------------------
   G — THE FOUR PLAN CARDS: READABLE WITHOUT HOVERING.

   OT: you have to hover each card to read it, the text under the rule
   cannot be read at all, and the rules sit at different heights. He likes
   the hover colour change, so that stays.

   Three separate faults:

   1. .pillar is in the reveal-card list in ti-v16.js, so its <p> is hidden
      behind a control until hover. On the four cards that explain what you
      are buying, that is the wrong trade: the README already makes exactly
      this exception for .plan ("what a price includes must be readable
      without asking"). These are the same thing, so the same rule applies.
      Handled in JS below, not by fighting the CSS.

   2. .p-fee ("Included in the build", "Fixed fee, quoted up front") sat on
      --muted at a small size under a rule and was effectively invisible.

   3. The rule above .p-fee sat wherever the bullet list ended, and the
      lists are 3, 3, 3 and 3 items but of different heights, so it landed
      at four different heights. The list now absorbs the slack.
   --------------------------------------------------------------------- */
.pillars{align-items:stretch}
.pillar{display:flex;flex-direction:column;height:100%}
.pillar ul{flex:1 1 auto;margin-bottom:1.4rem}
.pillar .p-fee{
  margin-top:auto !important;
  padding-top:1rem !important;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.10));
  color:var(--ink-quiet,#8f99ab) !important;
  font-size:.82rem !important;
  letter-spacing:.01em !important;
  opacity:1 !important;
  min-height:2.5rem;
  display:flex;
  align-items:center;
}
/* the description is content, not a reward for hovering */
.pillar > p{
  display:block !important;
  opacity:1 !important;
  max-height:none !important;
  visibility:visible !important;
  overflow:visible !important;
  margin:.2rem 0 1.1rem !important;
  color:var(--ink-quiet,#8f99ab);
  font-size:.96rem;
  line-height:1.58;
}
/* the reveal control has nothing left to reveal */
.pillar .r-more,.pillar .card-toggle,.pillar > button.rv-btn{display:none !important}

/* The "+" affordance (ti-v16.css:1138) promises there is more behind the
   card. On .pillar there no longer is, so it would be lying. The hover
   tint at :1049 stays, because that is the colour change OT likes and it
   signals "this one" rather than "there is hidden content here". */
.pillar::after{display:none !important}

/* The gold before/after tag inherited #8a6a1f, a darker gold than the brand
   one, which measured 2.98:1 on the dark card once the new tag styling gave
   it a tinted ground. The site's own --gold #c9a25c is 6.31:1 on the same
   ground, so this is a correction to the brand value rather than a new
   colour invented to pass a check. */
.ba-tag.ba-tag-gold,.ba-card .ba-tag-gold{
  color:var(--gold,#c9a25c) !important;
}

/* ---------------------------------------------------------------------
   H — DASHBOARD MOCK: BREATHING ROOM UNDER THE KPI ROW.

   The 9px collision itself was a stuck GSAP transform, fixed in
   index.html. This is the separate half of OT's note: even sitting
   correctly, 0.7rem between the KPI row and the panels below is tight for
   the piece he calls the showstopper. The row that carries the four
   headline figures now gets clear air under it.
   --------------------------------------------------------------------- */
.mock .kpis{margin-bottom:1.15rem !important}
.mock .grid2{margin-top:0 !important}

/* ---------------------------------------------------------------------
   I — THE FOUR PLAN CARDS: VISIBLE NUMBERS, DISTINCT MARKS.

   OT: the numbers should be readable without hovering or clicking, and
   Discovery, The Retainer and The Systems were all wearing the same icon.

   Both were consequences of pulling .pillar out of the reveal-card system
   last pass: that JS was what drew the numeral and the mark, and its
   keyword-guessed icon fell back to the same glyph three times out of
   four. Rebuilt in markup instead, so what is on the card is what is in
   the HTML, and each of the four names its own mark.

   The numerals are a real sequence — discovery precedes the build, the
   build precedes the retainer — so numbering them encodes something true
   rather than decorating. They sit at rest, and deepen on hover with the
   card tint he already likes.
   --------------------------------------------------------------------- */
.pillar{position:relative;padding-top:3.4rem !important}

.pillar .p-num{
  position:absolute;
  top:1.25rem;
  left:1.25rem;
  font-size:1.05rem;
  font-weight:700;
  letter-spacing:-.02em;
  color:var(--ink-quiet,#8f99ab);
  font-variant-numeric:tabular-nums;
  transition:color var(--t-base) var(--ease);
}
.pillar:hover .p-num{color:var(--green,#35c9a4)}

.pillar .p-mark{
  position:absolute;
  top:1.1rem;
  right:1.25rem;
  width:1.45rem;
  height:1.45rem;
  color:var(--ink-quiet,#8f99ab);
  opacity:.75;
  transition:color var(--t-base) var(--ease),opacity var(--t-base) var(--ease);
}
.pillar .p-mark svg{width:100%;height:100%;display:block;fill:none;
  stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.pillar:hover .p-mark{color:var(--green,#35c9a4);opacity:1}

/* the eyebrow moves down to clear the numeral */
.pillar .p-step{display:block;margin-bottom:.35rem}

@media (prefers-reduced-motion:reduce){
  .pillar .p-num,.pillar .p-mark{transition:none}
}

/* The hero text column was capped at 560px to clear artwork that used to
   start around 52vw. The artwork now begins at left:52% and bleeds right,
   so the column can take more width, which keeps the headline to three
   lines instead of five. Measured against the artwork's actual left edge,
   not guessed. */
@media (min-width:1000px){
  .hero .hero-inner{max-width:min(46%,640px) !important}
}

/* ---------------------------------------------------------------------
   J — THE MERGED ICONS AND THE DOUBLED NUMERAL.  OT's screenshot.

   The four plan cards were carrying THREE numbering/marking systems at
   once, which is what made them look smeared:

     1. ti-v15.css:225  .pillars .pillar::before — a 3.5rem outlined ghost
        numeral, filling solid green on hover. Real design, deliberate,
        and the thing OT says he likes ("subtle numbers at the top, bigger
        when you hover").
     2. ti-v15.css      .pillar::after — an 11px gradient mark, top right.
     3. the .p-num and .p-mark I added last pass, in the same two corners.

   1 and 3 drew a numeral on top of a numeral; 2 and 3 drew a mark on top
   of a mark. Resolved by keeping ONE of each and deleting the rest:

   - the ghost numeral STAYS. It is the better of the two — bigger, more
     distinctive, and already has the hover behaviour he described.
   - my .p-num goes. It was the duplicate.
   - the legacy 11px ::after mark goes. My .p-mark stays, because that is
     the one that gives each card its own glyph, which was the whole point
     of adding it.
   --------------------------------------------------------------------- */
/* ONE element per job. ::before goes back to being only the hover tint it
   is in ti-v16.css:1048, and .p-num is the only thing that draws a numeral.

   The conflict was subtle and worth writing down: v15 put the numeral in
   ::before with `content: counter(pil) !important`, and v16 later reused the
   SAME pseudo as a full-bleed tint layer at opacity:0. v15's !important won
   the content, but it never set opacity, so v16's opacity:0 won that — and
   the numeral was being painted invisibly. It surfaced only on hover, at the
   tint's .07, which is the faint ghost in OT's screenshot and why it looked
   like the number "appeared and got bigger". */
.pillars .pillar::before{content:"" !important}
.pillars .pillar::after{display:none !important}

/* The numeral takes v15's treatment, which was the good idea trapped behind
   that bug: a large outlined figure at rest that fills solid on hover. Now it
   is on a real element, so nothing can switch it off. */
.pillar .p-num{
  display:block !important;
  position:static !important;
  font-size:3.2rem !important;
  font-weight:800 !important;
  line-height:.9 !important;
  letter-spacing:-.04em !important;
  color:transparent !important;
  -webkit-text-stroke:1.4px rgba(53,201,164,.45);
  margin:0 0 1rem !important;
  transition:color var(--t-base) var(--ease),
             -webkit-text-stroke-color var(--t-base) var(--ease);
}
.pillar:hover .p-num{
  color:rgba(53,201,164,.95) !important;
  -webkit-text-stroke-color:transparent;
}
/* Safari and Firefox both support -webkit-text-stroke, but if it ever fails
   the numeral would be transparent and therefore missing. This keeps it
   readable in that case rather than invisible. */
@supports not (-webkit-text-stroke: 1px red){
  .pillar .p-num{color:rgba(53,201,164,.55) !important}
}

/* the numeral is in flow at the top now, so the padding that was clearing an
   absolutely-positioned one is gone */
.pillar{padding-top:1.6rem !important}

/* the mark sits on the numeral's line rather than the card edge */
.pillar .p-mark{top:1.85rem;right:1.35rem}

@media (prefers-reduced-motion:reduce){
  .pillar .p-num{transition:none}
}

/* ---------------------------------------------------------------------
   K — THE GOLD RAIL ON "WITH THE SYSTEM".

   ti-v9-polish.css:476 draws a 3px gold bar across the top of .ba-after,
   and ti-v12.css:459 sets a 3px gold border-top as well. It was there to
   mark that card as the good one, but the two cards now carry state tags
   that say which is which, so the rail is a second answer to a question
   already answered — and it made the pair look mismatched.
   --------------------------------------------------------------------- */
.ba-after{border-top:0 !important}
.ba-after::before{display:none !important}

/* The ACTION chip in the Monday briefing panel was inheriting the deep
   green #1d5f4a, which is 2.12:1 on that panel's #10242e ground — a real
   failure at bold 10px, not a large-text exemption. The brand --green is
   7.64:1 on the same ground, so this is a correction to the brand value
   rather than a colour invented to satisfy a checker. */
.ba-after .sev,.ba-card .sev,.sev.action{
  color:var(--green,#35c9a4) !important;
}

/* ---------------------------------------------------------------------
   L — THE DOUBLED ICON.  Not a markup bug: a stale cache.

   OT saw a small sparkle sitting on top of each card's mark. It could not
   be reproduced headlessly, and a 3x zoom of the built page showed exactly
   one icon, because a fresh browser has no cache.

   Cause: every stylesheet and script on all eight pages was pinned at
   `?v=85`, and ti-v16.css and ti-v16.js were both edited many times
   without that number moving. His browser was therefore running the OLD
   ti-v16.js, which still had `.pillar` in the reveal-card selector list
   and was appending its own keyword-guessed `.s-ico` sparkle next to the
   new .p-mark. All eight pages are now on `?v=86`.

   The rule below is the belt to that braces. Even if a stale script ever
   runs again, .pillar will not show a second mark. Cheap insurance
   against a class of bug that is invisible in testing and only ever
   appears on someone else's machine.
   --------------------------------------------------------------------- */
.pillar .s-ico{display:none !important}

/* =====================================================================
   v21 — SPREADSHEETS PAGE, 2026-08-17
   ===================================================================== */

/* ---------------------------------------------------------------------
   M — "WHAT THE £300 ACTUALLY BUYS": ONE HORIZONTAL LINE.

   OT asked for the four boxes on one row rather than the three-plus-one
   arrangement, and asked whether that is the best way to show them.

   It is not, quite, and the reason is worth stating: these four are not
   four features, they are four STEPS in a fixed order. Send the file,
   I take it apart, you get the report, you decide. Four equal tiles say
   "pick one"; a track says "this happens, then this". So they run in one
   line as he asked, and a hairline runs through the numerals to say the
   order matters. Same card, same shape, only the arrangement changed —
   which is the rule the README already set for this page.

   The cards narrow to fit four across. That is fine here because each one
   is two short sentences; nothing is being squeezed.
   --------------------------------------------------------------------- */
.steps.track4{
  grid-template-columns:repeat(4,1fr) !important;
  gap:0 !important;
  position:relative;
  align-items:stretch;
}
.steps.track4 > *{
  margin-top:0 !important;
  background:transparent !important;
  border:0 !important;
  border-radius:0 !important;
  box-shadow:none !important;
  padding:0 1.6rem !important;
  cursor:default !important;
  display:flex;
  flex-direction:column;
}
.steps.track4 > *:first-child{padding-left:0 !important}
.steps.track4 > *:last-child{padding-right:0 !important}

/* these are no longer reveal cards, so nothing here should suggest they
   open: no plus, no icon competing with the numeral, no hover tint */
.steps.track4 > *::after,
.steps.track4 > *::before,
.steps.track4 .s-ico{display:none !important}

/* the numeral sits in flow at the top of each step. The reveal script used
   to rebuild <i> into .s-num + .s-lbl; with the script skipping this row the
   <i> is plain, so both shapes are handled. */
.steps.track4 > * > i{
  display:block;
  font-style:normal;
  font-size:.82rem;
  font-weight:600;
  letter-spacing:.06em;
  color:var(--accent,#c9a25c);
  margin-bottom:1.1rem;
  padding-bottom:1.1rem;
  border-bottom:1px solid var(--line-soft,rgba(160,200,220,.14));
}
.steps.track4 > * > i .s-num{font-size:inherit;font-weight:inherit;color:inherit}
.steps.track4 > * > i .s-lbl{display:none}

.steps.track4 > * > b{
  display:block;
  font-size:1.02rem;
  font-weight:600;
  color:var(--ink);
  margin-bottom:.5rem;
}
/* the description is the point of the row and is always visible */
.steps.track4 > * > p{
  display:block !important;
  opacity:1 !important;
  max-height:none !important;
  visibility:visible !important;
  font-size:.92rem;
  line-height:1.56;
  color:var(--ink-quiet,#8f99ab);
  margin:0;
}
.steps.track4 > * > p .r-in{display:block}

/* vertical hairlines between steps, so the four read as one object rather
   than four tiles. The rule under each numeral continues the same line. */
.steps.track4 > * + *{
  border-left:1px solid var(--line-soft,rgba(160,200,220,.10)) !important;
}

@media (max-width:1000px){
  .steps.track4{grid-template-columns:repeat(2,1fr) !important;row-gap:2.2rem !important}
  .steps.track4 > *{padding-inline:1.4rem !important}
  .steps.track4 > *:nth-child(odd){padding-left:0 !important}
  .steps.track4 > *:nth-child(3){border-left:0 !important}
}
@media (max-width:620px){
  .steps.track4{grid-template-columns:1fr !important;row-gap:1.9rem !important}
  .steps.track4 > *{padding-inline:0 !important;border-left:0 !important}
}

/* ---------------------------------------------------------------------
   N — THE EXAMPLE AUDIT.

   Asymmetric on purpose: the argument is short and the artefact is the
   point, so the document gets the larger column. It is the same move the
   homepage hero makes with the live demo, and for the same reason — the
   thing itself is more persuasive than a description of it.

   The document leans and is cropped at the bottom rather than shown
   whole. A complete page shrunk to fit reads as a thumbnail; a cropped
   one reads as a real document you are looking at the top of.
   --------------------------------------------------------------------- */
.ex-grid{
  display:grid;
  grid-template-columns:minmax(0,0.92fr) minmax(0,1.08fr);
  gap:clamp(2rem,5vw,4.5rem);
  align-items:start;
}
@media (max-width:980px){
  .ex-grid{grid-template-columns:1fr;gap:2.6rem}
}

.ex-copy .lede{max-width:52ch}

.ex-facts{
  list-style:none;
  margin:1.8rem 0 0;
  padding:0;
  display:flex;
  flex-wrap:wrap;
  gap:0;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.10));
}
.ex-facts li{
  padding:.9rem 1.4rem .9rem 0;
  margin-right:1.4rem;
  font-size:.84rem;
  color:var(--muted);
  border-right:1px solid var(--line-soft,rgba(160,200,220,.10));
}
.ex-facts li:last-child{border-right:0;margin-right:0}
.ex-facts b{
  display:block;
  font-size:1.5rem;
  font-weight:700;
  color:var(--ink);
  letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;
  margin-bottom:.1rem;
}

.ex-actions{display:flex;flex-wrap:wrap;gap:.7rem;margin-top:1.9rem}
.ex-actions .btn.ghost{
  background:transparent;
  border:1px solid var(--line,rgba(160,200,220,.22));
  color:var(--ink);
  box-shadow:none;
}
.ex-actions .btn.ghost:hover{border-color:var(--green,#35c9a4);color:var(--green,#35c9a4)}

.ex-note{
  margin-top:1rem;
  font-size:.82rem;
  line-height:1.5;
  color:var(--muted);
  max-width:46ch;
}

/* ---- the document itself ---- */
.ex-doc{
  display:block;
  position:relative;
  border-radius:var(--r-md);
  overflow:hidden;
  border:1px solid var(--line,rgba(160,200,220,.16));
  box-shadow:var(--e-3);
  transform:perspective(1400px) rotateY(-3deg) rotateX(1deg);
  transform-origin:left center;
  transition:transform var(--t-slow) var(--ease), box-shadow var(--t-slow) var(--ease);
  /* cropped, not shrunk: you are looking at the top of a real document */
  max-height:min(70vh,620px);
}
.ex-doc:hover,.ex-doc:focus-visible{
  transform:perspective(1400px) rotateY(0deg) rotateX(0deg) translateY(-4px);
  box-shadow:0 34px 80px rgba(0,0,0,.5);
}
.ex-doc img{width:100%;height:auto;display:block}

/* the fade tells you it continues, and carries the affordance */
.ex-doc::after{
  content:"";
  position:absolute;
  left:0;right:0;bottom:0;
  height:38%;
  background:linear-gradient(180deg,transparent,rgba(10,20,32,.92));
  pointer-events:none;
}
.ex-open{
  position:absolute;
  left:50%;bottom:1.5rem;
  transform:translateX(-50%);
  z-index:1;
  font-size:.86rem;
  font-weight:600;
  color:var(--ink);
  background:rgba(10,20,32,.72);
  border:1px solid var(--line,rgba(160,200,220,.24));
  border-radius:var(--r-pill);
  padding:.55rem 1.05rem;
  -webkit-backdrop-filter:blur(8px);
  backdrop-filter:blur(8px);
  white-space:nowrap;
}
.ex-doc:hover .ex-open,.ex-doc:focus-visible .ex-open{
  border-color:var(--green,#35c9a4);
  color:var(--green,#35c9a4);
}

@media (prefers-reduced-motion:reduce){
  .ex-doc{transform:none;transition:none}
  .ex-doc:hover,.ex-doc:focus-visible{transform:none}
}
@media (max-width:980px){
  .ex-doc{transform:none;max-height:460px}
}

/* The example-audit actions were marked up as .btn, but .btn on its own is
   only a radius on this site — the filled treatment lives on .btn.solid
   (ti-dark-v13.css:51) or on specific selectors like .site-head .btn. So the
   two actions rendered as bare text links. Styled explicitly here rather
   than by borrowing a class whose meaning depends on where it sits. */
.ex-actions .btn{
  display:inline-flex;
  align-items:center;
  gap:.4rem;
  padding:.72rem 1.15rem;
  border-radius:var(--r-sm);
  font-size:.9rem;
  font-weight:600;
  line-height:1;
  background:var(--green,#35c9a4);
  color:#06121b;
  border:1px solid var(--green,#35c9a4);
  box-shadow:var(--e-glow);
  transition:background var(--t-base) var(--ease),
             border-color var(--t-base) var(--ease),
             color var(--t-base) var(--ease),
             transform var(--t-base) var(--ease);
}
.ex-actions .btn:hover{background:var(--green-bright,#5fe0bf);border-color:var(--green-bright,#5fe0bf)}
.ex-actions .btn.ghost{
  background:transparent;
  color:var(--ink);
  border-color:var(--line,rgba(160,200,220,.24));
  box-shadow:none;
}
.ex-actions .btn.ghost:hover{
  background:transparent;
  border-color:var(--green,#35c9a4);
  color:var(--green,#35c9a4);
}
@media (prefers-reduced-motion:reduce){ .ex-actions .btn{transition:none} }

/* =====================================================================
   v22 — SPREADSHEETS: THE PAGE WEARS EXCEL'S GREEN, 2026-08-17
   ===================================================================== */

/* ---------------------------------------------------------------------
   O — GOLD OUT, EXCEL GREEN IN.

   OT: the accent on this page should be the colour of the tool the page is
   about, exactly as the other pages do it. That is Excel green, not gold.

   The value is taken from the logo this site already ships,
   assets/logos/excel.svg, so it is the real one rather than a guess:
   #1d6f42.

   Measured, because ti-v16.css:573 originally moved this page OFF Excel
   green for a documented reason and that reason has not gone away:

     #1d6f42 on the page ground #0a1420 ...... 3.00:1
     #1d6f42 on the card ground #12212f ...... 2.65:1   <- fails AA text
     white on solid #1d6f42 .................. 6.17:1   <- passes

   So the exact green is used wherever it is a FILL with light content on
   top, where it passes comfortably and looks like Excel. For small text
   and hairlines sitting on the dark ground it would be muddy and would
   fail, so those take --accent-ink: the same hue and saturation lifted in
   lightness only, #2aa260, which measures 5.68:1 on the page and 5.01:1
   on the card. Same colour family, still unmistakably Excel, legible.

   This replaces gold on this page only. --gold is remapped as well as
   --accent because several rules on this page reach for gold directly
   rather than going through the accent token.
   --------------------------------------------------------------------- */
body.door-xl{
  --accent:#2aa260;          /* readable Excel green: marks, text, rules   */
  --accent-solid:#1d6f42;    /* the exact logo green: fills and surfaces   */
  --accent-ink:#2aa260;
  --gold:#2aa260;
  --gold-bright:#33c273;
}

/* fills, rails and anything carrying light content take the exact green */
body.door-xl .door.d-xl::before,
body.door-xl .sec-raised .eyebrow::before,
body.door-xl .steps.track4 > * > i::before,
body.door-xl .xl-mark,
body.door-xl .btn:not(.ghost){
  background:var(--accent-solid) !important;
}
body.door-xl .btn:not(.ghost){
  border-color:var(--accent-solid) !important;
  color:#ffffff !important;          /* 6.17:1 on the fill, measured */
  box-shadow:0 10px 30px rgba(29,111,66,.34) !important;
}
body.door-xl .btn:not(.ghost):hover{
  background:#258a52 !important;
  border-color:#258a52 !important;
}

/* the short accent dash that opens a section */
body.door-xl .head .eyebrow,
body.door-xl .eyebrow{color:var(--accent-ink) !important}

/* the Excel artefact keeps the true green, because in there it is not an
   accent at all — it is the application telling you which application it is */
body.door-xl .xl-title,
body.door-xl .xl-mark{background:var(--accent-solid) !important}

/* ---------------------------------------------------------------------
   P — THE CLEAN-RESULT LINE MOVES ONTO THIS PAGE.

   It was only on the homepage. It is the answer to the first question a
   careful buyer asks about a paid audit, so it belongs in the section
   that asks for the £300.
   --------------------------------------------------------------------- */
.buys-note{
  margin:2.4rem 0 0;
  padding:1.15rem 1.35rem;
  border:1px solid var(--line-soft,rgba(160,200,220,.14));
  border-left:3px solid var(--accent-solid,#1d6f42);
  border-radius:var(--r-sm);
  background:rgba(255,255,255,.02);
  color:var(--ink-quiet,#8f99ab);
  font-size:.95rem;
  line-height:1.6;
  max-width:70ch;
}
.buys-note b{color:var(--ink);font-weight:600}

/* The three example-audit stats were wrapping two-then-one, which reads as a
   layout accident rather than a row. Fixed to three equal cells so they hold
   the line at every width down to the mobile breakpoint. */
.ex-facts{display:grid;grid-template-columns:repeat(3,1fr);gap:0}
.ex-facts li{padding:.9rem 1rem .9rem 0;margin-right:0}
.ex-facts li + li{padding-left:1rem}
@media (max-width:520px){
  .ex-facts{grid-template-columns:1fr}
  .ex-facts li{border-right:0;border-bottom:1px solid var(--line-soft,rgba(160,200,220,.10))}
  .ex-facts li:last-child{border-bottom:0}
  .ex-facts li + li{padding-left:0}
}

/* ---------------------------------------------------------------------
   Q — "WHERE I WILL TELL YOU NOT TO BOTHER": VERDICT ROWS, NOT CARDS.

   OT: the explanation boxes look too similar across the site, and this
   section used the same card grid as "Three faults" two sections above it.
   He is right, and the fix is not a new card style — it is not being a
   card. A third grid on one page makes the page look like one component
   repeated at different widths.

   These three are things Oli would say out loud to a client, so they are
   set as speech: the quote takes the left column at display scale, the
   explanation runs beside it, and rows are separated by a rule. That gives
   the page a fourth arrangement without inventing a fourth component, and
   it reuses the quoted-question device already established on the homepage
   walkthrough — same vocabulary, different shape.
   --------------------------------------------------------------------- */
.verdicts{margin:0;padding:0}

.verdict{
  display:grid;
  grid-template-columns:minmax(0,0.82fr) minmax(0,1.18fr);
  gap:clamp(1.4rem,4vw,3.4rem);
  align-items:start;
  padding:1.7rem 0;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.12));
}
.verdict:last-child{border-bottom:1px solid var(--line-soft,rgba(160,200,220,.12))}

.verdict dt{
  margin:0;
  font-family:var(--display);
  font-size:clamp(1.15rem,1.9vw,1.5rem);
  font-weight:700;
  line-height:1.22;
  letter-spacing:-.02em;
  color:var(--ink);
  text-wrap:balance;
}
.verdict dd{
  margin:0;
  font-size:1rem;
  line-height:1.62;
  color:var(--ink-quiet,#8f99ab);
  max-width:56ch;
}

/* No ::first-letter accent. It picked up the T after the opening quote mark
   rather than the quote itself, so one arbitrary letter went green in the
   middle of a sentence. Decoration with no meaning attached is the thing this
   whole pass has been removing. */

@media (max-width:860px){
  .verdict{grid-template-columns:1fr;gap:.7rem;padding:1.4rem 0}
  .verdict dd{max-width:none}
}

/* =====================================================================
   v25 — AUTOMATIONS PAGE, 2026-08-17
   ===================================================================== */

/* ---------------------------------------------------------------------
   R — THE WORKFLOW CANVAS SCROLLED INTO EMPTY SPACE.

   OT screenshotted a scrollbar under both workflow players that slid the
   canvas sideways into nothing. It was not the diagram being too wide:
   every node sat within 1057px of a 1138px canvas.

   Cause found by measuring. ti-v12.css:380 puts a decorative shine on
   `.wf-canvas::after` — position:absolute, inset:0, so 1138px wide —
   animated to `translateX(120%)` with `animation-fill-mode: both`. `both`
   means it HOLDS the end state, so once the sweep has run the overlay
   parks itself permanently 1366px to the right of the canvas.

   1138 + 1366 = 2504, which is exactly the scrollWidth measured on both
   players. A one-off decorative shine was leaving a permanent, empty,
   scrollable region behind it.

   The canvas now clips. That removes the phantom scroll, and it also makes
   the sweep behave the way a shine on a panel should: contained by the
   panel it is shining on.
   --------------------------------------------------------------------- */
.wf-canvas{
  overflow:hidden !important;
}
/* The genuine small-screen scroll is unaffected: that lives on .wfp, which
   is INSIDE .wf-canvas and scrolls its own content. Restated here so a
   later edit does not "tidy" the two into one and break the phone layout. */
@media (max-width:700px){
  .wf-canvas{overflow:visible !important}
}

/* ---------------------------------------------------------------------
   S — THE AUTOMATIONS MENU SHOWS WHAT IT IS SELLING.

   "The ones owner-run businesses ask for most" is a menu: six things you
   might buy, meant to be scanned so you can point at the one that sounds
   like your week. Every description was hidden behind hover, which is the
   wrong trade for a menu — it makes a browsing reader work for the one
   piece of information that decides whether they care.

   The grid itself stays. A grid is the right shape for a menu of parallel
   options, unlike the sequence on the spreadsheets page or the verdicts,
   which were not parallel and should not have been grids.
   --------------------------------------------------------------------- */
.section-split .steps .step > p,
#build-menu .step > p{
  display:block !important;
  opacity:1 !important;
  max-height:none !important;
  visibility:visible !important;
}

/* ---------------------------------------------------------------------
   T — FAQ AS A REAL ACCORDION.

   Built on <details>/<summary>: keyboard operable, announces its own
   open/closed state to a screen reader, and works with JS off. The chevron
   is drawn rather than typed, so it rotates instead of swapping a "+" for
   a "-", and it is aria-hidden by virtue of being a pseudo-element.
   --------------------------------------------------------------------- */
.faq-list{
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
  max-width:78ch;
}
.faq-q{
  border-bottom:1px solid var(--line-soft,rgba(160,200,220,.14));
}
.faq-q > summary{
  list-style:none;
  cursor:pointer;
  display:flex;
  align-items:flex-start;
  gap:1rem;
  padding:1.15rem 0;
  font-size:1.03rem;
  font-weight:600;
  line-height:1.4;
  color:var(--ink);
  transition:color var(--t-fast) var(--ease);
}
.faq-q > summary::-webkit-details-marker{display:none}
.faq-q > summary:hover{color:var(--accent,#F4738F)}
.faq-q > summary:focus-visible{
  outline:none;
  box-shadow:var(--focus);
  border-radius:var(--r-xs);
}
/* the chevron */
.faq-q > summary::after{
  content:"";
  flex:0 0 auto;
  width:.62rem;height:.62rem;
  margin-left:auto;
  margin-top:.42rem;
  border-right:2px solid var(--accent,#F4738F);
  border-bottom:2px solid var(--accent,#F4738F);
  transform:rotate(45deg);
  transition:transform var(--t-base) var(--ease);
}
.faq-q[open] > summary::after{transform:rotate(-135deg)}
.faq-q[open] > summary{color:var(--accent,#F4738F)}

.faq-a{
  padding:0 2.4rem 1.35rem 0;
  color:var(--ink-quiet,#8f99ab);
  font-size:.98rem;
  line-height:1.65;
}
.faq-a p{margin:0 0 .75rem}
.faq-a p:last-child{margin-bottom:0}
.faq-a b{color:var(--ink);font-weight:600}

@media (prefers-reduced-motion:reduce){
  .faq-q > summary,.faq-q > summary::after{transition:none}
}

/* The root fix for R, rather than only clipping the symptom.

   The shine no longer moves the element at all. It is a wide gradient
   painted onto a stationary box, and the ANIMATION slides the background
   position across it. Nothing leaves the canvas, so there is no overflow to
   clip, no phantom scroll region, and no dependence on overflow:hidden to
   hide a mistake. Same effect on screen, none of the side effects. */
@media (prefers-reduced-motion:no-preference){
  .wf.reveal .wf-canvas::after{
    transform:none !important;
    background:linear-gradient(105deg,
      transparent 38%,
      rgba(240,237,228,.14) 50%,
      transparent 62%) !important;
    background-size:280% 100% !important;
    background-repeat:no-repeat !important;
    background-position:140% 0 !important;
    animation:none !important;
  }
  .wf.reveal.in .wf-canvas::after{
    animation:ti-sweep-bg 1.2s .35s cubic-bezier(.4,0,.2,1) both !important;
  }
}
@keyframes ti-sweep-bg{
  from{background-position:140% 0}
  to  {background-position:-40% 0}
}

/* ---------------------------------------------------------------------
   U — "REPLACED": WHAT THE WORKFLOW USED TO BE A PERSON DOING.

   The players showed a machine working and never said what it stood in
   for, so a diagram stayed a diagram. This puts the old job directly above
   the canvas, so the order you read it in is: here is what somebody used
   to do, now watch the thing that does it instead.

   Built from the label-then-value idiom already used in .wf-facts below
   the canvas, so it reads as the same page rather than a new component.
   The label is the accent because it is the one word doing the work.
   --------------------------------------------------------------------- */
.wf-was{
  margin:.9rem 0 0;
  padding-top:.85rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.12));
  font-size:.94rem;
  line-height:1.55;
  color:var(--muted);
  max-width:62ch;
}
.wf-was span{
  display:inline-block;
  margin-right:.6rem;
  font-size:.66rem;
  font-weight:600;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--accent,#F4738F);
  vertical-align:.08em;
}

/* ---------------------------------------------------------------------
   V — WHAT A FAILURE LOOKS LIKE.

   The page already carried the real numbers (4,720 runs, 106 failures) and
   the claim that a failsafe "shouts". They were in different places and
   neither proved the other. This section puts the count next to the thing
   the count produces.

   The alert is a fifth artefact type on this site — not a card, a window,
   a sheet or a player, but a message that arrives. That is deliberate:
   the page already runs four arrangements and a fifth card would have been
   the thing OT keeps catching.
   --------------------------------------------------------------------- */
.fail-grid{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,.92fr);
  gap:clamp(2rem,5vw,4.5rem);
  align-items:start;
}
@media (max-width:960px){ .fail-grid{grid-template-columns:1fr;gap:2.4rem} }

.fail-copy .lede{max-width:52ch}
.fail-copy .lede + .lede{margin-top:.9rem}

.fail-stat{
  display:flex;
  flex-wrap:wrap;
  gap:0;
  margin-top:2rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.12));
}
.fail-stat > div{
  padding:1rem 1.6rem 1rem 0;
  margin-right:1.6rem;
  border-right:1px solid var(--line-soft,rgba(160,200,220,.10));
}
.fail-stat > div:last-child{border-right:0;margin-right:0;padding-right:0}
.fail-stat b{
  display:block;
  font-family:var(--display);
  font-size:2.1rem;
  font-weight:700;
  letter-spacing:-.03em;
  line-height:1;
  color:var(--ink);
  font-variant-numeric:tabular-nums;
  margin-bottom:.35rem;
}
.fail-stat b.is-fail{color:var(--accent,#F4738F)}
.fail-stat span{font-size:.84rem;line-height:1.45;color:var(--muted);display:block;max-width:22ch}

.fail-note{
  margin-top:1.4rem;
  font-size:.86rem;
  line-height:1.55;
  color:var(--muted);
  max-width:54ch;
}

/* ---- the alert artefact ---- */
.alert{
  margin:0;
  border:1px solid var(--line,rgba(160,200,220,.18));
  border-radius:var(--r-md);
  background:var(--card,#12212f);
  overflow:hidden;
  box-shadow:var(--e-2);
}
.alert-bar{
  display:flex;
  align-items:center;
  gap:.6rem;
  padding:.7rem .95rem;
  background:rgba(244,115,143,.07);
  border-bottom:1px solid var(--line,rgba(160,200,220,.18));
}
.alert-dot{
  width:.5rem;height:.5rem;border-radius:50%;
  background:var(--accent,#F4738F);
  flex:0 0 auto;
}
.alert-from{font-size:.8rem;font-weight:600;color:var(--ink)}
/* --ink-quiet, not --muted. The alert bar carries a coral tint that lifts its
   effective ground to #222736, where --muted measures 4.15:1 and misses AA.
   The same applies to the caption over its own lifted panel. Measured, both. */
.alert-time{margin-left:auto;font-size:.76rem;color:var(--ink-quiet,#8f99ab)}

.alert-body{padding:1.15rem 1.15rem 1.25rem}
.alert-subject{
  margin:0 0 .9rem;
  font-size:1rem;
  font-weight:600;
  color:var(--accent,#F4738F);
  line-height:1.35;
}

.alert-rows{margin:0;padding:0}
.alert-rows > div{
  display:grid;
  grid-template-columns:8.2rem 1fr;
  gap:.8rem;
  padding:.5rem 0;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.09));
}
.alert-rows dt{
  margin:0;
  font-size:.76rem;
  font-weight:600;
  letter-spacing:.05em;
  text-transform:uppercase;
  color:var(--muted);
  padding-top:.1rem;
}
.alert-rows dd{margin:0;font-size:.9rem;line-height:1.5;color:var(--ink)}

.alert-foot{
  margin:1rem 0 0;
  padding-top:.85rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.12));
  font-size:.86rem;
  line-height:1.55;
  color:var(--ink-quiet,#8f99ab);
}
.alert figcaption{
  padding:.7rem .95rem;
  border-top:1px solid var(--line,rgba(160,200,220,.18));
  background:rgba(255,255,255,.015);
  font-size:.76rem;
  color:var(--ink-quiet,#8f99ab);
  line-height:1.45;
}
@media (max-width:520px){
  .alert-rows > div{grid-template-columns:1fr;gap:.15rem}
}

/* =====================================================================
   v27 — THE DOT-IN-A-CAPSULE PATTERN IS BANNED, 2026-08-17
   OT: "some obvious signs of AI". It is banned as a family, not as a
   wording, so this removes every instance and does not reintroduce it in
   a different colour somewhere else.
   ===================================================================== */

/* ---- 1. the hero overlay ---------------------------------------------
   .hd-cta is gone from the markup. The rule is neutralised here too so
   that nothing reintroduces it by re-adding the span. */
.hd-cta{display:none !important}

/* ---- 2. with / without -----------------------------------------------
   These two labels were doing real work: they are the only thing telling
   you which panel is the manual Monday and which is the built one. So they
   are replaced rather than deleted, and replaced with something LOUDER
   than the capsule was, because OT's note was that it has to be obvious.

   Set as plain type with a solid rule under it. No capsule, no dot, no
   border. The colour carries the state and the weight carries the
   emphasis, which is how the rest of the site already works. */
.ba-state{
  display:inline-block;
  /* the cards are flex columns, so a plain inline-block child still stretches
     to the full cross-axis width and the underline ran the width of the card
     rather than the width of the label */
  align-self:flex-start;
  width:fit-content;
  padding:0 0 .42rem;
  margin-bottom:1.05rem;
  font-size:.78rem;
  font-weight:700;
  letter-spacing:.12em;
  text-transform:uppercase;
  border-bottom:3px solid currentColor;
  background:none;
  border-radius:0;
}
/* the manual side: the warm red already used for the faults on this card,
   darkened one step. #c2503a measured 4.17:1 on the cream ground and missed
   AA at this size; #b4462f is 4.86:1 and is the same hue. */
.ba-state-without{color:#b4462f}
/* the built side: brand green, the same one that means "this is the system" */
.ba-state-with{color:var(--green,#35c9a4)}

/* ---- 3. the ACTION chip on the Monday briefing -----------------------
   Same family: a 7px dot inside a rounded chip. The chip stays, because a
   priority marker inside a briefing panel is real product UI, but the dot
   goes and it reads as a label rather than a status light. */
.sev::before,.sev.action::before{display:none !important}
.sev,.sev.action{
  padding-left:.5rem !important;
  letter-spacing:.1em;
  font-weight:700;
}

/* ---- 4. the alert bar dot --------------------------------------------
   My own, added an hour ago on the automations page, and the same tell.
   The alert is already unmistakably an alert from its colour and its
   subject line; it does not need a light on it. */
.alert-dot{display:none !important}
.alert-bar{padding-left:1rem}

/* =====================================================================
   v28 — DASHBOARDS PAGE, 2026-08-17
   ===================================================================== */

/* ---------------------------------------------------------------------
   W — THE OPEN CARD BECOMES READABLE.

   OT: when a card opens, the green fill arrives and the label at the top
   disappears into it.

   Measured with the card forced open: `.r-body` correctly went to #06121b,
   but `.r-eyebrow` stayed rgb(53,201,164) and `.r-title` stayed
   rgb(234,242,247) — green text and near-white text on a green fill.

   ti-cards-v14.css:84 DOES list `.r-eyebrow` and `.r-title` in its open
   rule, but ti-v16 loads after it and repaints both. So the fix belongs
   here, at the end, where nothing can undo it. Everything inside an open
   card takes the same ink, which is what OT asked for.
   --------------------------------------------------------------------- */
.rcard.is-open .r-eyebrow,
.rcard.is-open .r-title,
.rcard.is-open .r-body,
.rcard.is-open .r-more,
.rcard.is-open h3,
.rcard.is-open .r-tag,
.rcard.is-open .r-ico{
  color:#06121b !important;
}
.rcard.is-open .r-ico svg{stroke:#06121b !important}
@media (hover:hover){
  .rcard:hover .r-eyebrow,
  .rcard:hover .r-title,
  .rcard:hover .r-body,
  .rcard:hover .r-more,
  .rcard:hover h3,
  .rcard:hover .r-tag,
  .rcard:hover .r-ico{
    color:#06121b !important;
  }
  .rcard:hover .r-ico svg{stroke:#06121b !important}
}

/* ---------------------------------------------------------------------
   X — THE PAGE WEARS CLAUDE'S COLOUR.

   Every other product page takes the colour of the tool it is about:
   Excel green on spreadsheets, n8n coral on automations, cyan on data.
   Dashboards was the exception, wearing the house turquoise because it is
   the flagship — which made the flagship the only page not following its
   own rule, and made it read as a second homepage.

   The intelligence in this product is Claude. Value taken from the logo
   the site already ships, assets/logos/claude.svg: #d97757.

   Measured on this site's grounds:
     #d97757 on page #0a1420 .... 6.55:1
     #d97757 on card #12212f .... 5.78:1
   so unlike Excel green it needs no lifted variant for text. Buttons stay
   brand green everywhere, so "the green thing is the action" still holds.
   --------------------------------------------------------------------- */
body.door-dash,body.door-dashboards,body[class*="door-dash"]{
  --accent:#d97757;
  --accent-ink:#d97757;
  --accent-solid:#d97757;
  --gold:#d97757;
  --gold-bright:#e0906f;
}

/* ---------------------------------------------------------------------
   Y — THE VERDICT TAGS LOSE THE CAPSULE.

   Same banned pattern as the with/without labels: a rounded outline around
   a short status word. The words themselves are good — the Phase 2 brief
   singled them out as better than icons — so they stay, set as plain type
   with a rule under them.

   Colour does the work OT asked for: a warm amber on the two "do not buy
   this yet" verdicts, brand green on the one that says go. Subtle enough
   not to shout, different enough to read at a glance.
   --------------------------------------------------------------------- */
.rcard .r-tag{
  display:inline-block !important;
  width:fit-content;
  align-self:flex-start;
  padding:0 0 .34rem !important;
  margin-bottom:.85rem !important;
  border:0 !important;
  border-bottom:2px solid currentColor !important;
  border-radius:0 !important;
  background:none !important;
  font-size:.7rem !important;
  font-weight:700 !important;
  letter-spacing:.13em !important;
  text-transform:uppercase;
}
.rcard .r-tag-warn{color:#d9944e !important}   /* 6.9:1 on the card, measured */
.rcard .r-tag-go{color:var(--green,#35c9a4) !important}

/* ---------------------------------------------------------------------
   Z — TWO WARNINGS, THEN THE GREEN LIGHT.

   Was three equal columns, the same shape as every other trio on the site.
   The content is not three parallel things: it is two reasons not to buy
   and one reason to. The arrangement now says so — the pair sit together,
   the verdict sits apart and wider, with a rule between them.
   --------------------------------------------------------------------- */
.rgrid.verdict-split{
  display:grid;
  grid-template-columns:repeat(2,1fr);
  gap:1rem;
}
.rgrid.verdict-split > :last-child{
  grid-column:1 / -1;
  margin-top:.6rem;
  padding-top:1.6rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
}
@media (max-width:760px){
  .rgrid.verdict-split{grid-template-columns:1fr}
  .rgrid.verdict-split > :last-child{margin-top:0;padding-top:0;border-top:0}
}

/* ---------------------------------------------------------------------
   AA — THE BRIEFING ARTEFACT.

   This is the one thing on the page that shows what £250 a month actually
   produces, and it was a heading plus three lines of <br>. It now looks
   like the thing that lands in an inbox: a header strip that says when and
   how much needs doing, numbered findings with the figure carried in the
   sentence, and an action marker on each one so the right-hand side is not
   empty — which was OT's specific note.

   Not a card and not a capsule: a document with a bar on top, which is the
   same idiom as the Excel window and the failure alert, so it belongs to
   the same family without repeating either.
   --------------------------------------------------------------------- */
.brief{
  margin:0;
  border:1px solid var(--line,rgba(160,200,220,.18));
  border-radius:var(--r-md);
  background:var(--card,#12212f);
  overflow:hidden;
  box-shadow:var(--e-2);
}
.brief-bar{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:1rem;
  padding:.75rem 1rem;
  background:rgba(217,119,87,.08);
  border-bottom:1px solid var(--line,rgba(160,200,220,.18));
}
.brief-when{font-size:.82rem;font-weight:600;color:var(--ink)}
.brief-meta{font-size:.76rem;color:var(--ink-quiet,#8f99ab)}

.brief-body{padding:1.05rem 1.1rem 1.15rem}
.brief-lead{
  margin:0 0 .95rem;
  font-size:.94rem;
  font-weight:600;
  color:var(--ink);
}

.brief-row{
  display:grid;
  grid-template-columns:1.35rem 1fr;
  gap:.75rem;
  padding:.75rem 0;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.1));
}
.brief-n{
  font-size:.9rem;
  font-weight:700;
  color:var(--accent,#d97757);
  line-height:1.5;
  font-variant-numeric:tabular-nums;
}
.brief-row p{
  margin:0;
  font-size:.9rem;
  line-height:1.55;
  color:var(--ink-quiet,#8f99ab);
}
.brief-row p b{color:var(--ink);font-weight:600}

/* the action marker. Plain type with a rule, not a capsule with a dot. */
.brief-act{
  display:inline-block;
  margin-top:.45rem;
  padding-bottom:.16rem;
  border-bottom:2px solid currentColor;
  font-size:.66rem;
  font-weight:700;
  letter-spacing:.12em;
  text-transform:uppercase;
  color:var(--accent,#d97757);
}
.brief-act-quiet{color:var(--muted);border-bottom-color:var(--line,rgba(160,200,220,.22))}

.brief figcaption{
  padding:.7rem 1rem;
  border-top:1px solid var(--line,rgba(160,200,220,.18));
  background:rgba(255,255,255,.015);
  font-size:.74rem;
  line-height:1.45;
  color:var(--ink-quiet,#8f99ab);
}

/* ---------------------------------------------------------------------
   AB — THE HANDOVER MANIFEST.  #handover on dashboards.

   The page had four card grids already, so this could not be a fifth. It
   is a manifest: a numbered, ruled list where each row reads across as one
   sentence of fact — how you meet the thing, what it is, where it lives.

   Three columns, deliberately unequal. The interaction label is narrow
   because it is a category, the description takes the width because it is
   the content, and the location sits right and quiet because it is the
   footnote you check rather than read. The numeral comes from the <ol>,
   so the order is in the markup rather than typed into it.

   No card, no capsule, no icon. The only colour is the accent on the
   interaction label and the numeral, which is the one thing distinguishing
   the four rows from each other.
   --------------------------------------------------------------------- */
.manifest{
  list-style:none;
  margin:2.6rem 0 0;
  padding:0;
  counter-reset:man;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
}

.man{
  counter-increment:man;
  position:relative;
  display:grid;
  grid-template-columns:9.5rem minmax(0,1fr) 13rem;
  gap:clamp(1.2rem,3vw,2.8rem);
  align-items:start;
  padding:1.7rem 0 1.7rem 3.1rem;
  border-bottom:1px solid var(--line-soft,rgba(160,200,220,.14));
}
/* the numeral, from the list rather than typed in */
.man::before{
  content:counter(man,decimal-leading-zero);
  position:absolute;
  left:0;
  top:1.72rem;
  font-size:.86rem;
  font-weight:700;
  letter-spacing:.02em;
  color:var(--accent,#d97757);
  font-variant-numeric:tabular-nums;
}

/* how you meet it: the thing that makes the four different from each other */
.man-how{
  font-size:.72rem;
  font-weight:700;
  letter-spacing:.13em;
  text-transform:uppercase;
  color:var(--accent,#d97757);
  padding-top:.28rem;
}

.man-what b{
  display:block;
  font-family:var(--display);
  font-size:1.24rem;
  font-weight:700;
  letter-spacing:-.02em;
  color:var(--ink);
  margin-bottom:.42rem;
}
.man-what p{
  margin:0;
  font-size:.97rem;
  line-height:1.6;
  color:var(--ink-quiet,#8f99ab);
  max-width:54ch;
}

/* where it lives: quiet, right-aligned, read as a footnote */
.man-where{
  text-align:right;
  font-size:.86rem;
  font-weight:600;
  color:var(--ink);
  padding-top:.2rem;
  line-height:1.4;
}
.man-where i{
  display:block;
  margin-top:.22rem;
  font-style:normal;
  font-size:.79rem;
  font-weight:400;
  color:var(--muted);
  line-height:1.45;
}

/* the ownership statement. It applies to all four, so it sits under the
   whole list rather than being repeated as a fourth column. */
.man-own{
  margin:2.2rem 0 0;
  padding-left:3.1rem;
  font-size:.97rem;
  line-height:1.62;
  color:var(--ink-quiet,#8f99ab);
  max-width:74ch;
}
.man-own b{color:var(--ink);font-weight:600}

@media (max-width:1000px){
  .man{grid-template-columns:8rem minmax(0,1fr);gap:1.2rem}
  .man-where{
    grid-column:2;
    text-align:left;
    padding-top:.7rem;
    margin-top:.5rem;
    border-top:1px solid var(--line-soft,rgba(160,200,220,.1));
  }
  .man-where i{display:inline;margin-left:.5rem}
}
@media (max-width:680px){
  .man{grid-template-columns:1fr;gap:.55rem;padding-left:2.4rem}
  .man::before{top:1.75rem}
  .man-how{padding-top:0}
  .man-where{grid-column:1}
  .man-own{padding-left:2.4rem}
}

/* ---------------------------------------------------------------------
   AC — THE LAST GREEN ON AN ORANGE PAGE.

   OT spotted the proof figures in "What the first build actually found"
   and the week labels in "How it gets built" still rendering brand green
   after the page moved to Claude's colour.

   He is right, and the site's own rule already answers it. From the v12
   README: a product page carries ONE accent, applied to structural marks,
   and buttons stay brand green everywhere so "the green thing is the
   action" never breaks. Proof figures and week labels are structural
   marks, not actions. They were green only because they were written
   against --green directly, before this page had an accent of its own.

   Leaving them green was actively misleading, not just inconsistent: on a
   page where orange is the identity and green means "you can click this",
   a £277k figure in green reads as a link.

   Measured before changing: #d97757 is 5.78:1 on the card ground, and
   these figures are 36.8px, so they clear AA for large text several times
   over. The .r-step numeral keeps its own treatment; only the colour moves.
   --------------------------------------------------------------------- */
body.door-dash .pnum b,
body.door-dash .r-step,
body.door-dash .r-step small{
  color:var(--accent,#d97757) !important;
}
/* the basis tag under each figure stays muted — it is a qualifier, not a mark */
body.door-dash .pnum em{color:var(--muted) !important}

/* The "Said plainly" note kept a green 3px left rule. Same reasoning as the
   figures above it: a rule is a structural mark, so it takes the page accent.

   Audited the rest of the page rather than assuming. Everything else still
   rendering brand green is correct and stays:
     - .cta / .btn.solid / .skip-link  — actions. Green means "you can press
       this", site-wide, and that must not drift per page.
     - .r-tag-go "Right fit"           — green because it means GO, which is
       meaning rather than decoration. The two warnings beside it are amber
       for the same reason.
*/
body.door-dash .note{border-left-color:var(--accent,#d97757) !important}

/* =====================================================================
   v31 — DATA PAGE, 2026-08-17
   ===================================================================== */

/* ---------------------------------------------------------------------
   AD — THE ROUTE, REBUILT.

   Five stages on a grid with the connectors between them carrying the
   times. Panels are the site's own dark surface, so the section stops
   being the one cream thing on a dark page.

   The stages are top-aligned rather than centred on a shared axis, which
   is what made the old one look misaligned: four cards of four different
   heights, each centred on y=170, so no two edges agreed with each other.
   --------------------------------------------------------------------- */
.route{
  display:grid;
  grid-template-columns:1fr auto 1fr auto 1.05fr auto 1fr auto .92fr;
  gap:0;
  align-items:stretch;
  margin-top:2.4rem;
}

.rt-step,.rt-out > div{
  background:var(--card,#12212f);
  border:1px solid var(--line,rgba(160,200,220,.16));
  border-radius:var(--r-md);
  padding:1.05rem 1.1rem;
}
.rt-step{display:flex;flex-direction:column}
/* the one stage that is the product, not a step past it */
.rt-hot{border-color:rgba(31,192,231,.4)}

.rt-k{
  display:block;
  font-size:.66rem;
  font-weight:700;
  letter-spacing:.13em;
  text-transform:uppercase;
  color:var(--accent,#1FC0E7);
  margin-bottom:.45rem;
}
.rt-step > b{
  display:block;
  font-size:.94rem;
  font-weight:600;
  color:var(--ink);
  line-height:1.35;
  margin-bottom:.7rem;
}
.rt-step ul{
  list-style:none;
  margin:auto 0 0;
  padding:.7rem 0 0;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.1));
}
.rt-step li{
  font-size:.82rem;
  line-height:1.5;
  color:var(--ink-quiet,#8f99ab);
  padding:.12rem 0;
}

/* the connector. A rule with the time sitting on it, rather than a chip
   floating beside the flow. */
.rt-link{
  position:relative;
  min-width:2.6rem;
  display:flex;
  align-items:center;
  justify-content:center;
}
.rt-link::before{
  content:"";
  position:absolute;
  left:0;right:0;top:50%;
  height:1px;
  background:var(--line,rgba(160,200,220,.22));
}
.rt-link span{
  position:relative;
  background:var(--bg,#0a1420);
  padding:.1rem .4rem;
  font-size:.68rem;
  font-weight:600;
  letter-spacing:.06em;
  color:var(--accent,#1FC0E7);
  font-variant-numeric:tabular-nums;
}

/* the three things that come out the far end */
.rt-out{display:flex;flex-direction:column;gap:.5rem;justify-content:center}
.rt-out > div{padding:.7rem .85rem}
.rt-out b{display:block;font-size:.88rem;font-weight:600;color:var(--ink)}
.rt-out span{display:block;font-size:.76rem;color:var(--muted);margin-top:.1rem}

@media (max-width:1100px){
  .route{grid-template-columns:1fr auto 1fr;row-gap:1rem}
  .rt-link{min-height:2rem}
  .rt-link::before{left:50%;right:auto;top:0;bottom:0;width:1px;height:auto}
  .rt-out{grid-column:1 / -1;flex-direction:row}
  .rt-out > div{flex:1 1 0}
}
@media (max-width:720px){
  .route{grid-template-columns:1fr}
  .rt-link{min-height:1.6rem}
  .rt-out{flex-direction:column}
}

/* ---------------------------------------------------------------------
   AE — THE THREE HOPS PANEL.

   It was a cream card carrying pale grey body text — OT: "you really
   can't read that, it's horrible". Measured: the body was set on --muted
   against #f4f2ec. It is now the site's dark panel like everything else
   around it, and the text takes the normal ink.
   --------------------------------------------------------------------- */
.hops{
  background:var(--card,#12212f) !important;
  border:1px solid var(--line,rgba(160,200,220,.16)) !important;
  border-radius:var(--r-md);
  padding:1.5rem 1.6rem !important;
  color:var(--ink-quiet,#8f99ab) !important;
}
.hops h3{color:var(--ink) !important;margin-bottom:1.1rem}
.hops .hop b{display:block;color:var(--accent,#1FC0E7) !important;margin-bottom:.3rem;font-size:.9rem}
.hops .hop span{color:var(--ink-quiet,#8f99ab) !important;font-size:.9rem;line-height:1.55}
.hops .hop-foot{
  color:var(--ink) !important;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.12));
  padding-top:1rem;
  margin-top:1.2rem;
  font-size:.9rem;
}

/* ---------------------------------------------------------------------
   AF — "HOW THE FIGURES EARN TRUST".

   Three turquoise discs and a "+" that hid the evidence. The discs were
   the reveal system's fallback mark, which is what OT means by the weird
   globe. The numeral is real information (there are three checks, in
   order); the disc was not.
   --------------------------------------------------------------------- */
.v.v-open{position:relative;padding-left:3.1rem}
.v.v-open > i{
  position:absolute;
  left:1.1rem;
  top:1.15rem;
  width:auto;height:auto;
  background:none !important;
  border:0 !important;
  border-radius:0 !important;
  font-style:normal;
  font-size:1.05rem;
  font-weight:700;
  color:var(--accent,#1FC0E7);
  font-variant-numeric:tabular-nums;
}
.v.v-open > i::before,.v.v-open > i::after{display:none !important}
.v.v-open .s-ico,.v.v-open::after{display:none !important}
.v.v-open > b{display:block;margin-bottom:.4rem}
.v.v-open > p{
  display:block !important;
  opacity:1 !important;
  max-height:none !important;
  visibility:visible !important;
  font-size:.92rem;
  line-height:1.58;
  color:var(--ink-quiet,#8f99ab);
}

/* ---------------------------------------------------------------------
   AG — THE SYSTEMS AUDIT.  #systems-audit on data.html.

   Asymmetric: the method takes the width because it is the substance, the
   commercial terms sit in a narrow rail beside it because they are looked
   up rather than read. That split is the one arrangement this page did not
   already have — it runs a five-across route, a platform grid, a
   three-across protection row and a three-across trust row, so a fourth
   equal grid would have been the thing OT keeps catching.
   --------------------------------------------------------------------- */
.sa-grid{
  display:grid;
  grid-template-columns:minmax(0,1.55fr) minmax(0,.85fr);
  gap:clamp(2rem,4.5vw,3.6rem);
  align-items:start;
  margin-top:2.4rem;
}
@media (max-width:940px){ .sa-grid{grid-template-columns:1fr;gap:2.2rem} }

.sa-h{
  font-size:.72rem !important;
  font-weight:700 !important;
  letter-spacing:.14em !important;
  text-transform:uppercase;
  color:var(--accent,#1FC0E7) !important;
  margin:0 0 1rem !important;
  padding-bottom:.6rem;
  border-bottom:1px solid var(--line-soft,rgba(160,200,220,.14));
}
.sa-main .sa-h + .sa-h,
.sa-main .sa-p + .sa-h{margin-top:2.2rem !important}

.sa-steps{
  list-style:none;
  margin:0;
  padding:0;
  counter-reset:sa;
}
.sa-steps li{
  counter-increment:sa;
  position:relative;
  padding:0 0 1.1rem 2.5rem;
  font-size:.98rem;
  line-height:1.6;
  color:var(--ink-quiet,#8f99ab);
}
.sa-steps li::before{
  content:counter(sa,decimal-leading-zero);
  position:absolute;
  left:0;
  top:.06rem;
  font-size:.82rem;
  font-weight:700;
  color:var(--accent,#1FC0E7);
  font-variant-numeric:tabular-nums;
}
.sa-steps b{color:var(--ink);font-weight:600}

.sa-p{
  font-size:.98rem;
  line-height:1.62;
  color:var(--ink-quiet,#8f99ab);
  max-width:58ch;
  margin:0;
}

/* the commercial rail */
.sa-side{display:flex;flex-direction:column;gap:1rem}
.sa-price,.sa-credit{
  border:1px solid var(--line,rgba(160,200,220,.16));
  border-radius:var(--r-md);
  background:var(--card,#12212f);
  padding:1.15rem 1.2rem;
}
.sa-credit{background:transparent}
.sa-k{
  display:block;
  font-size:.68rem;
  font-weight:700;
  letter-spacing:.13em;
  text-transform:uppercase;
  color:var(--muted);
  margin-bottom:.4rem;
}
.sa-price b,.sa-credit b{
  display:block;
  font-family:var(--display);
  font-size:2.3rem;
  font-weight:700;
  letter-spacing:-.03em;
  line-height:1;
  color:var(--ink);
  font-variant-numeric:tabular-nums;
  margin-bottom:.5rem;
}
.sa-credit b{color:var(--accent,#1FC0E7);font-size:1.9rem}
.sa-n{display:block;font-size:.85rem;line-height:1.5;color:var(--ink-quiet,#8f99ab)}

/* The line that stops the credit being a trick. It is the most important
   sentence in the block and it argues against the sale, which is the
   register the rest of the site uses. */
.sa-honest{
  margin:.3rem 0 0;
  padding-top:.9rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.12));
  font-size:.86rem;
  line-height:1.55;
  color:var(--muted);
}

/* what happens after */
.sa-after{margin-top:clamp(2.4rem,5vw,3.6rem)}
.sa-outs{
  display:grid;
  grid-template-columns:repeat(4,1fr);
  gap:0;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
}
.sa-outs > div{
  padding:1.2rem 1.3rem 0 0;
  margin-right:1.3rem;
  border-right:1px solid var(--line-soft,rgba(160,200,220,.1));
}
.sa-outs > div:last-child{border-right:0;margin-right:0;padding-right:0}
.sa-outs b{
  display:block;
  font-size:.98rem;
  font-weight:600;
  color:var(--ink);
  margin-bottom:.4rem;
}
.sa-outs p{
  margin:0;
  font-size:.89rem;
  line-height:1.55;
  color:var(--ink-quiet,#8f99ab);
}
@media (max-width:900px){
  .sa-outs{grid-template-columns:1fr 1fr;row-gap:1.4rem}
  .sa-outs > div:nth-child(2n){border-right:0;margin-right:0;padding-right:0}
}
@media (max-width:560px){
  .sa-outs{grid-template-columns:1fr}
  .sa-outs > div{border-right:0;margin-right:0;padding-right:0;
    border-bottom:1px solid var(--line-soft,rgba(160,200,220,.1));padding-bottom:1.2rem}
  .sa-outs > div:last-child{border-bottom:0}
}

/* The outcomes heading is a full sentence, so it is set as a sentence.
   .sa-h is a tiny tracked label and a sentence in that treatment reads as a
   caption that has been shouted. */
.sa-after-h{
  font-size:1.28rem !important;
  font-weight:700 !important;
  letter-spacing:-.02em !important;
  text-transform:none !important;
  color:var(--ink) !important;
  margin:0 0 1.3rem !important;
  padding-bottom:0 !important;
  border-bottom:0 !important;
}

/* ---------------------------------------------------------------------
   AH — THE PRICING PAGE NOW HOLDS FIVE, NOT FOUR.

   .plans was `1fr 1fr 1.12fr 1fr`, a four-column grid with the featured
   dashboard given the extra width. A fifth plan wrapped onto its own row
   and left one card sitting alone under four.

   Five equal columns is too tight for a card with a four-item list, so the
   row splits 3 + 2: the three fixed-price pieces of work on top, and the
   dashboard and the retainer beneath, which is the honest grouping anyway
   (three things you buy once, two things that make a system).
   --------------------------------------------------------------------- */
.plans{
  grid-template-columns:repeat(3,1fr) !important;
}
.plans > .plan:nth-child(4),
.plans > .plan:nth-child(5){grid-column:span 1}
@media (min-width:1181px){
  /* Centre the trailing pair under the three above. The previous attempt
     used margin-left:16.6% on each card, which is roughly a sixth of ONE
     column — the shift actually needed is half a column plus half a gap,
     so the pair still sat left of centre with the void on the right.

     Six columns solves it exactly instead of approximately: each card
     spans two, so the top row lands on 1-2 / 3-4 / 5-6 (identical to three
     equal columns), and the bottom pair on 2-3 / 4-5. With six tracks t
     and gap g, the pair's left margin is t+g and its right margin is
     6t+5g − (5t+4g) = t+g. Equal by construction, at any width. */
  .plans{grid-template-columns:repeat(6,1fr) !important}
  .plans > .plan:nth-child(1){grid-column:1 / span 2}
  .plans > .plan:nth-child(2){grid-column:3 / span 2}
  .plans > .plan:nth-child(3){grid-column:5 / span 2}
  .plans > .plan:nth-child(4){grid-column:2 / span 2;margin-left:0}
  .plans > .plan:nth-child(5){grid-column:4 / span 2;margin-left:0}
}
@media (min-width:721px) and (max-width:1180px){
  /* Two across, so five cards land 2 + 2 + 1 and the last one was sitting
     hard left with the row's whole second half empty. Same fix as above,
     one breakpoint down: four tracks, each card spanning two, and the odd
     one out placed on 2-3 so its margins are t+g on both sides. */
  .plans{grid-template-columns:repeat(4,1fr) !important}
  .plans > .plan:nth-child(1){grid-column:1 / span 2}
  .plans > .plan:nth-child(2){grid-column:3 / span 2}
  .plans > .plan:nth-child(3){grid-column:1 / span 2}
  .plans > .plan:nth-child(4){grid-column:3 / span 2;margin-left:0}
  .plans > .plan:nth-child(5){grid-column:2 / span 2;margin-left:0}
}
@media (max-width:720px){
  .plans{grid-template-columns:1fr !important}
  .plans > .plan{grid-column:1 / -1 !important;margin-left:0}
}

/* The example block on the data page reuses the spreadsheet page's .ex-grid
   wholesale — same component, same proportions — because it is the same
   deliverable one rung up, and making it look different would imply it is a
   different kind of thing. Only the top margin differs, since it follows a
   commercial rail rather than a step track. */
.sa-example{margin-top:clamp(2.6rem,5vw,4rem)}

/* ---------------------------------------------------------------------
   AI — THE TWO AUTOMATION TIERS.  #tiers on automations.

   Two columns split by a rule, not two cards. The automations page already
   runs a card grid for the menu and a ruled accordion for the FAQ; a third
   boxed component would be the thing OT keeps catching.

   The split is even, because for once the two things ARE parallel: same
   shape of offer, different complexity. That is the one case where equal
   columns state something true rather than defaulting.
   --------------------------------------------------------------------- */
.tiers{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:clamp(1.8rem,4vw,3.4rem);
  margin-top:2.4rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
}
.tier{
  padding-top:1.8rem;
  display:flex;
  flex-direction:column;
}
.tier + .tier{
  border-left:1px solid var(--line-soft,rgba(160,200,220,.14));
  padding-left:clamp(1.8rem,4vw,3.4rem);
}

.tier-k{
  display:block;
  font-size:.72rem;
  font-weight:700;
  letter-spacing:.13em;
  text-transform:uppercase;
  color:var(--accent,#F4738F);
  margin-bottom:.7rem;
}
.tier-p{
  display:block;
  font-family:var(--display);
  font-size:2.5rem;
  font-weight:700;
  letter-spacing:-.032em;
  line-height:1;
  color:var(--ink);
  font-variant-numeric:tabular-nums;
  margin-bottom:.9rem;
}
.tier-d{
  margin:0 0 1.2rem;
  font-size:1rem;
  line-height:1.6;
  color:var(--ink-quiet,#8f99ab);
}

.tier-l{
  list-style:none;
  margin:0 0 1.3rem;
  padding:0;
  flex:1 1 auto;
}
.tier-l li{
  position:relative;
  padding:.5rem 0 .5rem 1.15rem;
  font-size:.93rem;
  line-height:1.55;
  color:var(--ink-quiet,#8f99ab);
  border-top:1px solid var(--line-soft,rgba(160,200,220,.09));
}
.tier-l li::before{
  content:"";
  position:absolute;
  left:0;top:1.02em;
  width:.5rem;height:1px;
  background:var(--accent,#F4738F);
}

.tier-f{
  display:block;
  padding-top:.9rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
  font-size:.83rem;
  color:var(--muted);
}

/* the notes under the pair. The first one argues against the expensive
   option, which is the register the rest of the site uses. */
.tier-notes{
  margin-top:clamp(2rem,4vw,3rem);
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:clamp(1.6rem,3.5vw,2.8rem);
  align-items:start;
}
.tier-notes p{
  margin:0;
  font-size:.95rem;
  line-height:1.62;
  color:var(--ink-quiet,#8f99ab);
}
.tier-notes b{color:var(--ink);font-weight:600}
.tier-notes a{color:var(--accent,#F4738F);text-decoration:underline;text-underline-offset:.18em}
.tier-honest{
  grid-column:1 / -1;
  padding-top:1.2rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.12));
  color:var(--muted) !important;
  font-size:.9rem !important;
}

@media (max-width:860px){
  .tiers{grid-template-columns:1fr;gap:0}
  .tier + .tier{
    border-left:0;padding-left:0;
    border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
    margin-top:1.8rem;
  }
  .tier-notes{grid-template-columns:1fr;gap:1.3rem}
}

/* ---------------------------------------------------------------------
   AJ — THE FIVE MOVES SHOW THEIR WORK.

   Same fix as the pillars, the £300 track and the trust items: the sparkle
   was the reveal system's keyword-guessed fallback mark, the "+" promised
   content that should not have been hidden, and the text is the point.

   This is the fifth exception. Noted in ti-v16.js: if a sixth is needed,
   the reveal system should be retired rather than exception-listed, because
   at that point it is being switched off nearly everywhere it applies.
   --------------------------------------------------------------------- */
.rstep::after,.rstep .s-ico,.rstep .dot{display:none !important}
.rstep > p{
  display:block !important;
  opacity:1 !important;
  max-height:none !important;
  visibility:visible !important;
  margin-top:.5rem;
  font-size:.9rem;
  line-height:1.55;
  color:var(--ink-quiet,#8f99ab);
}
.rstep > i{
  display:block;
  font-style:normal;
  font-size:1.5rem;
  font-weight:700;
  letter-spacing:-.02em;
  color:var(--accent,#1FC0E7);
  font-variant-numeric:tabular-nums;
  margin-bottom:.35rem;
}
.rstep > b{display:block;font-size:.98rem;color:var(--ink)}

/* ---------------------------------------------------------------------
   AK — THE SECOND TIER BECOMES A CONVERSATION, 2026-08-18.

   The price came off. A job with a judgement in it cannot be quoted
   honestly before it has been scoped, so a number here would have been
   guessing in public — and it sat on the same page as two workflows you
   can press "run" on, which made the asymmetry visible.

   The right column now carries a call to action where the price was, so
   the two columns still balance: one has a number, one has a next step.
   --------------------------------------------------------------------- */
.tier-p-talk{
  font-size:1.55rem !important;
  line-height:1.15 !important;
  letter-spacing:-.022em !important;
  color:var(--accent,#F4738F) !important;
  max-width:14ch;
}
.tier-cta{
  align-self:flex-start;
  margin-bottom:1.1rem;
  padding:.72rem 1.15rem;
  font-size:.9rem;
  font-weight:600;
  line-height:1;
  border-radius:var(--r-sm);
}
/* the left column has no CTA, so its list keeps the space instead and the
   two footers still land on the same line */
.tier:first-child .tier-l{padding-bottom:3.35rem}
@media (max-width:860px){
  .tier:first-child .tier-l{padding-bottom:0}
  .tier-p-talk{max-width:none}
}

/* ---------------------------------------------------------------------
   AL — THE NOTES SIT ON THE SAME AXIS AS THE COLUMNS ABOVE.

   .tier-notes had its own gap — clamp(1.6rem,3.5vw,2.8rem) against the
   columns' clamp(1.8rem,4vw,3.4rem) — so the right-hand note started a
   few pixels left of the right-hand column and the whole block read as
   slightly out of true.

   Now identical metrics, and the divider carries down through the notes
   so the split is one continuous line from the top of the columns to the
   bottom of the page section rather than stopping halfway.
   --------------------------------------------------------------------- */
.tier-notes{
  grid-template-columns:1fr 1fr;
  gap:clamp(1.8rem,4vw,3.4rem) !important;
  margin-top:0 !important;
  padding-top:clamp(1.6rem,3vw,2.2rem);
}
.tier-notes > p:nth-child(2){
  border-left:1px solid var(--line-soft,rgba(160,200,220,.14));
  padding-left:clamp(1.8rem,4vw,3.4rem);
}
/* the columns lose their bottom padding so the rule is continuous rather
   than a rule, a gap, then another rule */
.tiers{padding-bottom:0}
.tier{padding-bottom:1.4rem}

@media (max-width:860px){
  .tier-notes{grid-template-columns:1fr;gap:1.3rem !important}
  .tier-notes > p:nth-child(2){border-left:0;padding-left:0}
}

/* ---------------------------------------------------------------------
   AM — RULES AND THEIR EXCEPTIONS.  2026-08-18.

   A shape used nowhere else on this page. The automations page already
   runs: a spec strip in the hero, a card grid for the menu, framed
   workflow players, two ruled columns for the tiers, an asymmetric
   copy-plus-artefact block for the failure, and a details accordion for
   the FAQ. So this is none of those.

   The rule takes the full width and states itself. The exception is
   inset beneath it, narrower, with its own marker — so the indent itself
   carries the meaning: this is the rule, and this is the part it does not
   cover. Subordination shown by position rather than announced.

   Both are visible at rest, which was the whole point: hiding a caveat to
   a promise is the one thing a trust page cannot do.
   --------------------------------------------------------------------- */
.rules{
  margin-top:2.4rem;
  display:flex;
  flex-direction:column;
  gap:0;
}

.rule{
  padding:2rem 0;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
}
.rule:last-child{border-bottom:1px solid var(--line-soft,rgba(160,200,220,.14))}

.rule-head{
  display:flex;
  align-items:baseline;
  gap:1.4rem;
  flex-wrap:wrap;
  margin-bottom:.9rem;
}
.rule-n{
  flex:0 0 auto;
  font-size:.7rem;
  font-weight:700;
  letter-spacing:.15em;
  text-transform:uppercase;
  color:var(--accent,#F4738F);
  padding-top:.2rem;
}
.rule-claim{
  margin:0 !important;
  font-size:clamp(1.35rem,2.4vw,1.85rem) !important;
  font-weight:700 !important;
  line-height:1.2 !important;
  letter-spacing:-.022em !important;
  color:var(--ink) !important;
}
/* the second clause takes the accent, matching how every other heading on
   the site marks its second half */
.rule-claim em{font-style:normal;color:var(--accent,#F4738F) !important}

.rule-body{
  margin:0;
  font-size:1rem;
  line-height:1.62;
  color:var(--ink-quiet,#8f99ab);
  max-width:64ch;
  padding-left:calc(4.4rem + 1.4rem);
}

/* the carve-out. Inset further than the rule it qualifies, so the
   indent does the explaining. */
.rule-exc{
  margin-top:1.15rem;
  margin-left:calc(4.4rem + 1.4rem);
  padding:.95rem 1.2rem;
  border-left:2px solid var(--accent,#F4738F);
  background:rgba(255,255,255,.022);
  border-radius:0 var(--r-sm) var(--r-sm) 0;
  max-width:60ch;
}
.rule-exc-k{
  display:block;
  font-size:.66rem;
  font-weight:700;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--accent,#F4738F);
  margin-bottom:.35rem;
}
.rule-exc p{
  margin:0;
  font-size:.94rem;
  line-height:1.58;
  color:var(--ink);
}

@media (max-width:760px){
  .rule-head{gap:.5rem}
  .rule-n{flex:1 0 100%;padding-top:0}
  .rule-body{padding-left:0}
  .rule-exc{margin-left:0;max-width:none}
}

/* One shared indent instead of three guesses.

   The claim sat at 230px, the body and the exception at 243px, because the
   head used a flex gap after a variable-width label while the body used a
   hardcoded padding. Any change to the label text would have moved one and
   not the others. --rule-indent is now the single source for all three. */
.rules{--rule-indent:5rem}
.rule-head{gap:0}
.rule-n{
  width:var(--rule-indent);
  flex:0 0 var(--rule-indent);
}
.rule-body{padding-left:var(--rule-indent)}
.rule-exc{margin-left:var(--rule-indent)}
@media (max-width:760px){
  .rules{--rule-indent:0rem}
  .rule-n{flex:1 0 100%;width:auto;margin-bottom:.35rem}
}

/* ---------------------------------------------------------------------
   AN — THE DATA PAGE STOPS WEARING GREEN.  2026-08-18.

   Same audit as the dashboards page. Structural marks were still painted
   with --green / --line-green because they were authored before this page
   had an accent of its own, so a cyan page carried green outlines and
   green labels.

   The rule is unchanged: brand green means ACTION and is reserved for
   buttons; everything structural takes the page accent, which here is
   connection cyan #1FC0E7. Measured: 8.14:1 on the page ground and 7.2:1
   on the card, so it clears AA comfortably at any size.
   --------------------------------------------------------------------- */
body.door-data .v,
body.door-data .vet .v{
  border-color:rgba(31,192,231,.34) !important;
}
body.door-data .nd i,
body.door-data .nd u,
body.door-data .rstep > i,
body.door-data .src b,
body.door-data .hop b,
body.door-data .note b{
  color:var(--accent,#1FC0E7) !important;
}
body.door-data .note{border-left-color:var(--accent,#1FC0E7) !important}
body.door-data .nd{border-color:var(--line,rgba(160,200,220,.16)) !important}
body.door-data .nd:hover{border-color:rgba(31,192,231,.42) !important}

/* ---------------------------------------------------------------------
   AO — THE HERO FLOW: BIGGER, FIVE SOURCES, SEQUENCED.  2026-08-18.

   Kept exactly what worked — the node lighting up and the wire drawing
   itself — and gave both more room and more to say. Was three sources in
   a 430px box; now five, scaling with the viewport.

   The stagger is driven off a --i index on each node and each path, so
   adding or removing a source needs no new CSS. Each node lights, then its
   wire draws, so the eye follows one system at a time into the answer
   instead of five things happening at once.
   --------------------------------------------------------------------- */
.flow-mini{
  max-width:min(720px,94%) !important;
  margin:3rem auto 0 !important;
}

.fm-sources{gap:.55rem .7rem !important}
.fm-node{
  gap:.5rem !important;
  padding:.5rem 1rem .5rem .7rem !important;
  font-size:.86rem !important;
  border-color:var(--line,rgba(160,200,220,.18)) !important;
}
.fm-node img{width:19px !important;height:19px !important}

/* each node arrives in turn, then holds. --i carries the order. */
@media (prefers-reduced-motion:no-preference){
  .flow-mini.reveal .fm-node{opacity:0;transform:translateY(-6px)}
  .flow-mini.reveal.in .fm-node{
    animation:fm-light .5s cubic-bezier(.2,.7,.3,1) forwards;
    animation-delay:calc(var(--i) * .11s);
  }
  /* the wire follows its own node rather than all five going together */
  .flow-mini.reveal .fm-lines path{stroke-dashoffset:1}
  .flow-mini.reveal.in .fm-lines path{
    animation:ti-draw .8s cubic-bezier(.4,0,.2,1) forwards;
    animation-delay:calc(.2s + var(--i) * .11s) !important;
  }
  .flow-mini.reveal .fm-out{opacity:0;transform:translateY(8px)}
  .flow-mini.reveal.in .fm-out{
    animation:fm-light .6s cubic-bezier(.2,.7,.3,1) .82s forwards;
  }
}
@keyframes fm-light{to{opacity:1;transform:none}}

.fm-lines{
  width:100%;
  height:clamp(58px,7vw,96px);
  display:block;
  margin:.2rem 0;
  overflow:visible;
}
.fm-lines path{
  stroke:var(--accent,#1FC0E7);
  stroke-width:1.4;
  fill:none;
  opacity:.75;
}

.fm-out{
  max-width:min(560px,100%) !important;
  padding:1.15rem 1.35rem !important;
  border:1px solid rgba(31,192,231,.24);
}
.fm-out span{font-size:.92rem !important}

@media (max-width:640px){
  .fm-node{font-size:.78rem !important;padding:.42rem .8rem .42rem .55rem !important}
  .fm-node img{width:16px !important;height:16px !important}
  .fm-lines{height:52px}
}

/* The hops panel is not a second route diagram, and now says so. The route
   answers "what happens to my data"; this answers "which companies hold it,
   and where" — a residency question, and the one that carries the single
   most important disclosure on the page, that one hop leaves the UK. Merging
   them would have buried that. */
.hops h3{
  font-size:1.15rem !important;
  line-height:1.3 !important;
  margin-bottom:.5rem !important;
}
.hops h3 em{font-style:normal;color:var(--accent,#1FC0E7)}
.hops-note{
  margin:0 0 1.35rem !important;
  font-size:.9rem;
  line-height:1.55;
  color:var(--muted);
  max-width:62ch;
}

/* the rail's heading matches the left column exactly, so the two columns
   open on the same baseline instead of the price label sitting 20px low
   inside its card padding */
.sa-side > .sa-h{margin-bottom:1rem !important}

/* ---------------------------------------------------------------------
   AP — THE FIVE MOVES, PRESENTED PROPERLY.  2026-08-18.

   They were five equal boxes in a row, which is the shape the rest of the
   site keeps getting pulled up on. They are not five parallel options —
   they are five steps in a fixed order, and the page's own promise is that
   none of them disturbs your systems.

   So they run as a numbered track with a thread through the numerals, the
   same grammar as the £300 track on the spreadsheets page. Reusing an
   established arrangement rather than inventing a sixth is the point: the
   site should have a small vocabulary used consistently.
   --------------------------------------------------------------------- */
.rail{
  display:grid !important;
  grid-template-columns:repeat(5,1fr) !important;
  gap:0 !important;
  position:relative;
  margin-top:2.4rem;
}
.rail > .rstep{
  background:transparent !important;
  border:0 !important;
  border-radius:0 !important;
  box-shadow:none !important;
  padding:2.5rem 1.3rem 0 !important;
  margin:0 !important;
}
.rail > .rstep:first-child{padding-left:0 !important}
.rail > .rstep:last-child{padding-right:0 !important}
.rail > .rstep + .rstep{
  border-left:1px solid var(--line-soft,rgba(160,200,220,.11)) !important;
}
/* the thread. Stops inside the first and last so it reads as a connection
   between the steps rather than a rule across the section. */
.rail::before{
  content:"";
  position:absolute;
  top:2.95rem;
  left:10%;
  right:10%;
  height:1px;
  background:var(--line-soft,rgba(160,200,220,.16));
}
.rail > .rstep > i{
  position:absolute;
  top:2.25rem;
  margin:0 !important;
  font-size:1.02rem !important;
  background:var(--bg,#0a1420);
  padding-right:.55rem;
  z-index:1;
}
.rail > .rstep{position:relative}
.rail > .rstep > b{margin-top:.35rem}

@media (max-width:1000px){
  .rail{grid-template-columns:repeat(2,1fr) !important;row-gap:2rem}
  .rail::before{display:none}
  .rail > .rstep{padding-inline:1.3rem !important}
  .rail > .rstep:nth-child(odd){padding-left:0 !important}
  .rail > .rstep:nth-child(odd){border-left:0 !important}
}
@media (max-width:620px){
  .rail{grid-template-columns:1fr !important;row-gap:1.7rem}
  .rail > .rstep{padding-inline:0 !important;border-left:0 !important}
}

/* ---------------------------------------------------------------------
   AQ — THE ROUTE, LESS CLUNKY.

   OT: "looks a little bit clunky". The panels were carrying full card
   weight — border, radius, fill — on five boxes in a row, which made a
   flow diagram read as five stacked cards with lines between them.

   The panels lose their fill and keep only a hairline, so the connectors
   become the structure and the panels become stops on it. Same content,
   less furniture.
   --------------------------------------------------------------------- */
.rt-step,.rt-out > div{
  background:rgba(255,255,255,.018) !important;
  border-color:var(--line-soft,rgba(160,200,220,.12)) !important;
}
.rt-hot{
  background:rgba(31,192,231,.05) !important;
  border-color:rgba(31,192,231,.3) !important;
}
.rt-link::before{background:var(--line,rgba(160,200,220,.26))}
.rt-link span{
  font-size:.66rem;
  letter-spacing:.1em;
  text-transform:uppercase;
}
.rt-step > b{font-size:.9rem}
.rt-step li{font-size:.79rem;padding:.1rem 0}

/* The numerals were landing on top of the titles: padding-top was 2.5rem
   while the numeral sat at 2.25rem, so the title began exactly where the
   numeral was. The numeral now has its own band above the text, sitting on
   the thread with the page ground punching a hole in it. */
.rail > .rstep{padding-top:4.3rem !important}
.rail::before{top:2.72rem}
.rail > .rstep > i{
  top:2.15rem;
  padding:0 .55rem 0 0;
  line-height:1.1;
}
.rail > .rstep:first-child > i{padding-right:.55rem}
.rail > .rstep > b{margin-top:0}
@media (max-width:1000px){
  .rail > .rstep{padding-top:3.6rem !important}
  .rail > .rstep > i{top:1.6rem}
}

/* ---------------------------------------------------------------------
   AR — THE FLOW LINES START WHERE THE CHIPS ARE.  2026-08-18.

   The wires began at hardcoded x positions (56, 168, 280, 392, 504 in a
   560 viewBox) which assume five evenly spaced chips. The chips were
   inline-flex with variable text widths — "Xero" against "Payments"
   against "The sheet" — so none of them sat where its wire began.

   Fixed by making the assumption true rather than by measuring in JS: the
   chips now sit on an even five-column grid, each centred in its column,
   so the centres land at exactly 10 / 30 / 50 / 70 / 90 percent. In a 560
   viewBox that is 56 / 168 / 280 / 392 / 504 — the numbers already in the
   paths. No script, correct at every width.
   --------------------------------------------------------------------- */
.fm-sources{
  display:grid !important;
  grid-template-columns:repeat(5,1fr) !important;
  align-items:center;
  /* gap:0 on purpose. A gap shrinks each column and moves its centre inward,
     which put the chips 3px off their wires. With no gap the columns are
     exactly a fifth of the width and the centres land on 10/30/50/70/90 as
     the paths assume. The chips keep their own padding, so they still have
     air between them. */
  gap:0 !important;
}
.fm-node{
  justify-self:center;
  max-width:100%;
  white-space:nowrap;
}

/* Below 700px five chips cannot sit in a row without shrinking to nothing,
   so the grid gives way and the wires are dropped rather than pointing at
   the wrong places. The output panel takes a rule instead, which says the
   same thing in the space available. */
@media (max-width:700px){
  .fm-sources{
    display:flex !important;
    flex-wrap:wrap;
    justify-content:center;
  }
  .fm-lines{display:none}
  .fm-out{
    margin-top:1.4rem !important;
    border-top:2px solid var(--accent,#1FC0E7);
  }
}

/* ---------------------------------------------------------------------
   AS — THE ROUTE AS A JOURNEY.  2026-08-18.

   OT wanted the flow to read as a journey rather than five static boxes:
   each stage lights, the connector carries across to the next, and the
   three outputs arrive at the end.

   Sequenced entirely in CSS off a --s index on each stage and connector,
   so the order lives in the markup and no script is needed. It runs ONCE
   when the section reveals — a permanent loop mid-page is a distraction,
   and the site's rule is motion only where it carries meaning. Here the
   meaning is the order things happen in, which you only need shown once.

   Each stage holds its lit state briefly then settles back to a hairline,
   so what remains at rest is the calm diagram and what you saw was the
   path through it.
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion:no-preference){

  .route.reveal .rt-step{
    border-color:var(--line-soft,rgba(160,200,220,.12));
  }
  .route.reveal.in .rt-step{
    animation:rt-light 1.5s cubic-bezier(.3,.7,.3,1) forwards;
    animation-delay:calc(var(--s,0) * .42s + .15s);
  }

  /* the connector draws from the stage it leaves towards the one it feeds */
  .route.reveal .rt-link::before{transform:scaleX(0);transform-origin:left center}
  .route.reveal.in .rt-link::before{
    animation:rt-draw .42s cubic-bezier(.4,0,.2,1) forwards;
    animation-delay:calc(var(--s,0) * .42s + .48s);
  }
  /* the time sits on the connector and arrives with it */
  .route.reveal .rt-link span{opacity:0}
  .route.reveal.in .rt-link span{
    animation:rt-fade .4s ease forwards;
    animation-delay:calc(var(--s,0) * .42s + .62s);
  }

  /* the three outputs land last, one after another */
  .route.reveal .rt-out > div{opacity:0;transform:translateX(-6px)}
  .route.reveal.in .rt-out > div{
    animation:rt-arrive .5s cubic-bezier(.2,.7,.3,1) forwards;
    animation-delay:calc(1.85s + var(--o,0) * .13s);
  }
}

@keyframes rt-light{
  0%   {border-color:var(--line-soft,rgba(160,200,220,.12));
        box-shadow:0 0 0 0 rgba(31,192,231,0)}
  30%  {border-color:rgba(31,192,231,.85);
        box-shadow:0 0 0 3px rgba(31,192,231,.10)}
  70%  {border-color:rgba(31,192,231,.85);
        box-shadow:0 0 0 3px rgba(31,192,231,.10)}
  100% {border-color:var(--line-soft,rgba(160,200,220,.12));
        box-shadow:0 0 0 0 rgba(31,192,231,0)}
}
/* the Supabase stage is the one that holds your data, so it keeps its
   highlight rather than settling back with the others */
@keyframes rt-light-hot{
  0%   {border-color:var(--line-soft,rgba(160,200,220,.12))}
  30%  {border-color:rgba(31,192,231,.9)}
  100% {border-color:rgba(31,192,231,.34)}
}
.route.reveal.in .rt-hot{animation-name:rt-light-hot !important}

@keyframes rt-draw{to{transform:scaleX(1)}}
@keyframes rt-fade{to{opacity:1}}
@keyframes rt-arrive{to{opacity:1;transform:none}}

/* nothing may be invisible at rest for anyone who has asked for less motion */
@media (prefers-reduced-motion:reduce){
  .route .rt-link::before{transform:none}
  .route .rt-link span,.route .rt-out > div{opacity:1;transform:none}
}

/* ---------------------------------------------------------------------
   AT — TWO SECTIONS THAT LOOKED THE SAME NOW LOOK DIFFERENT.  2026-08-18.

   "How your data is protected" (six .t-item) and "How the figures earn
   trust" (three .v) were both boxed cards with an icon or numeral and a
   paragraph, one after the other, so the page appeared to say the same
   thing twice in the same shape.

   They are not the same kind of claim, so they should not be the same
   shape:

   - PROTECTION is a checklist. Six flat statements of equal weight, none
     more important than another, and you scan them looking for the one
     you care about. So: no boxes at all, a ruled grid, mark and text on
     one line. It reads like a spec sheet, which is what it is.

   - TRUST is an argument in three steps, each one building on the last —
     the platforms are vetted, THEN the figures are reconciled, THEN a
     human reads it. So: a numbered sequence, running vertically, with the
     number carrying the order.

   Neither invents a new component: the checklist borrows the ruled-cell
   grammar from the manifest on dashboards, the sequence borrows the
   numbered-track grammar from spreadsheets. Same vocabulary, applied to
   the shape of the argument rather than by default.
   --------------------------------------------------------------------- */

/* ---- protection: a spec sheet, not cards ---- */
.trust-grid{
  display:grid !important;
  grid-template-columns:repeat(3,1fr) !important;
  gap:0 !important;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
  margin-top:1.8rem;
}
.trust-grid .t-item{
  background:transparent !important;
  border:0 !important;
  border-bottom:1px solid var(--line-soft,rgba(160,200,220,.14)) !important;
  border-radius:0 !important;
  box-shadow:none !important;
  padding:1.5rem 1.6rem 1.5rem 0 !important;
  margin:0 !important;
  display:grid;
  grid-template-columns:1.35rem 1fr;
  gap:.85rem;
  align-items:start;
}
.trust-grid .t-item + .t-item{padding-left:1.6rem !important}
.trust-grid .t-item:nth-child(3n+1){padding-left:0 !important}
.trust-grid .t-item:not(:nth-child(3n+1)){
  border-left:1px solid var(--line-soft,rgba(160,200,220,.1)) !important;
}
.trust-grid .t-item svg{
  width:1.35rem;height:1.35rem;
  stroke:var(--accent,#1FC0E7);
  fill:none;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;
  margin:0 !important;
  grid-row:1 / span 2;
}
.trust-grid .t-item h3{
  margin:0 0 .3rem !important;
  font-size:.98rem !important;
  line-height:1.3 !important;
  color:var(--ink) !important;
}
.trust-grid .t-item p{
  margin:0 !important;
  grid-column:2;
  font-size:.88rem;
  line-height:1.55;
  color:var(--ink-quiet,#8f99ab);
}
@media (max-width:900px){
  .trust-grid{grid-template-columns:repeat(2,1fr) !important}
  .trust-grid .t-item:nth-child(3n+1){padding-left:1.6rem !important}
  .trust-grid .t-item:nth-child(2n+1){padding-left:0 !important}
  .trust-grid .t-item:not(:nth-child(3n+1)){border-left:0 !important}
  .trust-grid .t-item:nth-child(2n){border-left:1px solid var(--line-soft,rgba(160,200,220,.1)) !important}
}
@media (max-width:600px){
  .trust-grid{grid-template-columns:1fr !important}
  .trust-grid .t-item{padding-left:0 !important;border-left:0 !important}
}

/* ---- trust: a three-step argument, stacked ---- */
.vet{
  display:flex !important;
  flex-direction:column !important;
  gap:0 !important;
  margin-top:1.8rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
  max-width:78ch;
}
.vet .v.v-open{
  background:transparent !important;
  border:0 !important;
  border-bottom:1px solid var(--line-soft,rgba(160,200,220,.14)) !important;
  border-radius:0 !important;
  box-shadow:none !important;
  padding:1.5rem 0 1.5rem 3.6rem !important;
  display:block;
}
.vet .v.v-open > i{
  left:0 !important;
  top:1.6rem !important;
  font-size:1.25rem !important;
}
.vet .v.v-open > b{font-size:1.02rem;margin-bottom:.35rem}
.vet .v.v-open > p{max-width:64ch}
@media (max-width:600px){
  .vet .v.v-open{padding-left:2.6rem !important}
}

/* The deliverable block sat straight under the terms rail with no air, so
   the two read as one crowded unit. It now gets a rule and real space,
   which also marks it as the end of the section rather than more of it. */
.sa-example{
  margin-top:clamp(3.2rem,6vw,5rem) !important;
  padding-top:clamp(2.4rem,4vw,3.2rem);
  border-top:1px solid var(--line-soft,rgba(160,200,220,.14));
}

/* ---------------------------------------------------------------------
   AU — THE ROUTE: SLOWER, LOOPING, AND CONSISTENT.  2026-08-18.

   Three things OT caught, all correct:

   1. The times were on two of the four connectors, so the other two read
      as unexplained stray lines. The times are also not load-bearing —
      "overnight" is the point, not 06:00 — so all four connectors are now
      identical and the times are gone.

   2. The final connector fans into three, one per output, drawn the same
      way the hero flow draws its wires. Previously one rule pointed at a
      stack of three boxes and left the relationship implied.

   3. The sequence ran once and fast. It now loops on a long cycle and is
      roughly half the speed. A journey diagram that plays once is missed
      by anyone who scrolled past it; this one is slow enough to follow
      and quiet enough to ignore.
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion:no-preference){

  /* one full pass every 9s, so it reads as a system ticking over rather
     than an animation demanding attention */
  .route.reveal.in .rt-step{
    animation:rt-light 9s cubic-bezier(.3,.7,.3,1) infinite;
    animation-delay:calc(var(--s,0) * .85s);
  }
  .route.reveal.in .rt-hot{animation-name:rt-light-hot !important}

  .route.reveal.in .rt-link::before{
    animation:rt-sweep 9s cubic-bezier(.4,0,.2,1) infinite;
    animation-delay:calc(var(--s,0) * .85s + .5s);
  }

  /* the fan draws last, one strand at a time */
  .route.reveal .rt-fan path{stroke-dashoffset:1}
  .route.reveal.in .rt-fan path{
    animation:rt-fan-draw 9s cubic-bezier(.4,0,.2,1) infinite;
    animation-delay:calc(3.4s + var(--f,0) * .16s);
  }

  .route.reveal.in .rt-out > div{
    animation:rt-arrive-loop 9s cubic-bezier(.2,.7,.3,1) infinite;
    animation-delay:calc(3.7s + var(--o,0) * .16s);
  }
}

/* a lit state that holds, then releases, inside one 9s cycle */
@keyframes rt-light{
  0%,4%   {border-color:var(--line-soft,rgba(160,200,220,.12));
           box-shadow:0 0 0 0 rgba(31,192,231,0)}
  10%,44% {border-color:rgba(31,192,231,.8);
           box-shadow:0 0 0 3px rgba(31,192,231,.09)}
  56%,100%{border-color:var(--line-soft,rgba(160,200,220,.12));
           box-shadow:0 0 0 0 rgba(31,192,231,0)}
}
@keyframes rt-light-hot{
  0%,4%   {border-color:rgba(31,192,231,.34)}
  10%,44% {border-color:rgba(31,192,231,.9)}
  56%,100%{border-color:rgba(31,192,231,.34)}
}
/* the connector wipes across, holds, then clears */
@keyframes rt-sweep{
  0%,3%   {transform:scaleX(0)}
  9%,46%  {transform:scaleX(1)}
  58%,100%{transform:scaleX(0)}
}
@keyframes rt-fan-draw{
  0%,2%   {stroke-dashoffset:1}
  8%,44%  {stroke-dashoffset:0}
  56%,100%{stroke-dashoffset:1}
}
@keyframes rt-arrive-loop{
  0%,2%   {opacity:.45;transform:translateX(-4px)}
  9%,48%  {opacity:1;transform:none}
  60%,100%{opacity:.45;transform:translateX(-4px)}
}

/* ---- the fan itself ---- */
.rt-fan{
  position:relative;
  min-width:3.2rem;
  display:flex;
  align-items:stretch;
}
.rt-fan svg{
  width:100%;
  height:100%;
  overflow:visible;
}
.rt-fan path{
  fill:none;
  stroke:var(--accent,#1FC0E7);
  stroke-width:1.2;
  opacity:.7;
  vector-effect:non-scaling-stroke;
}

@media (prefers-reduced-motion:reduce){
  .route .rt-fan path{stroke-dashoffset:0}
  .route .rt-out > div{opacity:1;transform:none}
  .route .rt-link::before{transform:none}
}
@media (max-width:1100px){
  .rt-fan{min-height:2.4rem;min-width:0}
  .rt-fan svg{transform:rotate(90deg)}
}

/* AV (panel lights blue on arrival) removed 2026-08-18 — the panel keeps
   its grey hairline; the blue belongs to the trace alone. */

/* ---------------------------------------------------------------------
   AW — THE TRUST SEQUENCE'S FIRST RULE.

   The list had a top border from --line-soft and bottom borders per item
   in the accent, so the first line read grey and the rest blue. One rule
   set, one colour.
   --------------------------------------------------------------------- */
.vet{border-top-color:rgba(31,192,231,.28) !important}
.vet .v.v-open{border-bottom-color:rgba(31,192,231,.28) !important}

/* The fan's SVG was contributing its own intrinsic height (viewBox 200)
   to the grid row, which stretched every stage panel to ~660px. Taking it
   out of flow fixes that: the fan now fills whatever height the row is,
   rather than deciding it. */
.rt-fan{
  position:relative;
  min-width:3.4rem;
  display:block;
  align-self:stretch;
}
.rt-fan svg{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
}

/* The third card in the terms rail. Neutral ground so the price and the
   credit keep the visual weight — this one answers a question rather than
   making an offer. */
.sa-time{
  border:1px solid var(--line-soft,rgba(160,200,220,.12));
  border-radius:var(--r-md);
  padding:1.15rem 1.2rem;
  background:transparent;
}
.sa-time b{
  display:block;
  font-family:var(--display);
  font-size:1.55rem;
  font-weight:700;
  letter-spacing:-.025em;
  line-height:1;
  color:var(--ink);
  margin-bottom:.5rem;
}

/* AX (panel starts dark, then lights) removed 2026-08-18 — superseded:
   the panel never changes colour now. See AZ. */

/* ---------------------------------------------------------------------
   AY — THE ROUTE SHOWS DATA MOVING, NOT JUST BOXES LIGHTING.

   OT asked whether it could be better. The one thing it was not showing is
   direction: four panels lit in turn, but nothing travelled between them,
   so it read as a sequence of states rather than a pipeline.

   A single small dot now runs each connector as it opens. It is the only
   moving object on the page and it carries the one fact the diagram exists
   to state — that this goes one way, overnight, without anybody pushing it.
   --------------------------------------------------------------------- */
.rt-link{overflow:visible}
.rt-link::after{
  content:"";
  position:absolute;
  top:50%;
  left:0;
  width:5px;height:5px;
  margin-top:-2.5px;
  border-radius:50%;
  background:var(--accent,#1FC0E7);
  opacity:0;
  box-shadow:0 0 6px rgba(31,192,231,.8);
}
@media (prefers-reduced-motion:no-preference){
  .route.reveal.in .rt-link::after{
    animation:rt-travel 9s cubic-bezier(.45,0,.25,1) infinite;
    animation-delay:calc(var(--s,0) * .85s + .45s);
  }
}
@keyframes rt-travel{
  0%,3%   {left:0;opacity:0}
  6%      {opacity:1}
  13%     {left:100%;opacity:1}
  16%,100%{left:100%;opacity:0}
}

/* =====================================================================
   AZ — THE HERO FLOW.  Rewritten again 2026-08-19 to OT's sequence.

   Takes 1-5 each got one part right and one part wrong. Recorded so it is
   not re-litigated a seventh time:

     takes 1-3  looped the whole scene, so the chips and the box faded out
                and came back. He never asked for that.
     take  4-5  fixed that by making the wires draw ONCE and then sit
                static, so only the trace repeated. Also wrong: the wires
                stopping meant the sequence never restarted.

   THE SEQUENCE HE ASKED FOR, which is what this is:

     1. the wires draw out of the five chips
     2. they converge and land on the top centre of the box
     3. the moment they land, two blue lines leave that point
     4. they run left and right round the perimeter
     5. they meet at the bottom centre
     6. it holds, then the whole thing clears and runs again

   Throughout all six beats the five chips and the box STAY VISIBLE and the
   box keeps its grey hairline. The blue is the wires and the trace. The box
   never changes colour.

   One shared 7s cycle drives wires and trace so the handover at beat 3 can
   never drift: 0-23% draw, 23-46% trace, 46-69% hold, 69-80% clear,
   80-100% the reset before it runs again.
   ===================================================================== */
@media (prefers-reduced-motion:no-preference){

  /* the chips arrive once and then never move again */
  .flow-mini.reveal .fm-node{opacity:0;transform:translateY(-6px)}
  .flow-mini.reveal.in .fm-node{
    animation:fm-light .5s cubic-bezier(.2,.7,.3,1) forwards !important;
    animation-delay:calc(var(--i) * .11s) !important;
  }

  /* beat 1-2: the wires draw, every cycle */
  .flow-mini.reveal .fm-lines path{stroke-dashoffset:1}
  .flow-mini.reveal.in .fm-lines path{
    animation:fm-wire-cycle 7s cubic-bezier(.4,0,.25,1) infinite !important;
    animation-delay:.35s !important;
  }

  /* beat 3-5: the trace leaves the landing point and runs both ways */
  .flow-mini.reveal.in .fm-trace path{
    animation:fm-trace-cycle 7s cubic-bezier(.35,0,.3,1) .35s infinite;
  }

  /* nothing animates while it is off screen */
  .flow-mini.paused .fm-lines path,
  .flow-mini.paused .fm-trace path{animation-play-state:paused}
}

/* draw, hold while the trace runs, clear, reset */
@keyframes fm-wire-cycle{
  0%      {stroke-dashoffset:1;opacity:1}
  23%     {stroke-dashoffset:0;opacity:1}
  76%     {stroke-dashoffset:0;opacity:1}
  88%     {stroke-dashoffset:0;opacity:0}
  88.01%  {stroke-dashoffset:1;opacity:0}
  100%    {stroke-dashoffset:1;opacity:0}
}

/* holds off until 23% — the exact frame the wires land — then both halves
   leave the top centre together and meet at the bottom */
@keyframes fm-trace-cycle{
  0%,22.9%{stroke-dashoffset:1;opacity:0}
  23%     {stroke-dashoffset:1;opacity:1}
  46%     {stroke-dashoffset:0;opacity:1}
  76%     {stroke-dashoffset:0;opacity:1}
  88%     {stroke-dashoffset:0;opacity:0}
  88.01%  {stroke-dashoffset:1;opacity:0}
  100%    {stroke-dashoffset:1;opacity:0}
}

/* ---- the panel: grey, always. The blue never touches it ---- */
.flow-mini .fm-out{
  position:relative;
  border:1px solid var(--line-soft,rgba(160,200,220,.14)) !important;
  box-shadow:none !important;
}

/* ---- the trace overlay ---- */
.fm-trace{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  overflow:visible;
  pointer-events:none;
  z-index:1;
}
.fm-trace path{
  fill:none;
  stroke:var(--accent,#1FC0E7);
  stroke-width:2;
  stroke-linecap:round;
  vector-effect:non-scaling-stroke;
  opacity:0;
}
.fm-out > b,.fm-out > span{position:relative;z-index:2}

@media (prefers-reduced-motion:reduce){
  .fm-trace{display:none}
}

/* =====================================================================
   BA — THE ROUTE DIAGRAM: two defects found in the 2026-08-19 review.
   ===================================================================== */

/* 1. The connectors between the four stages were a 1px line at
      rgba(160,200,220,.14) over the navy panel — a measured 1.32:1, which
      is invisible. A section titled "the route your data takes" was
      rendering as four disconnected boxes.

      The track is now permanent and legible (3.3:1, above the 3:1 WCAG
      1.4.11 floor for a graphic that carries meaning) and no longer
      collapses to scaleX(0) for half of every cycle. The motion is carried
      by the travelling dot, which was always bright enough at 8.6:1 — it
      just had no visible rail to run along. */
.rt-link::before{
  background:rgba(160,200,220,.48) !important;
  transform:none !important;
  animation:none !important;
}

/* 2. The output panels sat at opacity 0 for the first 3.7s after the
      section scrolled into view, because the loop's animation-delay
      outran the reveal's base state. They now start visible and the loop
      floor is lifted, so Dashboard / Briefing / Findings are readable
      from the moment the diagram appears. */
@media (prefers-reduced-motion:no-preference){
  .route.reveal .rt-out > div{opacity:.55;transform:none}
  .route.reveal.in .rt-out > div{
    animation:rt-arrive-loop 9s cubic-bezier(.2,.7,.3,1) infinite;
    animation-delay:calc(1.1s + var(--o,0) * .16s);
  }
}
@keyframes rt-arrive-loop{
  0%,2%   {opacity:.55;transform:translateX(-3px)}
  9%,48%  {opacity:1;transform:none}
  60%,100%{opacity:.55;transform:translateX(-3px)}
}

/* =====================================================================
   BB — PLATFORM CARDS: READABLE ON HOVER.  2026-08-19.

   OT: "when you hover over and the thing goes white you can then not see
   the actual name of the platform because it's white."

   Exactly right, and it was every card. `.pcard` inherits a light-theme
   hover from ti-v9-polish that paints a cream fill through the padding-box
   layer:

       background: linear-gradient(#fdfcf8, #f8f6ef) padding-box,
                   var(--ring) border-box !important;

   The dark stylesheet leaves the label at rgb(234,242,247), so on hover the
   card became near-white text on a near-white card — a measured 1.08:1.
   The name and the caption both disappeared.

   The hover itself is worth keeping: the card lifting to light with its
   brand colour orbiting the border is the reward, and the logos are drawn
   for light backgrounds anyway. Only the ink was wrong. So the ink now
   flips with the surface.

   Deliberately NOT scoped to @media (hover:hover): the cream rule it is
   correcting is unscoped, so a touch device that latches :hover would
   otherwise get the cream card and the invisible text back.
   ===================================================================== */
.pcard:hover b,
.pcard:focus-visible b{color:#16202e !important}

.pcard:hover span,
.pcard:focus-visible span{color:#4c5665 !important}

/* the ink crossfades with the surface rather than snapping a frame early */
.pcard b,.pcard span{transition:color .35s var(--ease,cubic-bezier(.2,.7,.3,1))}

/* =====================================================================
   BC — PRICING CARDS: THE BUTTON AND THE FOOTER LINE, ALIGNED. 2026-08-19.

   OT: the Automations card's button and the rule under it were "really
   really squished", while the spreadsheet card had a big gap in the same
   place — and none of them lined up across the row.

   Measured, button-bottom to the footer rule:

       Spreadsheet Audit   96px      <- the big gap
       Automations         41px
       Systems Audit        0px      <- touching
       Dashboard            0px      <- touching

   Cause: `.foot` already had `margin-top:auto`, so it sat on the floor of
   every card and lined up correctly. `.go` did not — it had a fixed
   `margin-top:1.4rem` and simply followed the end of the list, so its
   position tracked how many bullets the card happened to have. Four
   different list lengths, four different gaps.

   The auto margin moves to `.go`, so the slack collects ABOVE the button
   instead of between the button and the rule. Both the buttons and the
   rules now sit on shared baselines across the row, with one deliberate
   gap between them.

   `.foot` keeps `margin-top:auto` for the Retainer, which is the one card
   with no button — that card's rule still needs to find the floor on its
   own. The `.go + .foot` sibling selector only overrides it where a button
   actually sits above.
   ===================================================================== */
.plan .go{margin-top:auto}
.plan .go + .foot{margin-top:1.9rem}

/* A floor under the list. `margin-top:auto` alone collects the slack above
   the button, but on the LONGEST card there is no slack, so the last bullet
   ended up 3px off the button — the same squash, moved to the other side of
   it. This guarantees breathing room on the tallest card; because the cards
   are grid siblings that stretch to the tallest, the whole row gains it. */
.plan ul{margin-bottom:1.5rem}

/* =====================================================================
   BD — THE STICKY HEADER WAS NOT STICKING.  2026-08-19.

   Found while checking that the two new deep links land clear of the
   header. `.site-head` is written as a sticky bar — `position:sticky;
   top:0; z-index:100` with a backdrop blur — but it computed to
   `position:relative` on all eight pages, so the nav scrolled away and
   never came back. On a 9,000px page that is a long walk back to the menu.

   Two separate causes, both needed fixing:

   1. Block 2898 above lifts content over the fixed decorative background
      with `main,.site-head,footer{position:relative;z-index:1}`. That is
      right for main and footer, but `.site-head` already sat above that
      layer on its own z-index:100 — including it here overwrote its
      `sticky` with `relative` for no gain.

   2. `body{overflow-x:hidden}` in ti-base-v12 makes the body a scroll
      container, and a sticky child sticks to its nearest scrolling
      ancestor rather than the viewport. `overflow-x:clip` clips exactly
      the same overflow WITHOUT creating that scroll container, which is
      what the rule wanted in the first place. `hidden` stays first as the
      fallback for anything that does not know `clip`.
   ===================================================================== */
.site-head{position:sticky !important;top:0;z-index:100}

body{
  overflow-x:hidden;
  overflow-x:clip;
}

/* =====================================================================
   BE — "WHAT A YEAR ON THE LOOP": THE HEAD FILLS THE ROW.  2026-08-19.

   The section head was a single 760px column inside a 1140px wrap, so a
   380 x 200px block of the row sat empty to the right of the heading, and
   another ~120px of nothing separated it from the timeline below.

   This is the one move worth stealing from the Linear reference OT
   supplied: their section heads run the heading down the left and the
   supporting sentence down the right, on a shared baseline, so the head
   occupies the full measure instead of hugging one side. It also gives
   this page the asymmetric block the brief asks for, without inventing a
   new component — the parts are already here, only the arrangement
   changes.
   ===================================================================== */
@media (min-width:900px){
  .year .head{
    max-width:none !important;
    display:grid;
    grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr);
    gap:clamp(2rem,4.5vw,4rem);
    align-items:end;
    margin-bottom:2.4rem !important;
  }
  /* the lede sits on the heading's last baseline rather than floating */
  .year .head .lede{
    margin-top:0 !important;
    max-width:44ch !important;
    padding-bottom:.35rem;
  }
}

/* =====================================================================
   BF — WHO BUILDS IT (who.html).  2026-08-19.

   The footer's "Who builds it · Oliver Thompson" pointed at a small card
   on the homepage. It now has a page.

   Three decisions worth recording:

   1. NO new page accent. The product doors each own a colour (xl gold,
      auto coral, dash turquoise, data cyan) but the trust pages do not,
      and this is a trust page. It takes the neutral palette with the gold
      hairline the pipeline page already uses, so it reads as part of that
      set rather than a sixth product.

   2. The portrait is cut out and desaturated rather than dropped in as
      supplied. The shoot is on a white seamless; placed as-is it would put
      a hard white rectangle in the middle of a dark page. Cut out, it sits
      IN the page.

   3. The quotes are staggered, not stacked. Four long quotes in a single
      column is exactly the "even vertical rhythm with nothing breaking the
      grid" the brief bans, so they alternate across a twelve-column field
      and overlap slightly in the gutter.
   ===================================================================== */

/* ---- hero ---- */
.who-hero{
  position:relative;
  padding:clamp(2.5rem,6vw,4.5rem) 0 0;
  overflow:hidden;
}
.who-grid{
  display:grid;
  grid-template-columns:minmax(0,1.06fr) minmax(0,.84fr);
  gap:clamp(1.5rem,5vw,4rem);
  align-items:end;
}
.who-intro{padding-bottom:clamp(2.5rem,6vw,5rem)}

.who-mark{
  display:block;width:2.4rem;height:2px;margin-bottom:1.1rem;
  background:var(--gold-bright,#c9a25c);border-radius:2px;
}
.who-hero h1{
  font-size:clamp(2.6rem,6.4vw,4.6rem);
  line-height:.98;
  letter-spacing:-.035em;
  margin:.55rem 0 0;
}
.who-role{
  margin-top:1rem;
  font-size:clamp(1rem,1.5vw,1.12rem);
  color:var(--ink);
  max-width:42ch;   /* 32ch broke it across three ragged lines */
}
.who-bio{
  margin-top:1.5rem;
  max-width:54ch;
  font-size:1rem;
  line-height:1.72;
}
.who-bio b{color:var(--ink);font-weight:600}

.who-links{display:flex;flex-wrap:wrap;gap:.7rem;margin-top:1.9rem}
.wlink{
  border:1px solid var(--line);
  border-radius:9px;
  padding:.68rem 1.05rem;
  font-size:.88rem;font-weight:500;
  color:var(--ink);
  transition:border-color .3s,color .3s,background-color .3s;
}
.wlink:hover{border-color:var(--gold-bright,#c9a25c);color:var(--gold-bright,#c9a25c)}

/* ---- the portrait ---- */
.who-photo{
  position:relative;
  margin:0;
  align-self:end;
  line-height:0;
}
.who-photo img{
  display:block;
  width:100%;
  height:auto;
  max-height:min(74vh,720px);
  object-fit:contain;
  object-position:bottom center;
  /* it is a cut-out on a dark ground, so it needs its own ground shadow
     or it looks pasted on */
  filter:drop-shadow(0 26px 46px rgba(0,0,0,.55));
}
/* a soft plinth so he is standing on something rather than floating */
.who-photo::after{
  content:"";
  position:absolute;
  left:6%;right:6%;bottom:-2px;height:120px;
  background:radial-gradient(60% 100% at 50% 100%, rgba(201,162,92,.10), transparent 72%);
  pointer-events:none;
}

/* ---- the quote stack ---- */
.words .head{max-width:62ch}
.qstack{
  display:grid;
  grid-template-columns:repeat(12,1fr);
  gap:clamp(1.2rem,2.4vw,2rem) clamp(1rem,2vw,1.6rem);
  margin-top:clamp(1.8rem,4vw,3rem);
}
.q{
  position:relative;
  margin:0;
  background:var(--card);
  border:1px solid var(--line);
  border-radius:18px;
  padding:clamp(1.7rem,2.8vw,2.4rem);
  box-shadow:0 22px 54px rgba(0,0,0,.22);
}
.q-a{grid-column:1 / span 8}
.q-b{grid-column:5 / span 8}

.q-label{
  display:block;
  font-size:.78rem;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;
  color:var(--gold-bright,#c9a25c);
  margin-bottom:1rem;
}
.q p{
  font-size:clamp(1.02rem,1.55vw,1.2rem);
  line-height:1.62;
  color:var(--ink);
}
.q cite{
  display:block;
  margin-top:1.2rem;
  font-style:normal;
  font-size:.86rem;
  color:var(--muted);
}
/* the opening mark, set as a quiet piece of furniture rather than a
   giant decorative glyph */
.q::before{
  content:"";
  position:absolute;top:0;left:clamp(1.7rem,2.8vw,2.4rem);
  width:2.2rem;height:2px;
  background:var(--gold-bright,#c9a25c);
  border-radius:0 0 2px 2px;
  opacity:.7;
}

@media (max-width:1000px){
  .q-a,.q-b{grid-column:1 / -1}
}
@media (max-width:900px){
  .who-grid{grid-template-columns:1fr;gap:0}
  .who-intro{padding-bottom:2rem;order:2}
  .who-photo{order:1;max-width:min(380px,74vw);margin:0 auto 1.5rem}
  .who-photo img{max-height:none}
  .who-hero h1{font-size:clamp(2.4rem,10vw,3.2rem)}
}

/* =====================================================================
   BG — THE HOMEPAGE FOUNDER BOX LINKS TO who.html.  2026-08-19.

   OT wanted the whole box to be the way through to the new page, photo
   included, and obviously so.

   Done as a stretched link rather than by wrapping the box in an <a>:
   the box already contains a mailto link, and an anchor inside an anchor
   is invalid HTML that browsers silently unpick. So one real link carries
   the destination and its ::after covers the card. The email is lifted
   above that overlay so it still works, and gets its own hover so it is
   visibly a separate target.
   ===================================================================== */
.tb-founder{position:relative}

.tb-more{
  display:inline-flex;align-items:center;gap:.4rem;
  margin-top:1.1rem;
  font-size:.9rem;font-weight:600;
  color:var(--green,#35c9a4);
  transition:gap .3s var(--ease,cubic-bezier(.2,.7,.3,1)),color .3s;
}
.tb-more span{transition:transform .3s var(--ease,cubic-bezier(.2,.7,.3,1))}
.tb-founder:hover .tb-more span{transform:translateX(4px)}

/* the whole card becomes the hit area */
.tb-more::after{
  content:"";
  position:absolute;
  inset:0;
  border-radius:inherit;
  z-index:0;
}

/* anything else clickable inside has to sit above that overlay */
.tb-founder .tb-mail{position:relative;z-index:1}
.tb-founder .tb-mail:hover{color:var(--green,#35c9a4)}

/* the card reads as interactive: the photo lifts and the box lights */
.tb-founder{
  cursor:pointer;
  transition:border-color .35s var(--ease,cubic-bezier(.2,.7,.3,1)),
             transform .35s var(--ease,cubic-bezier(.2,.7,.3,1)),
             box-shadow .35s var(--ease,cubic-bezier(.2,.7,.3,1));
}
.tb-founder:hover{transform:translateY(-3px)}
.tb-founder .tb-photo{transition:filter .4s ease, transform .4s ease}
.tb-founder:hover .tb-photo{transform:scale(1.02)}

/* keyboard users get the same affordance as the mouse */
.tb-more:focus-visible{outline:2px solid var(--green,#35c9a4);outline-offset:3px;border-radius:4px}

@media (prefers-reduced-motion:reduce){
  .tb-founder:hover{transform:none}
  .tb-founder:hover .tb-photo{transform:none}
  .tb-founder:hover .tb-more span{transform:none}
}

/* =====================================================================
   BH — THE PRIVACY PAGE.  2026-08-19.

   Four things OT reported, all confirmed by measurement first.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. "Two promises worth more than certificates" was unreadable.

   `.pp` was painting a cream light-theme gradient,
   linear-gradient(#fdfcf8, #faf8f1), while the dark theme kept the text
   at rgb(234,242,247). Measured: the heading at 1.10:1 and the body at
   2.10:1 against their own card. Effectively invisible.

   Same fault as the platform-card hover on the data page: a light-theme
   surface surviving into a dark page. Here the card should simply be a
   card, so it takes the same surface every other panel on this page uses.
   --------------------------------------------------------------------- */
.promise-pair .pp{
  background:var(--card,#12212f) !important;
  border:1px solid var(--line,rgba(160,200,220,.14));
  border-radius:16px;
  padding:1.6rem 1.5rem;
  box-shadow:0 18px 44px rgba(0,0,0,.24);
}
.promise-pair .pp b{color:var(--ink);display:block;margin-bottom:.55rem}
.promise-pair .pp span{color:var(--ink-dim)}
/* the left accent rule is the only colour it needs */
.promise-pair .pp{border-left:2px solid var(--green,#35c9a4)}

/* ---------------------------------------------------------------------
   2. "What I will never do": three cards stacked with gap:0, so they sat
   as one bunched slab with a hairline between, and each was collapsed to
   a title plus a "+". Three short promises do not need to be an
   accordion — the whole point is that they are read.

   Opened in the markup with .v-open, and given room here.
   --------------------------------------------------------------------- */
/* The three cards are marked .vet-cards on privacy.html, because .v-open
   already means something else. On the data page `.vet .v.v-open` is a flat
   numbered list — it strips the border, the radius and the background with
   !important and indents 57.6px for the numeral. Adding .v-open here to open
   the bodies therefore also stripped them back to bare rows.

   Rather than fight that rule, privacy's set gets its own hook and states
   the card treatment explicitly at higher specificity. */
.vet.vet-cards{
  gap:1rem !important;
  border-top:0 !important;
  margin-top:1.6rem !important;
}
.vet.vet-cards .v,
.vet.vet-cards .v.v-open{
  border:1px solid var(--line,rgba(160,200,220,.14)) !important;
  border-radius:16px !important;
  padding:1.75rem 1.6rem !important;
  background:var(--card,#12212f) !important;
  box-shadow:0 16px 40px rgba(0,0,0,.2);
}
.vet.vet-cards .v b{display:block;margin-bottom:.6rem;color:var(--ink)}
.vet.vet-cards .v p{margin:0}
/* the collapsed-accordion "+" has nothing to toggle on an open card */
.vet.vet-cards .v .s-ico,
.vet.vet-cards .v::after{display:none !important}

/* ---------------------------------------------------------------------
   3. The vault diagram, rebuilt as HTML (markup in privacy.html).

   Motion is deliberately quieter than the version it replaces: OT called
   the old one "so AI it's kind of stupid". The wire draws once on reveal
   and the one room that is yours holds a soft key-light. Nothing loops.
   --------------------------------------------------------------------- */
.vault2{
  background:var(--bg2,#0d1a26);
  border:1px solid var(--line-soft,rgba(160,200,220,.1));
  border-radius:20px;
  padding:clamp(1.5rem,3vw,2.4rem);
  margin-top:2rem;
}
.v-cap{
  font-size:.78rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--green,#35c9a4);margin-bottom:1.5rem;
}
.v2-flow{
  display:grid;
  grid-template-columns:minmax(0,1fr) auto minmax(0,1.15fr);
  gap:clamp(.8rem,2vw,1.6rem);
  align-items:center;
}

/* the source */
.v2-src{
  background:var(--card,#12212f);
  border:1px solid var(--line,rgba(160,200,220,.14));
  border-radius:14px;padding:1.2rem 1.1rem;
}
.v2-k{display:block;font-size:.8rem;font-weight:700;color:var(--ink);margin-bottom:.8rem}
.v2-src ul{list-style:none;display:flex;flex-direction:column;gap:.4rem;margin:0}
.v2-src li{
  font-size:.82rem;color:var(--ink-dim);
  background:var(--bg,#0a1420);
  border:1px solid var(--line-soft,rgba(160,200,220,.1));
  border-radius:7px;padding:.42rem .6rem;
}
.v2-src p{margin:.85rem 0 0;font-size:.76rem;color:var(--muted)}

/* the gate */
.v2-gate{display:flex;flex-direction:column;align-items:center;gap:.55rem;min-width:6.5rem}
.v2-wire{
  display:block;width:100%;height:1px;
  background:linear-gradient(90deg,transparent,var(--green,#35c9a4),transparent);
  opacity:.55;
}
.v2-shield{
  display:grid;place-items:center;
  width:3.1rem;height:3.1rem;border-radius:12px;
  border:1px solid rgba(53,201,164,.4);
  background:var(--card,#12212f);
}
.v2-shield svg{width:1.35rem;height:1.35rem;fill:none;stroke:var(--green,#35c9a4);
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.v2-gk{font-size:.74rem;color:var(--muted);text-align:center;line-height:1.35}

/* the rooms */
.v2-rooms{display:flex;flex-direction:column;gap:.6rem}
.v2-room{
  display:grid;
  grid-template-columns:auto 1fr;
  grid-template-areas:"ico name" "ico note";
  column-gap:.75rem;align-items:center;
  background:var(--card,#12212f);
  border:1px solid var(--line-soft,rgba(160,200,220,.1));
  border-radius:12px;padding:.9rem 1rem;
}
.v2-room .v2-ico{grid-area:ico;display:grid;place-items:center;width:1.6rem;height:1.6rem}
.v2-room .v2-ico svg{width:100%;height:100%;fill:none;stroke:var(--muted);
  stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.v2-room b{grid-area:name;font-size:.9rem;color:var(--muted);font-weight:600}
.v2-room .v2-note{grid-area:note;font-size:.76rem;color:var(--ink-dim);margin-top:.15rem}
/* The other tenants are deliberately dimmer than yours, but #7787a0
   measured 4.48:1 against the card and the floor is 4.5. Lifted to the
   nearest value that passes; still clearly recessive next to your own
   room, which is full --ink plus the green key. */
.v2-room:not(.is-yours) b{color:#7d8da6}

.v2-room.is-yours{
  border-color:rgba(53,201,164,.45);
  background:linear-gradient(180deg,rgba(53,201,164,.07),transparent), var(--card,#12212f);
  box-shadow:0 0 0 1px rgba(53,201,164,.08), 0 14px 34px rgba(53,201,164,.09);
}
.v2-room.is-yours b{color:var(--ink)}
.v2-room.is-yours .v2-ico svg{stroke:var(--green,#35c9a4)}

/* the wire draws once, then stops */
@media (prefers-reduced-motion:no-preference){
  .vault2.reveal .v2-wire{transform:scaleX(0);transform-origin:left center}
  .vault2.reveal.in .v2-wire{
    animation:v2-draw .9s cubic-bezier(.4,0,.2,1) .25s forwards;
  }
}
@keyframes v2-draw{to{transform:scaleX(1)}}

.v-foot{
  display:flex;flex-wrap:wrap;gap:.7rem;
  margin-top:1.6rem;padding-top:1.3rem;
  border-top:1px solid var(--line-soft,rgba(160,200,220,.1));
}
.v-foot span{
  font-size:.78rem;color:var(--ink-dim);
  border:1px solid var(--line-soft,rgba(160,200,220,.1));
  border-radius:8px;padding:.5rem .85rem;
}
.v-foot b{color:var(--green,#35c9a4);font-weight:600}

@media (max-width:900px){
  .v2-flow{grid-template-columns:1fr;gap:1rem}
  .v2-gate{flex-direction:row;justify-content:center;gap:.9rem;min-width:0}
  .v2-wire{width:2.5rem;height:1px;flex:0 0 auto}
  .v2-gk{text-align:left}
}

/* =====================================================================
   BI — THE AMBIENT BACKGROUND.  2026-08-19.  Final polish pass.

   OT: the pages are "very very plain in the background behind", and he
   wanted something either uniform or differing per category.

   It differs per category, because the site already has the vocabulary
   for that: each product door owns an accent (gold for spreadsheets,
   coral for automations, turquoise for dashboards, cyan for data), and
   the trust pages share the house green. The background now simply
   breathes in whatever colour the page is already speaking, so a visitor
   moving between doors feels the shift without being told about it.

   THREE CONSTRAINTS SHAPED THIS, and they are why it is so restrained:

   1. It must not read as decoration. The reference sites let a background
      speak in two places only, top and bottom, and never in the middle
      behind text. These gradients sit off the top-left and bottom-right
      corners and fade out well before the measure.

   2. It has to be cheap. OT's machine is 8GB and this runs on every page
      on every scroll. So: two composited layers, transform only, no JS,
      no canvas, no rAF, no repaint. A transform on a fixed layer is
      handed to the GPU and costs essentially nothing per frame.

   3. Motion guidance says animate one or two things per view, never
      everything. This is one thing, moving slowly enough (52s and 68s)
      that you register it as depth rather than movement.

   Layers are inset -25% so a drifting gradient can never pull its own
   edge into view.

   ON THE ALPHA VALUES (5% / 4% / 3%, and why not more):

   Once the sections stopped repainting the page colour, body text began
   sitting on the wash rather than on the flat ground — so the wash now
   affects real contrast. axe cannot see this: it reads the computed
   background token, not the pixel that ends up behind the glyph.

   Measured it directly by hiding all content and sampling the brightest
   wash pixel per page, then re-testing every body-text colour against
   THAT. At the first attempt (11%/9%/6%) `--muted` fell to **3.62:1** on
   the dashboards page, against a 4.5 floor. On the bare ground it is
   5.17:1, so the wash alone had broken it.

   These values keep the worst case near 4.8:1 with headroom for two
   layers overlapping. If anyone strengthens them later, re-run that
   measurement rather than trusting axe.
   ===================================================================== */

/* ---- layer A: the two corner washes, on body::before ---- */
/* GEOMETRY NOTE, because this caught me out: the layer is inset -25%, so
   the box is 150% of the viewport and the VIEWPORT occupies only the
   middle 25%-75% of it in both axes. A gradient placed at "20% 4%" of the
   box therefore lands well above and left of anything anybody can see.
   Positions below are expressed against the box with that in mind:
   28%/20% reads as just off the viewport's top-left, 72%/80% as just off
   its bottom-right. */
body::before{
  inset:-25% !important;
  background:
    radial-gradient(42% 30% at 28% 20%,
      color-mix(in srgb, var(--accent,#35c9a4) 5%, transparent), transparent 70%),
    radial-gradient(38% 27% at 74% 80%,
      color-mix(in srgb, var(--accent,#35c9a4) 4%, transparent), transparent 68%);
  will-change:transform;
}

/* ---- layer B: one wide, much fainter wash drifting the other way ----
   On main, not html. `--accent` is declared on `body.door-*`, and an
   html::before cannot inherit from body — it was picking up the :root
   default, so the data page's second layer was painting gold under a cyan
   first layer. main sits inside body and inherits correctly. */
main::before{
  content:"";
  position:fixed;
  inset:-25%;
  z-index:0;
  pointer-events:none;
  background:
    radial-gradient(34% 30% at 64% 44%,
      color-mix(in srgb, var(--accent,#35c9a4) 3%, transparent), transparent 72%);
  will-change:transform;
}

/* A browser without color-mix gets the house green rather than nothing.
   Declared first so a supporting browser simply overwrites it above. */
@supports not (background: color-mix(in srgb, red 5%, transparent)){
  body::before{
    background:
      radial-gradient(42% 30% at 28% 20%, rgba(53,201,164,.05), transparent 70%),
      radial-gradient(38% 27% at 74% 80%, rgba(53,201,164,.04), transparent 68%);
  }
  main::before{
    background:radial-gradient(34% 30% at 64% 44%, rgba(53,201,164,.03), transparent 72%);
  }
}

@media (prefers-reduced-motion:no-preference){
  body::before{animation:ti-drift-a 52s ease-in-out infinite alternate}
  main::before{animation:ti-drift-b 68s ease-in-out infinite alternate}
}

/* Amplitude is deliberately tiny. At 1440px these move the wash by about
   twenty pixels over the better part of a minute, which reads as the page
   being alive rather than as an animation playing. */
@keyframes ti-drift-a{
  from{transform:translate3d(-1.2%, -0.8%, 0) scale(1.02)}
  to  {transform:translate3d( 1.2%,  0.8%, 0) scale(1.06)}
}
@keyframes ti-drift-b{
  from{transform:translate3d( 1.6%,  1%, 0) scale(1.05)}
  to  {transform:translate3d(-1.6%, -1%, 0) scale(1.01)}
}

/* Stillness when asked for: the wash stays, the movement goes. */
@media (prefers-reduced-motion:reduce){
  body::before,main::before{animation:none;transform:none}
}

/* ---------------------------------------------------------------------
   BI (cont.) — LETTING THE WASH THROUGH.

   The ambient layer was invisible, and the reason was not the layer. Every
   section on the site paints `rgb(10,20,32)` as its own background-color —
   and that is the exact colour of the body underneath. Surveyed across
   five pages: thirteen distinct section classes, all painting the same
   value, none of them using a different one.

   So each was repainting the page colour on top of the page colour, which
   achieves nothing visually and blocks anything sitting behind. Clearing
   the colour lets the wash show through and changes nothing else.

   background-COLOR only. `.section.contact` carries a background-image and
   keeps it.
   --------------------------------------------------------------------- */
main .section,
main .sec-raised,
main .hero,
main .svc-hero,
main .builton,
main .scene,
main .counter-band,
main .contrast,
main .trustband,
main .magnet,
main .tiv9-washed,
main .contact,
main .route2,
main .year,
main .words,
main .who-hero{
  background-color:transparent !important;
}

/* ---------------------------------------------------------------------
   BI (cont.) — --muted lifted a step, #78899c -> #7f90a3.

   Once the wash showed through, the darkest ground on the site became
   rgb(17,37,45) — the dashboards page, where the pre-existing decorative
   dot layer already lifts the field before the wash adds to it. Measured
   on a blurred capture (the blur averages out the 1px dots, leaving the
   field a glyph actually sits on) `--muted` landed at **4.42:1**, just
   under the floor.

   Two ways out: weaken the wash further, or give the token the headroom it
   never really had. It was 5.17:1 on the bare ground before any of this,
   which is thin for the smallest text on the site.

   Lifted one step. Now 5.66:1 bare and 4.84:1 on the worst ground, so it
   passes everywhere with margin and secondary text is easier to read on
   every page, wash or no wash.
   --------------------------------------------------------------------- */
:root{--muted:#7f90a3}

/* =====================================================================
   BJ — THE BACKGROUND, PROPERLY.  2026-08-19.

   OT, after the first attempt: "you haven't done the subtle background
   animations at all, it's still looking blank". He was right, and the
   first attempt failed for a reason worth writing down.

   WHY IT FAILED. I put one wash behind the WHOLE page, including behind
   body copy. That meant every increase in strength cost text contrast, so
   I kept dialling it down until it measured safe — 5% — at which point it
   was invisible. I had optimised for the measurement instead of the ask.

   WHAT THE REFERENCE SITES ACTUALLY DO. Checked Linear, Vercel and Stripe:
   they run a strong, obvious aurora — big blurred colour blobs — but only
   behind heroes and closing bands, where the type is large and display
   weight. Behind body copy they run either nothing or a flat dot grid.
   The glow is loud precisely because it never sits under small text.

   So this splits into two jobs with different rules:

     - THE FIELD: a dot grid, sitewide, quiet, static. Safe anywhere
       because 1px dots do not meaningfully lift the ground behind a
       glyph. This is the "Vercel aesthetic" grid.

     - THE GLOW: an aurora, four times the strength of the old wash,
       moving, and scoped to .hero / .svc-hero / .who-hero / .cta-band
       only. Visible because nothing small is ever set on top of it.

   Block 4 of this file had flattened the site to a single ground and
   killed `.hero::after` outright, which is why the pages using .hero
   (home, data, pricing, pipeline, privacy, who) had nothing at all while
   the ones using .svc-hero kept their dots. That asymmetry is what OT was
   seeing. Both are restored and unified here.
   ===================================================================== */

/* ---- THE FIELD: dot grid, back on, and on every hero variant ---- */
.hero::after,.svc-hero::after,.who-hero::after{
  display:block !important;
  content:"";
  position:absolute;
  inset:0;
  z-index:0;
  pointer-events:none;
  background-image:radial-gradient(rgba(160,200,220,.17) 1px, transparent 1.5px) !important;
  background-size:28px 28px;
  /* fades out before it reaches the copy at the bottom of the hero */
  -webkit-mask-image:radial-gradient(78% 82% at 50% 18%, #000 0%, transparent 78%);
  mask-image:radial-gradient(78% 82% at 50% 18%, #000 0%, transparent 78%);
}

/* ---- THE GLOW: aurora, scoped to display-type areas only ---- */
.hero,.svc-hero,.who-hero,.cta-band{position:relative;overflow:hidden}
.hero > *,.svc-hero > *,.who-hero > *,.cta-band > *{position:relative;z-index:2}
/* AUDIT 2026-08-19: the blanket rule above overrode .hero-demo's absolute
   positioning, pushing the live panel 78.8px past the right edge of the
   viewport at 1440 (0% visible, hidden by .hero{overflow:hidden}). */
.hero .hero-demo{position:absolute}

.hero::before,.svc-hero::before,.who-hero::before{
  content:"";
  position:absolute;
  left:50%;
  top:-45%;
  width:130%;
  height:170%;
  margin-left:-65%;
  z-index:0;
  pointer-events:none;
  background:
    radial-gradient(30% 34% at 30% 34%,
      color-mix(in srgb, var(--accent,#35c9a4) 42%, transparent), transparent 68%),
    radial-gradient(26% 30% at 71% 26%,
      color-mix(in srgb, var(--accent,#35c9a4) 30%, transparent), transparent 66%),
    radial-gradient(24% 26% at 52% 62%,
      color-mix(in srgb, var(--accent,#35c9a4) 22%, transparent), transparent 64%);
  filter:blur(58px);
  opacity:.5;
  will-change:transform;
}

/* the closing band gets the same treatment, mirrored */
.cta-band::after{
  content:"" !important;
  position:absolute !important;
  left:50% !important;
  top:auto !important;
  bottom:-60% !important;
  width:120% !important;
  height:170% !important;
  margin-left:-60% !important;
  z-index:0 !important;
  pointer-events:none;
  background:
    radial-gradient(30% 32% at 38% 62%,
      color-mix(in srgb, var(--accent,#35c9a4) 38%, transparent), transparent 68%),
    radial-gradient(26% 28% at 66% 70%,
      color-mix(in srgb, var(--accent,#35c9a4) 26%, transparent), transparent 66%) !important;
  filter:blur(62px);
  opacity:.45;
  border-radius:0 !important;
  will-change:transform;
}

@supports not (background: color-mix(in srgb, red 5%, transparent)){
  .hero::before,.svc-hero::before,.who-hero::before{
    background:
      radial-gradient(30% 34% at 30% 34%, rgba(53,201,164,.42), transparent 68%),
      radial-gradient(26% 30% at 71% 26%, rgba(53,201,164,.30), transparent 66%);
  }
  .cta-band::after{
    background:radial-gradient(30% 32% at 38% 62%, rgba(53,201,164,.38), transparent 68%) !important;
  }
}

/* AUDIT 2026-08-19: removed. These ambient loops ran forever on layers the
   scrim at :6845 makes invisible. No ambient motion anywhere on the site. */

/* Slow and small. The blobs cross about a tenth of their own width over
   half a minute, which reads as light moving rather than as a loop. */
@keyframes ti-aurora{
  from{transform:translate3d(-4%, -2%, 0) scale(1)}
  to  {transform:translate3d( 4%,  3%, 0) scale(1.12)}
}
@keyframes ti-aurora-b{
  from{transform:translate3d( 5%,  2%, 0) scale(1.08)}
  to  {transform:translate3d(-5%, -2%, 0) scale(1)}
}

@media (prefers-reduced-motion:reduce){
  .hero::before,.svc-hero::before,.who-hero::before,.cta-band::after{
    animation:none;transform:none;
  }
}

/* ---------------------------------------------------------------------
   BJ (cont.) — TURNED UP.

   Both layers were applying correctly and still reading as blank, because
   two older rules were holding them down: ti-v9-polish sets
   `.hero::after{opacity:.4}`, which took the dot grid to 0.4 x 0.17 alpha
   — about 7% — and the aurora was left at half strength out of the same
   caution that made the first attempt invisible.

   The caution is not needed here. Nothing small is ever set on this: the
   aurora sits behind hero display type at clamp(2.2rem,4.6vw,3.6rem) in
   --ink, which is 18:1 on the darkest ground and large-text threshold
   anyway. So it goes to nearly full strength, which is what the reference
   sites actually run.
   --------------------------------------------------------------------- */
.hero::after,.svc-hero::after,.who-hero::after{
  opacity:1 !important;
  background-image:radial-gradient(rgba(160,200,220,.22) 1px, transparent 1.5px) !important;
}

.hero::before,.svc-hero::before,.who-hero::before{
  opacity:.95;
  background:
    radial-gradient(32% 36% at 28% 32%,
      color-mix(in srgb, var(--accent,#35c9a4) 62%, transparent), transparent 70%),
    radial-gradient(28% 32% at 73% 24%,
      color-mix(in srgb, var(--accent,#35c9a4) 45%, transparent), transparent 68%),
    radial-gradient(26% 28% at 50% 66%,
      color-mix(in srgb, var(--accent,#35c9a4) 30%, transparent), transparent 66%);
  filter:blur(64px);
}
.cta-band::after{
  opacity:.8 !important;
  background:
    radial-gradient(32% 34% at 36% 64%,
      color-mix(in srgb, var(--accent,#35c9a4) 55%, transparent), transparent 70%),
    radial-gradient(28% 30% at 68% 72%,
      color-mix(in srgb, var(--accent,#35c9a4) 38%, transparent), transparent 68%) !important;
}

/* ---------------------------------------------------------------------
   BJ (cont.) — THE GLOW MOVES TO THE EDGES, AND THE CENTRE GETS A SCRIM.

   Measured every text element in every hero against the pixels actually
   behind it, and two were genuinely failing:

     data.html   "Connecting your data"  2.75:1  on rgb(35,108,131)
     who.html    "Who builds it"         2.63:1  on rgb(103,96,74)

   Both are eyebrows, and the reason is structural rather than bad luck:
   the eyebrow is coloured with the page accent, and the aurora is made of
   the page accent, so it was accent on accent. Turning the glow down
   fixes it by making it invisible again, which is where this started.

   The reference sites solve it by geometry, not opacity: the bright parts
   sit at the OUTER EDGES of the hero and the middle stays dark, because
   the middle is where the words are. So the blobs move out to 8% and 92%,
   and a dark scrim is painted on top of them down the centre column.

   Background layers paint first-listed on top, so the scrim is listed
   first and the accent blobs sit underneath it.
   --------------------------------------------------------------------- */
.hero::before,.svc-hero::before,.who-hero::before{
  background:
    /* the scrim, on top: keeps the text column dark */
    radial-gradient(44% 56% at 50% 44%,
      rgba(10,20,32,.92) 0%, rgba(10,20,32,.72) 45%, transparent 76%),
    /* and the glow, pushed out to the corners */
    radial-gradient(30% 34% at 8% 30%,
      color-mix(in srgb, var(--accent,#35c9a4) 68%, transparent), transparent 70%),
    radial-gradient(28% 30% at 92% 22%,
      color-mix(in srgb, var(--accent,#35c9a4) 52%, transparent), transparent 68%),
    radial-gradient(26% 26% at 74% 82%,
      color-mix(in srgb, var(--accent,#35c9a4) 34%, transparent), transparent 66%);
}
.cta-band::after{
  background:
    radial-gradient(42% 54% at 50% 56%,
      rgba(10,20,32,.9) 0%, rgba(10,20,32,.68) 46%, transparent 76%),
    radial-gradient(30% 32% at 10% 62%,
      color-mix(in srgb, var(--accent,#35c9a4) 60%, transparent), transparent 70%),
    radial-gradient(28% 30% at 90% 70%,
      color-mix(in srgb, var(--accent,#35c9a4) 42%, transparent), transparent 68%) !important;
}

/* ---------------------------------------------------------------------
   BJ (cont.) — the scrim widened and deepened.

   The edge-plus-scrim geometry took the two failing eyebrows from 2.63:1
   and 2.75:1 to 4.98:1 and 4.05:1. Three were still just under the 4.5
   floor, all of them small accent-coloured labels sitting high in the
   hero, above where the scrim was strongest.

   So the scrim's centre moves up to 36% and its reach grows, which is
   where the eyebrow and headline actually sit. The blobs are unchanged:
   the glow at the edges is the part worth keeping and it is untouched.
   --------------------------------------------------------------------- */
.hero::before,.svc-hero::before,.who-hero::before{
  background:
    radial-gradient(56% 72% at 50% 36%,
      rgba(10,20,32,.97) 0%, rgba(10,20,32,.88) 42%, rgba(10,20,32,.55) 66%, transparent 82%),
    radial-gradient(30% 34% at 4% 30%,
      color-mix(in srgb, var(--accent,#35c9a4) 62%, transparent), transparent 70%),
    radial-gradient(28% 30% at 96% 22%,
      color-mix(in srgb, var(--accent,#35c9a4) 50%, transparent), transparent 68%),
    /* pulled out to the corner: at 78%/84% it sat directly under the
       spreadsheets hero caption, a 9.9px line that measured 4.28:1 */
    radial-gradient(24% 24% at 93% 94%,
      color-mix(in srgb, var(--accent,#35c9a4) 34%, transparent), transparent 66%);
}

/* ---------------------------------------------------------------------
   BJ (cont.) — the spreadsheets eyebrow gets its ink lifted.

   Last one under the floor, and the glow is not really at fault. The Excel
   green `#2aa260` measures 5.68:1 on the bare page, the thinnest headroom
   of any accent on the site, so the ~1.2 the aurora costs takes it to
   4.47:1.

   The door already separates `--accent` (marks, rules, fills) from
   `--accent-ink` (text), which is precisely the split this needs. Only the
   ink moves, to #32b06c: 6.67:1 bare and comfortably over the floor with
   the glow on top. The green used for marks is untouched, so nothing
   about the page's colour reads differently.
   --------------------------------------------------------------------- */
body.door-xl{--accent-ink:#32b06c}

/* ══════════════════════════════════════════════════════════════════════════
   AUDIT FIXES — 2026-08-19
   Appended last so these win the cascade without editing rules above.
   ══════════════════════════════════════════════════════════════════════════ */

/* FIX 11 — .hero had zero inline padding, so headline ink ran to the screen
   edge on the five pages that use it (index measured ink at x=0.0 at 375).
   .svc-hero / .who-hero pages were already safe because their content sits
   inside .wrap{width:min(1140px,92vw)}. */
@media (max-width:900px){
  .hero{padding-inline:15px}
  .hero > .wrap{width:100%}
  .hero .hero-inner{max-width:none;width:100%}
}

/* FIX 9+10 — duplicate and stranded header CTA at <=900px.
   index.html marks its header CTA .btn (hidden at :656). The other eight pages
   mark it .cta, which :382 forces to inline-flex, so they shipped BOTH the
   header pill and the in-drawer .tab-cta: two "Book a call" buttons 70.6px
   apart with the drawer open, and a 141.4px two-row header even with it shut.
   Match index: at <=900px the drawer's .tab-cta is the only header CTA. */
@media (max-width:900px){
  .site-head .wrap > .cta{display:none !important}
}

/* FIX — footer identity block. dxw.com and every credible professional-services
   site publishes who you are and how to reach a human. This is the direct answer
   to "a cold email with no contact details ... is this a spoofing attempt?" */
.ident{display:block;max-width:none;margin:0 0 .55rem;
  font-size:.82rem;line-height:1.75;color:var(--muted, #78899c)}
.ident a{color:inherit;border-bottom:1px solid rgba(255,255,255,.22)}
.ident a:hover{color:#fff;border-bottom-color:currentColor}
@media (max-width:620px){.ident{font-size:.8rem;line-height:1.9}}

/* FIX 12 — the real privacy notice. The page was a security marketing page with
   no controller identity, no lawful basis, no retention, no complaints route.
   This is the page a prospect's accountant opens before sending exports. */
.pn{max-width:78ch;margin-top:1.6rem}
.pn h3{font-size:1.06rem;margin:2rem 0 .55rem;color:var(--ink,#eef2f7)}
.pn h3:first-child{margin-top:0}
.pn p,.pn li{font-size:.95rem;line-height:1.75}
.pn ul{margin:.5rem 0 0 1.15rem;display:flex;flex-direction:column;gap:.45rem}
.pn a{color:var(--green-bright,#5fae83);border-bottom:1px solid rgba(95,174,131,.35)}

/* ══════════════════════════════════════════════════════════════════════════
   FIX 14 + 16 — the template feel.
   Nine pages opened with one identical treatment: a radial-gradient aurora
   (::before, a banned gradient hero background) under an identical dot grid
   (::after). Two heroes wearing nine hats.

   The aurora goes entirely — flat ground instead. The field becomes specific
   to each page's own subject, and three pages carry none at all so the
   presence of a field is itself a decision.
   ══════════════════════════════════════════════════════════════════════════ */

/* 1. kill the gradient hero background everywhere */
.hero::before,.svc-hero::before,.who-hero::before,.cta-band::after{
  background:none !important; opacity:0 !important; animation:none !important;
}

/* 2. reset the shared dot grid; each page opts back in below */
.hero::after,.svc-hero::after,.who-hero::after{
  background-image:none !important; opacity:0 !important;
}

/* 3. per-page fields, derived from what the page is about */
body.door-home .hero::after{                   /* the ledger the product reads */
  opacity:1 !important;
  background-image:
    linear-gradient(to right, rgba(160,200,220,.055) 1px, transparent 1px),
    linear-gradient(to bottom,rgba(160,200,220,.035) 1px, transparent 1px) !important;
  background-size:132px 34px !important;
  -webkit-mask-image:linear-gradient(100deg, transparent 34%, #000 82%);
          mask-image:linear-gradient(100deg, transparent 34%, #000 82%);
}
body.door-xl .svc-hero::after{                 /* spreadsheet cells */
  opacity:1 !important;
  background-image:
    linear-gradient(to right, rgba(201,162,92,.075) 1px, transparent 1px),
    linear-gradient(to bottom,rgba(201,162,92,.05) 1px, transparent 1px) !important;
  background-size:118px 30px !important;
  -webkit-mask-image:linear-gradient(105deg, transparent 40%, #000 88%);
          mask-image:linear-gradient(105deg, transparent 40%, #000 88%);
}
body.door-auto .svc-hero::after{               /* node lattice, wide gauge */
  opacity:1 !important;
  background-image:
    repeating-linear-gradient(90deg, rgba(244,115,143,.085) 0 1px, transparent 1px 168px),
    repeating-linear-gradient( 0deg, rgba(244,115,143,.05)  0 1px, transparent 1px 84px) !important;
  -webkit-mask-image:radial-gradient(62% 92% at 88% 46%, #000, transparent 72%);
          mask-image:radial-gradient(62% 92% at 88% 46%, #000, transparent 72%);
}
body.door-dash .svc-hero::after{               /* chart baselines */
  opacity:1 !important;
  background-image:repeating-linear-gradient(0deg,
    rgba(53,201,164,.07) 0 1px, transparent 1px 56px) !important;
  -webkit-mask-image:linear-gradient(#000 0 58%, transparent 92%);
          mask-image:linear-gradient(#000 0 58%, transparent 92%);
}
body.door-data .hero::after{                   /* two systems, joined */
  opacity:1 !important;
  background-image:repeating-linear-gradient(58deg,
    rgba(31,192,231,.06) 0 1px, transparent 1px 46px) !important;
  -webkit-mask-image:radial-gradient(70% 96% at 76% 50%, #000, transparent 74%);
          mask-image:radial-gradient(70% 96% at 76% 50%, #000, transparent 74%);
}
/* door-price, door-loop, door-privacy and who deliberately carry no field:
   flat ground, and the hairline below does the separating. */
body.door-price .hero,body.door-loop .hero,body.door-privacy .hero,.who-hero{
  border-bottom:1px solid rgba(160,200,220,.10);
}

/* FIX — the centred hero headlines had their inline max-width:22ch overridden to
   100%, so at 1300px they ran the full viewport width: a ~90 character measure,
   well past readable, and the reason pricing looked overscaled next to the rest.
   index escapes it only because .hero-inner constrains it. */
body.door-data .hero h1,
body.door-price .hero h1,
body.door-loop .hero h1,
body.door-privacy .hero h1{
  max-width:21ch !important;
  margin-inline:auto !important;
}
body.door-data .hero .sub,
body.door-price .hero .sub,
body.door-loop .hero .sub,
body.door-privacy .hero .sub,
body.door-data .hero .lede,
body.door-price .hero .lede,
body.door-loop .hero .lede,
body.door-privacy .hero .lede{
  max-width:58ch !important; margin-inline:auto !important;
}

/* FIX 2a — the closed nav panel kept visibility:visible, so its links stayed
   focusable behind a max-height:0 clip: 8 phantom tab stops, and Enter
   navigated. Paired with the `inert` attribute set in ti-v16.js. */
@media (max-width:900px){
  .tabs{visibility:hidden;transition:max-height var(--t-slow) var(--ease),visibility 0s linear var(--t-slow)}
  .tabs.open{visibility:visible;transition:max-height var(--t-slow) var(--ease),visibility 0s}
}

/* FIX — privacy's "What I will never do" cards.
   .vet.vet-cards sets grid-template-columns:repeat(3,1fr) but inherits
   display:flex; flex-direction:column from .vet, so the columns were ignored
   and the three cards stacked at 759px inside a 1140px measure — roughly 380px
   of dead width beside them, which is most of what reads as "empty space". */
.vet.vet-cards{
  display:grid !important;
  grid-template-columns:repeat(3,1fr) !important;
  flex-direction:initial !important;
  align-items:stretch !important;
  max-width:none !important;
  width:100% !important;
}
@media (max-width:900px){
  .vet.vet-cards{grid-template-columns:1fr !important}
}

/* ══════════════════════════════════════════════════════════════════════════
   CROSS-PAGE VIEW TRANSITIONS — 2026-08-19
   Nine separate HTML pages currently read as nine separate visits: white flash,
   header redraws, everything jumps. This makes navigating between them feel
   like one document, which is what a considered site feels like.

   Deliberately NOT ambient background animation. Every per-section background
   loop on this site was built and then removed because it read as generated,
   and it is on the banned list. Motion here carries meaning: it shows you that
   the header and the headline are the same objects moving, not new ones.

   Degrades to a normal navigation in browsers without support.
   ══════════════════════════════════════════════════════════════════════════ */
@view-transition{ navigation:auto }

::view-transition-old(root),
::view-transition-new(root){
  animation-duration:.28s;
  animation-timing-function:cubic-bezier(.2,.7,.2,1);
}

/* the header is the same object on every page, so it should not redraw */
.site-head{ view-transition-name:site-head }
::view-transition-group(site-head){ animation-duration:.24s }

/* the page identity: let the headline settle rather than blink */
main h1{ view-transition-name:page-title }
::view-transition-old(page-title){ animation:ti-vt-out .22s cubic-bezier(.4,0,1,1) both }
::view-transition-new(page-title){ animation:ti-vt-in  .3s cubic-bezier(.2,.7,.2,1) both }

@keyframes ti-vt-out{ to{ opacity:0; transform:translateY(-6px) } }
@keyframes ti-vt-in { from{ opacity:0; transform:translateY(8px) } }

@media (prefers-reduced-motion:reduce){
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*){ animation:none !important }
}

/* ══════════════════════════════════════════════════════════════════════════
   HERO FIELDS v2 — 2026-08-19
   The first pass was pitched so low it was invisible on a real screen: data and
   pricing read as flat black, index and spreadsheets barely showed. Roughly
   doubled, masks opened up, and the four pages that had nothing now have one.
   Every field is a straight ruled geometry derived from the page's own subject,
   aligned to a whole-pixel gauge so the lines stay crisp and never moire.
   ══════════════════════════════════════════════════════════════════════════ */

/* index — the ledger the product reads */
body.door-home .hero::after{
  opacity:1 !important;
  background-image:
    linear-gradient(to right, rgba(160,200,220,.13) 1px, transparent 1px),
    linear-gradient(to bottom,rgba(160,200,220,.085) 1px, transparent 1px) !important;
  background-size:132px 34px !important;
  -webkit-mask-image:linear-gradient(100deg, transparent 26%, #000 78%);
          mask-image:linear-gradient(100deg, transparent 26%, #000 78%);
}
/* spreadsheets — worksheet cells, gold to match the page accent */
body.door-xl .svc-hero::after{
  opacity:1 !important;
  background-image:
    linear-gradient(to right, rgba(201,162,92,.16) 1px, transparent 1px),
    linear-gradient(to bottom,rgba(201,162,92,.105) 1px, transparent 1px) !important;
  background-size:118px 30px !important;
  -webkit-mask-image:linear-gradient(105deg, transparent 30%, #000 84%);
          mask-image:linear-gradient(105deg, transparent 30%, #000 84%);
}
/* automations — a node lattice at n8n's own gauge */
body.door-auto .svc-hero::after{
  opacity:1 !important;
  background-image:
    repeating-linear-gradient(90deg, rgba(244,115,143,.16) 0 1px, transparent 1px 168px),
    repeating-linear-gradient( 0deg, rgba(244,115,143,.10) 0 1px, transparent 1px 84px) !important;
  -webkit-mask-image:radial-gradient(72% 100% at 84% 46%, #000, transparent 76%);
          mask-image:radial-gradient(72% 100% at 84% 46%, #000, transparent 76%);
}
/* dashboards — chart baselines */
body.door-dash .svc-hero::after{
  opacity:1 !important;
  background-image:repeating-linear-gradient(0deg,
    rgba(53,201,164,.13) 0 1px, transparent 1px 56px) !important;
  -webkit-mask-image:linear-gradient(#000 0 62%, transparent 94%);
          mask-image:linear-gradient(#000 0 62%, transparent 94%);
}
/* data — two systems being joined */
body.door-data .hero::after{
  opacity:1 !important;
  /* two rules crossing: the page is about two systems being joined. */
  background-image:
    repeating-linear-gradient( 58deg, rgba(31,192,231,.20) 0 1px, transparent 1px 54px),
    repeating-linear-gradient(-58deg, rgba(31,192,231,.11) 0 1px, transparent 1px 54px) !important;
  -webkit-mask-image:radial-gradient(104% 140% at 50% 34%, #000, transparent 82%);
          mask-image:radial-gradient(104% 140% at 50% 34%, #000, transparent 82%);
}
/* pricing — the columns of a price list */
body.door-price .hero::after{
  opacity:1 !important;
  background-image:repeating-linear-gradient(90deg,
    rgba(201,162,92,.13) 0 1px, transparent 1px 152px) !important;
  -webkit-mask-image:linear-gradient(#000 0 54%, transparent 92%);
          mask-image:linear-gradient(#000 0 54%, transparent 92%);
}
/* pipeline — a repeating cycle, ticking left to right */
body.door-loop .hero::after{
  opacity:1 !important;
  background-image:
    repeating-linear-gradient(90deg, rgba(95,174,131,.14) 0 1px, transparent 1px 96px),
    repeating-linear-gradient( 0deg, rgba(95,174,131,.07) 0 1px, transparent 1px 96px) !important;
  -webkit-mask-image:radial-gradient(96% 130% at 50% 30%, #000, transparent 74%);
          mask-image:radial-gradient(96% 130% at 50% 30%, #000, transparent 74%);
}
/* privacy — a closed grid, deliberately tighter and cooler than the rest */
body.door-privacy .hero::after{
  opacity:1 !important;
  background-image:
    linear-gradient(to right, rgba(160,200,220,.11) 1px, transparent 1px),
    linear-gradient(to bottom,rgba(160,200,220,.11) 1px, transparent 1px) !important;
  background-size:44px 44px !important;
  -webkit-mask-image:radial-gradient(70% 104% at 50% 26%, #000, transparent 72%);
          mask-image:radial-gradient(70% 104% at 50% 26%, #000, transparent 72%);
}
/* who — the quietest of the set; it sits behind a face */
body.door-who .who-hero::after{
  opacity:1 !important;
  background-image:repeating-linear-gradient(0deg,
    rgba(160,200,220,.10) 0 1px, transparent 1px 68px) !important;
  -webkit-mask-image:linear-gradient(96deg, #000 0 34%, transparent 72%);
          mask-image:linear-gradient(96deg, #000 0 34%, transparent 72%);
}
/* the hairline stays on the flat-ground pages that now carry a field too */
body.door-price .hero,body.door-loop .hero,body.door-privacy .hero,.who-hero{
  border-bottom:1px solid rgba(160,200,220,.10);
}

/* ══════════════════════════════════════════════════════════════════════════
   VISUAL AUDIT FIXES — 2026-08-19 (round 2)
   ══════════════════════════════════════════════════════════════════════════ */

/* V3 — between 901 and 1199px the tab row squeezed the brand: the logo
   compressed to 15x26 instead of 26x26 and the wordmark wrapped to two lines,
   taking the header to 57px. The mark must never shrink and the name must
   never wrap; the tab row gives up the space instead. */
.brand svg{flex:0 0 26px !important;width:26px !important;height:26px !important;min-width:26px}
.brand .word{white-space:nowrap}
@media (min-width:901px) and (max-width:1199px){
  .site-head .tabs{gap:1.05rem !important;font-size:.82rem !important}
  .site-head .tabs a{padding-inline:0 !important}
}

/* V1 — the .rt-fan connector painted straight through the cards below it.
   At <=1100px the svg is rotated 90deg, which makes a wide-short element paint
   tall, and `.rt-fan svg{overflow:visible}` let it escape: measured 170px of
   content in a 38px box at 375, striking through "Your questions, computed",
   "Claude writes the briefing" and "Checked before it ships". It is decorative
   and aria-hidden, so below 1100px it simply goes; the stacked cards read fine
   without a connector. Clipped at every width so it can never spill again. */
.rt-fan{overflow:hidden !important}
.rt-fan svg{overflow:hidden !important}
@media (max-width:1100px){
  .rt-fan{display:none !important}
}

/* V2 — three brand marks were effectively invisible. openai.svg and vercel.svg
   are fill="#000000" and zapier.svg is fill="#201515", sitting on a #0f1620
   card: Vercel read as a hole in the page. Rather than recolour someone else's
   trademark, the dark-fill marks get a light plate to sit on, which is how the
   brands are meant to be reproduced. Zapier keeps its orange, so it only needs
   the plate, not an inversion. */
.pcard img[src*="openai"],
.pcard img[src*="vercel"],
.pcard img[src*="notion"],
.pcard img[src*="zapier"],
.pcard img[src*="xero"]{
  background:#f3f0e8;
  border-radius:7px;
  padding:4px;
  box-sizing:border-box;
  opacity:1 !important;
}

/* V10 — Stripe, Gmail and Shopify are wide marks forced into a square slot.
   The chip rows use object-fit:contain; .pcard img did not, so a 512x214
   Stripe wordmark was squashed to 28x28 and read as a smudge. */
.pcard img{object-fit:contain !important}

/* V7 — .pgrid was a fixed repeat(5,1fr) whatever it held. Two of the four
   groups carry only 2 cards, so 61% of the row sat empty and left-aligned;
   the 7-card group stranded 2 on a second row. auto-fill with a sane track
   lets each group size itself, and the cards stay a consistent width. */
.pgrid{
  grid-template-columns:repeat(auto-fill,minmax(150px,196px)) !important;
  justify-content:start !important;
}
@media (max-width:1000px){
  .pgrid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr)) !important}
}

/* V5 — pipeline's year strip escaped its container. At >=920px .ymonths becomes
   a flex row of 12 x 196px cells (width:max-content = ~2546px) inside a 1140px
   .ybar with no clipping, so at 1024 "Month 5" was hard-cut at the viewport edge
   and at 1440 a month dot floated 130px past the end of the .ytrack hairline
   with no rail under it. Clip it, and fade the right edge so the cut reads as
   "this continues" rather than "this is broken". */
@media (min-width:920px){
  .ybar{overflow:hidden}
  .ybar::after{
    content:"";position:absolute;top:0;bottom:0;right:0;width:96px;
    pointer-events:none;z-index:3;
    background:linear-gradient(to right, rgba(10,20,32,0), var(--bg,#0a1420) 88%);
  }
  /* the rail must stop where the clip does, so nothing sits off the end of it */
  .ytrack,.yfill{max-width:100%}
}

/* V6 — the CTA band had no inline gutter below 375: measured ink at x=0 -> 375
   on data.html, letters touching both screen edges. Same family as the .hero
   fix earlier; .cta-band pages were missed because their content is not in .wrap. */
@media (max-width:620px){
  .cta-band{padding-inline:16px}
  .cta-band > .wrap{width:100%}
}

/* V11 / minor — nothing outside a data artefact should set below 12px. The
   mockups keep their own small type (that is what makes them read as software);
   real page copy does not. */
.ym span,.tagg,.ex-facts i,.ex-actions i{font-size:12px !important}

/* B1 — index hero headline was 208-254px right of every other section between
   1000 and 1279px. .hero-inner carries .wrap (margin:0 auto) but is capped to
   min(46%,640px) from 1000px up, so instead of sitting on the page gutter it
   centred itself in the viewport. Pin it to the same gutter the header uses. */
/* index overrides .wrap to min(1180px,92vw) at index.html:49, not the 1140px
   the rest of the site uses, so the gutter formula has to match that. */
@media (min-width:1000px) and (max-width:1279px){
  body.door-home .hero .hero-inner{
    margin-left:calc((100vw - min(1180px, 92vw)) / 2) !important;
    margin-right:0 !important;
  }
}

/* B1b — the 901-999px band had the opposite problem: the headline sat 21px
   LEFT of the header because .hero carries its own padding rather than the
   page gutter. Same gutter, applied the same way. */
@media (min-width:901px) and (max-width:999px){
  body.door-home .hero .hero-inner{
    margin-left:calc((100vw - min(1180px, 92vw)) / 2) !important;
    margin-right:0 !important;
    max-width:none !important;
  }
  body.door-home .hero{padding-inline:0}
}

/* B3 — "the engine · live demo" sat at the right end of the panel chrome, and
   the panel deliberately bleeds ~86px off the right edge, so the label was cut
   mid-word: only 40px of 113px on screen at 1440, reading "THE ENG". Move it
   next to the window dots, which is where a title belongs anyway. */
.hm-chrome{justify-content:flex-start !important;gap:.55rem}
.hm-chrome span{margin-left:.35rem}

/* B2 — the red fault pin is absolutely positioned at top:2px right:2px inside
   the cell, but the numbers are right-aligned, so it painted over the last
   glyph: "1,400" ran x1246-1280 with the pin at x1273-1286. Give the flagged
   cells room on the right so the value clears the badge. */
.xl-flag{padding-right:20px !important}
.xl-pin{right:3px}

/* B4 — the "Open the full report" pill landed on still-legible body text: the
   existing scrim only started fading at 62% depth, so the crop read as the
   document being obscured rather than deliberately faded out. Start the fade
   higher and take it further, so the pill sits on solid ground and the cut is
   clearly intentional. */
.ex-doc::after{
  height:56% !important;
  background:linear-gradient(180deg,
    rgba(10,20,32,0) 0%,
    rgba(10,20,32,.55) 42%,
    rgba(10,20,32,.93) 74%,
    rgba(10,20,32,.99) 100%) !important;
}
.ex-open{
  background:rgba(10,20,32,.9) !important;
  bottom:1.15rem !important;
}

/* ── MOBILE AUDIT 2026-08-20 · dashboard mock legibility ───────────────────
   The mock's labels were set in rem fractions tuned for a 1060px-wide desktop
   card: .46rem to .54rem, which land at 7.4px to 8.6px on a phone. The big
   figures (£48,210) read fine, which made it worse: a visitor saw numbers with
   nothing legible to say what they referred to. The questions and the verdicts
   ARE the product promise on this page, so they are the ones that had to grow. */
@media (max-width:720px){
  .dash-chrome span      {font-size:.62rem; letter-spacing:.10em}
  .dash-kpis span        {font-size:.60rem; letter-spacing:.10em}
  .dash-kpis s           {font-size:.62rem}
  .dash-q > b            {font-size:.64rem; letter-spacing:.13em}
  .dash-q .qr            {font-size:.82rem}
  .dash-q .qr u          {font-size:.66rem; letter-spacing:.06em}
  .dash-q .qr i          {font-size:.68rem}
  .dash-charts .dc .dc-lbl{font-size:.62rem; letter-spacing:.08em}
  .proof .pnum em        {font-size:.68rem}
}

/* iOS Safari zooms the whole viewport when a focused input is under 16px, and
   leaves the page zoomed and pannable after blur. The three checklist fields
   were 15.2px, so tapping "Your name" magnified the page and the visitor then
   fought the zoom through all three. This is the site's only lead form. */
@media (max-width:720px){
  #mfName, #mfEmail, #mfCompany{font-size:16px}
  #mfSubmit{font-size:15px}
}

/* On 404 and privacy the email and phone links sat inline in one paragraph:
   their boxes overlapped by 17px and the phone link's own centre hit-tested to
   the <p>, so tapping the obvious spot did nothing and tapping slightly off
   dialled instead of composing. Give each its own line and a real target. */
@media (max-width:720px){
  .nf-out a, .pn p a[href^="mailto:"], .pn p a[href^="tel:"]{
    display:inline-block; padding:.35rem 0; min-height:34px}
}

/* ── OT WALKTHROUGH 2026-08-20 · an email route beside the call route ──────
   His note: some people want to ask a question before they will book twenty
   minutes. "Book a call" stays the primary action; Email sits beside it as an
   outline, so it reads as the lighter of the two rather than a competitor.
   It is a plain mailto:, which means the existing chooser picks it up and
   offers Gmail, Outlook or the native app, same as every other address here. */
.head-mail{
  display:inline-flex; align-items:center; justify-content:center;
  height:38px; padding:0 16px; margin-right:10px;
  font-size:.86rem; font-weight:600; letter-spacing:.005em;
  color:var(--ink,#f3f0e8); text-decoration:none; white-space:nowrap;
  border:1px solid rgba(243,240,232,.26); border-radius:8px;
  background:transparent; transition:border-color .2s ease, background-color .2s ease;
}
.head-mail:hover{border-color:rgba(243,240,232,.55); background:rgba(243,240,232,.06)}
.head-mail:focus-visible{outline:2px solid var(--green,#35c9a4); outline-offset:3px}
@media (max-width:900px){ .head-mail{display:none} }   /* the drawer carries it instead */

/* drawer twin: same shape as .tab-cta but outlined, so the pair reads as
   two options rather than two primary buttons stacked */
.tabs .tab-cta.tab-mail{
  background:transparent; color:var(--ink,#f3f0e8);
  border:1px solid rgba(243,240,232,.26);
}
.tabs .tab-cta.tab-mail:hover{border-color:rgba(243,240,232,.55); background:rgba(243,240,232,.06)}
/* both header actions share the last grid column; .site-head .wrap is
   grid-template-columns:1fr auto 1fr, so a loose extra child was taking a
   column of its own and stretching the Email pill to 228px. */
.head-actions{display:flex;align-items:center;gap:0;justify-self:end}
@media (max-width:900px){ .head-actions{display:none} }

/* ══════════════════════════════════════════════════════════════════════════
   OT WALKTHROUGH 2026-08-20 · hero fields, three notes
   ══════════════════════════════════════════════════════════════════════════ */

/* 1. HOME — his note: "that grid at the top is literally the exact same as the
   spreadsheet one". Correct, and wrong for the page: a two-axis ruled grid IS a
   worksheet, which is one of four things he sells, not the whole business.
   Keep the worksheet on the spreadsheets page where it earns its keep. Home
   gets the rising line from the logo mark instead, widely spaced so it reads as
   direction rather than pattern. */
body.door-home .hero::after{
  background-image:
    repeating-linear-gradient(64deg, rgba(160,200,220,.115) 0 1px, transparent 1px 118px),
    repeating-linear-gradient(64deg, rgba(201,162,92,.075) 0 1px, transparent 1px 472px) !important;
  background-size:auto !important;
  -webkit-mask-image:radial-gradient(118% 128% at 76% 34%, #000 12%, transparent 78%) !important;
          mask-image:radial-gradient(118% 128% at 76% 34%, #000 12%, transparent 78%) !important;
}

/* 2. DASHBOARDS — his note: the lines and the dots collide and look sloppy.
   They were two separate systems drawn on top of each other: the hero's
   baselines, plus a full-page fixed .ambient layer carrying two drifting glows
   AND a moving dot field. Only this page loads it. Drop the dots, keep the
   glows as lighting rather than pattern, and let the baselines be the one
   figure on the page. */
body.door-dash .ambient .dots{display:none !important}
body.door-dash .ambient::before{opacity:.5}
body.door-dash .ambient::after{opacity:.4}
body.door-dash .svc-hero::after{
  background-image:
    repeating-linear-gradient(0deg, rgba(53,201,164,.15) 0 1px, transparent 1px 52px),
    repeating-linear-gradient(0deg, rgba(53,201,164,.055) 0 1px, transparent 1px 13px) !important;
  -webkit-mask-image:linear-gradient(#000 0 58%, transparent 92%) !important;
          mask-image:linear-gradient(#000 0 58%, transparent 92%) !important;
}

/* 3. WHO — his note: "get rid of those lines, just let my face do the talking."
   Agreed. It is the one page carrying a photograph; the field was competing
   with it for no gain. */
body.door-who .who-hero::after{display:none !important}
body.door-who .who-hero{border-bottom:1px solid rgba(160,200,220,.10)}

/* ── OT WALKTHROUGH round 2 · 2026-08-20 ──────────────────────────────────
   1. DASHBOARDS — "too much, too in your face, makes everything hard to read."
   Fair: I had just added a second finer gradient on top of the baselines,
   which doubled the line count behind the copy. Back to one set, wider gauge,
   roughly a third of the alpha, and faded out well before the text starts. */
body.door-dash .svc-hero::after{
  background-image:repeating-linear-gradient(0deg,
    rgba(53,201,164,.055) 0 1px, transparent 1px 88px) !important;
  -webkit-mask-image:linear-gradient(90deg, transparent 0 46%, #000 96%) !important;
          mask-image:linear-gradient(90deg, transparent 0 46%, #000 96%) !important;
}

/* 2. WHO — the hairline sat hard against the bottom of the photograph and
   read as a crop mark. Give the section room to finish. */
body.door-who .who-hero{
  padding-bottom:clamp(38px, 5vw, 68px);
  border-bottom:1px solid rgba(160,200,220,.07);
}
