/* ============================================================================
   TalkLogic — shared site stylesheet
   ----------------------------------------------------------------------------
   Copied verbatim out of `index.html`, which is the design record for the
   whole site (owner instruction, 2026-09-03). index.html keeps its own inline
   copy and is frozen — see docs/HERO-FROZEN.md and docs/DO-NOT-TOUCH.md. This
   file exists so the other seventeen pages share one shell instead of carrying
   seventeen drifting copies of it.

   If index.html and this file ever disagree, index.html is right.

   Line provenance in index.html at checkpoint/pre-subpages:
     tokens + base + primitives   23-117
     .tlink                      143-147
     section heads + registers   176-191
     header (liquid glass)       192-299
     footer                      596-604
     reveal + reduced motion     605-621
   The hero, the instrument, the services lists, the evidence thread, the method
   rail, pricing and the closing form are NOT here. They are landing-only.
   ============================================================================ */

/* ============================================================
   TOKENS — brand/tokens.css (Cobalt v1) verbatim, plus the light-surface
   derivations the kit does not carry yet. No hex below this block.
   ============================================================ */
:root{
  --tl-black:#000000;--tl-focus:#020203;--tl-panel:#0B0C10;--tl-panel-2:#111217;--tl-raise:#1A1D26;
  --tl-text:#F2F0F7;--tl-text-2:#D9DCE3;--tl-text-3:#B4BAC6;--tl-text-4:#8E96A5;--tl-text-5:#5C6470;
  --tl-accent:#2F6BFF;--tl-accent-hi:#6E97FF;--tl-accent-lo:#1F4FD6;--tl-accent-glow:rgba(47,107,255,.45);--tl-accent-ring:rgba(47,107,255,.25);--tl-on-accent:#FFFFFF;
  --tl-slate:#F8FAFC;--tl-navy:#0A2540;--tl-slate-text-2:#64748B;
  --tl-accent-on-light:#1F4FD6;--tl-accent-ink:#1F4FD6;   /* accent as a graphic on light · accent as text on light (≥4.5:1) */
  --tl-line-1:rgba(255,255,255,.06);--tl-line-2:rgba(255,255,255,.10);--tl-line-3:rgba(255,255,255,.16);--tl-line-sel:rgba(255,255,255,.60);
  --tl-glass:rgba(17,18,23,.45);--tl-glass-blur:blur(18px) saturate(135%);--tl-glass-edge:inset 0 1px 0 rgba(255,255,255,.08);
  --tl-shadow:0 24px 60px rgba(0,0,0,.5);
  --tl-font-display:'Space Grotesk',sans-serif;--tl-font-body:'Instrument Sans',sans-serif;--tl-font-mono:'DM Mono',monospace;
  --tl-track-display:-.035em;--tl-track-word:-.045em;--tl-track-mono:.12em;
  --tl-ease:cubic-bezier(.16,1,.3,1);--tl-ease-morph:cubic-bezier(.3,.9,.25,1);--tl-dur:.6s;--tl-dur-fast:.18s;
  /* derived for the light half of the page (proposed additions to tokens.css) */
  --tl-white:#FFFFFF;--tl-slate-2:#EEF1F5;--tl-navy-muted:#475569;
  --tl-line-light:#E2E8F0;--tl-line-light-soft:#EDF1F5;
  --dot-idle:#BBBBBB;--dot-hover:#D6D6D6;--dot-active:#717171;
  /* width system, from the reference landing */
  --wide:2160px;--gutter:clamp(1.5rem,3.6vw,4.5rem);
  --hdr-strip:76px;--hdr-bar:60px;--hdr-inset:16px;
  --z-content:1;--z-overlay:65;--z-nav:70;--z-modal:100;   /* 65 was in use as calc(--z-nav - 5); named so the scale is declared, not derived */
  --section:clamp(4rem,8vw,8rem);--section-sm:clamp(3rem,6vw,6rem);
  --hit:44px;--r:2px;
  --fs-xs:11px;--fs-sm:13px;--fs-md:15px;--fs-base:17px;--fs-lg:20px;
}
/* ============================================================ base */
*{box-sizing:border-box}
/* The chrome we did not draw still ships with the page. Left alone, a black
   hero renders a bright system scrollbar down its right edge, the caret in the
   closing form is a UA default, and any native control picks the light palette.
   color-scheme is the switch that fixes most of it in one declaration; the rest
   is themed off the same tokens as everything else. */
:root{color-scheme:dark;accent-color:var(--tl-accent);scrollbar-color:var(--tl-line-3) transparent;scrollbar-width:thin}
::-webkit-scrollbar{width:11px;height:11px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--tl-line-3);border-radius:var(--r);border:3px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:var(--tl-line-sel);background-clip:content-box}
/* the two white surfaces that can scroll take the light thumb instead — a
   16%-white thumb on a white panel is invisible */
.fly,#drawer{color-scheme:light;scrollbar-color:var(--tl-line-light) transparent}
.fly::-webkit-scrollbar-thumb,#drawer::-webkit-scrollbar-thumb{background:var(--tl-line-light);background-clip:content-box}
html{scroll-behavior:smooth;overflow-x:clip}
body{margin:0;background:var(--tl-black);color:var(--tl-text);font-family:var(--tl-font-body);font-size:var(--fs-base);line-height:1.6;-webkit-font-smoothing:antialiased;touch-action:manipulation;-webkit-tap-highlight-color:transparent}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;cursor:pointer}
h1,h2,h3{margin:0;font-family:var(--tl-font-display);text-wrap:balance}
p{margin:0;text-wrap:pretty}
ul,ol{margin:0;padding:0;list-style:none}
::selection{background:var(--tl-accent);color:var(--tl-on-accent)}
:focus-visible{outline:2px solid var(--tl-accent-ink);outline-offset:2px}
.on-dark :focus-visible,.on-dark:focus-visible{outline-color:var(--tl-white)}
/* The header is not an .on-dark section, so while its bar is dark over the hero
   its focus ring stayed --tl-accent-ink: 2.93:1 against that bar, under the 3:1
   WCAG 2.2 floor for focus indicators. A keyboard user tabbing the nav at the
   top of the page could not see where they were. White is 15.8:1 there. Once the
   bar turns light on scroll the accent ring is back and clears at 6.67:1. */
#hdr.dark :focus-visible{outline-color:var(--tl-white)}
[id]{scroll-margin-top:5.5rem}
.skip{position:absolute;left:-9999px;top:0;z-index:var(--z-modal);background:var(--tl-white);color:var(--tl-navy);padding:13px 16px;font-weight:600;font-size:var(--fs-md)}
.skip:focus{left:0}
main{position:relative;z-index:var(--z-content)}

/* ============================================================ type + primitives */
.wide{width:100%;max-width:var(--wide);margin-inline:auto;padding-inline:var(--gutter)}
.mono{font-family:var(--tl-font-mono);font-size:var(--fs-sm);letter-spacing:.08em;text-transform:uppercase;font-weight:500}
.num{font-variant-numeric:tabular-nums}
.h-section{font-size:clamp(1.5rem,2.7vw,2.5rem);font-weight:500;line-height:1.06;letter-spacing:-.02em}
.lead{font-size:var(--fs-lg);line-height:1.6;max-width:46ch}
/* buttons — sharp 2px corners, flat, the arrow slide carries the hover */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:var(--hit);border-radius:var(--r);padding:0 24px;font-size:var(--fs-md);font-weight:500;border:1px solid transparent;transition:background-color .2s var(--tl-ease),border-color .2s var(--tl-ease),color .2s var(--tl-ease);touch-action:manipulation}
.btn:active{transform:scale(.99)}
/* the submit button sets .disabled while the form posts; without this it looked
   identical to its resting state and still offered a pointer */
.btn:disabled{opacity:.55;cursor:not-allowed}
.btn:disabled .arr{transform:none}
.btn .arr{display:inline-block;transition:transform .2s var(--tl-ease)}
.btn:hover .arr{transform:translateX(3px)}
.btn-lg{min-height:56px;padding:0 32px;font-size:var(--fs-base)}
.btn-invert-ghost{background:transparent;color:var(--tl-white);border-color:rgba(255,255,255,.45)}
.btn-invert-ghost:hover{border-color:rgba(255,255,255,.8);background:rgba(255,255,255,.10)}
.btn-outline{background:transparent;color:var(--tl-navy);border:2px solid var(--tl-navy)}
.btn-outline:hover{background:var(--tl-slate)}
.btn-accent{background:var(--tl-accent-lo);color:var(--tl-on-accent);box-shadow:inset 0 1px 0 rgba(255,255,255,.12)}
.btn-accent:hover{background:var(--tl-accent)}
.chip{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border:1px solid var(--tl-line-light);border-radius:var(--r);font-family:var(--tl-font-mono);font-size:var(--fs-xs);letter-spacing:.1em;text-transform:uppercase;color:var(--tl-navy-muted);white-space:nowrap}
.live{position:relative;display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--tl-accent);box-shadow:0 0 10px var(--tl-accent-glow)}
.light .live,.light-alt .live{background:var(--tl-accent-on-light);}
/* the ping rides a pseudo-element on transform+opacity so it stays on the compositor;
   animating box-shadow spread repainted the halo every frame, and its keyframes also
   overrode the dot's resting 10px glow, which is why that glow never showed */
.live::after{content:"";position:absolute;inset:0;border-radius:inherit;box-shadow:0 0 0 6px var(--tl-accent-glow);animation:livepulse 2.4s ease-in-out infinite}
@keyframes livepulse{0%,100%{opacity:.85;transform:scale(.2)}50%{opacity:0;transform:scale(1)}}

/* ============================================================ cost of the problem (§2)
   The page's only external evidence — everything else here is a product spec or
   labelled sample data. Three numerals at the largest non-hero size on the page:
   the mid-page beat the body was missing, each on a hairline that rhymes with
   the hero's horizon. Sourcing stays visible; an uncited 21× reads as marketing,
   and the citation is what invites the reader to check instead of discount. */
.stats{display:grid;gap:40px}
/* Three columns used to start at 720, which put these paragraphs in 211px
   columns at a 768 tablet — 27 characters a line, four to five lines each. Two
   columns from 720 and three only from 1024 gives ~44ch at tablet. Three items
   in two columns leaves the third alone on its row, so it spans both. */
@media (min-width:720px){.stats{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:clamp(32px,4vw,64px)}.stats>li:last-child{grid-column:1/-1}}
@media (min-width:1024px){.stats{grid-template-columns:repeat(3,minmax(0,1fr))}.stats>li:last-child{grid-column:auto}}
.stat{display:flex;flex-direction:column;gap:12px;padding-top:20px;border-top:1px solid var(--tl-line-light)}
.stat b{font-family:var(--tl-font-mono);font-weight:500;font-size:clamp(2.25rem,4.4vw,3.5rem);line-height:1;letter-spacing:-.03em;color:var(--tl-navy);font-variant-numeric:tabular-nums}
.stat p{font-size:var(--fs-md);line-height:1.55;max-width:34ch}
.stat cite{font-style:normal;font-family:var(--tl-font-mono);font-size:var(--fs-xs);letter-spacing:.05em;line-height:1.5;color:var(--tl-navy-muted)}
/* was --tl-slate-text-2: 4.55:1 on slate at 11px, over the AA floor by 0.05 and
   the lowest ratio on the site. A citation is the one thing a sceptical reader
   goes looking for; it should not be the hardest thing to read. --tl-navy-muted
   takes it to 7.2:1 at no cost to the hierarchy. */

/* sections get a text link, never a second button — the page has one primary */
.tlink{display:inline-flex;align-items:center;gap:8px;align-self:flex-start;min-height:var(--hit);font-size:var(--fs-md);font-weight:500;color:var(--tl-accent-ink)}
.tlink .arr{transition:transform .2s var(--tl-ease)}
.tlink:hover .arr{transform:translateX(3px)}
.tlink:hover span:first-child{text-decoration:underline;text-underline-offset:4px}

/* section head — hairline across the container, h2 cols 1–6, lead cols 8–12 */
.sh{display:flex;flex-direction:column;gap:20px}
.sh .rule{height:1px;width:100%;background:var(--tl-line-light)}
.on-dark .sh .rule{background:var(--tl-line-3)}
.sh .grid{display:grid;gap:20px}
@media (min-width:1280px){.sh.split .grid{grid-template-columns:repeat(12,minmax(0,1fr));align-items:baseline;column-gap:56px}.sh.split h2{grid-column:1/span 6}.sh.split .lead{grid-column:8/span 5}}
section.s{position:relative;padding-block:var(--section)}
section.s-sm{padding-block:var(--section-sm)}
/* light / dark registers */
.light{background:var(--tl-white);color:var(--tl-navy)}
.light-alt{background:var(--tl-slate);color:var(--tl-navy)}
.light p,.light-alt p{color:var(--tl-navy-muted)}
.on-dark{background:var(--tl-panel);color:var(--tl-text)}

.on-dark p{color:var(--tl-text-3)}


/* ============================================================ header — liquid glass */
#hdr{position:sticky;top:0;z-index:var(--z-nav);height:var(--hdr-strip);padding:var(--hdr-inset) var(--hdr-inset) 0;transition:transform .3s var(--tl-ease);will-change:transform}
#hdr.hide{transform:translateY(calc(-1 * var(--hdr-strip)))}
/* nav sits next to the wordmark, not centred: `space-between` was spreading
   brand / nav / right-group across the bar, which put the links in the middle.
   flex-start groups them left and `.hdr-r` claims the remaining space. */
#hdr .bar{position:relative;height:var(--hdr-bar);border-radius:12px;padding:0 24px;display:flex;align-items:center;justify-content:flex-start;gap:16px;color:var(--tl-navy);background:rgba(255,255,255,.70);backdrop-filter:blur(20px) saturate(1.5);-webkit-backdrop-filter:blur(20px) saturate(1.5);box-shadow:0 4px 10px rgba(0,0,0,.06),inset 0 1px 0 rgba(255,255,255,.16);transition:background-color .7s var(--tl-ease-morph),color .7s var(--tl-ease-morph)}
#hdr.dark .bar{background:rgba(255,255,255,.10);color:var(--tl-white)}
#hdr.solid .bar{background:var(--tl-white);color:var(--tl-navy)}
.brand{display:inline-flex;align-items:center;gap:10px;min-height:var(--hit);font-family:var(--tl-font-display);font-weight:600;font-size:var(--fs-lg);letter-spacing:var(--tl-track-word)}
.brand svg{display:block;width:24px;height:24px}
#hdr:not(.dark) .brand svg path{stroke:var(--tl-accent-on-light)}
/* 2026-09-03, owner instruction, after the reference header: the hovered or
   open item sits on a tinted block, and the disclosure mark is a solid
   triangle that takes the accent and flips while its panel is open. Items
   carry 12px side padding, so the gap drops to 4px and the margin to 8px to
   keep the ink where it was. */
#nav{display:flex;align-items:center;gap:4px;margin-left:8px}
#nav a{font-family:var(--tl-font-display);font-size:var(--fs-md);font-weight:500;letter-spacing:-.01em;padding:9px 12px;min-height:var(--hit);display:inline-flex;align-items:center;border-radius:4px;opacity:.85;transition:opacity .18s ease,background-color .18s ease}
#nav a:hover{opacity:1;background:var(--tl-slate-2)}
#hdr.dark #nav a:hover{background:var(--tl-line-2)}
.hdr-r{display:flex;align-items:center;gap:12px;margin-left:auto}
.lang{display:inline-flex;align-items:center;gap:6px;min-height:var(--hit);padding:0 6px;border:0;background:none;font-family:var(--tl-font-mono);font-size:var(--fs-xs);letter-spacing:.08em;opacity:.7;transition:opacity .18s ease}
.lang:hover{opacity:1}
/* the CTA arrives on scroll. `visibility` has to be in the transition list or it
   snaps to hidden on the way out and cuts the fade; with it, showing is immediate
   and hiding waits out the full duration. The shorthand also has to restate the
   .btn colour transitions it overrides, or this one button's hover snaps. */
.hdr-cta{visibility:hidden;opacity:0;transform:translateY(-5px);transition:opacity .42s var(--tl-ease-morph),transform .42s var(--tl-ease-morph),visibility .42s,background-color .2s var(--tl-ease),border-color .2s var(--tl-ease),color .2s var(--tl-ease);min-height:var(--hit);padding:0 6px;font-size:var(--fs-md);border:0;color:inherit;background:none}
/* No box. A 2px-cornered rectangle inside a 12px-rounded glass bar always reads
   as foreign, because the container sets the corner language locally — and the
   two available fixes both break something: rounding the button adds a third
   radius to a system that has exactly two (2px controls, 12px surfaces), and
   sharpening the bar to match would carry into the drawer and both flyout
   panels. Removing the shape removes the conflict instead of negotiating it,
   and it matches the language toggle now sitting beside it. The arrow and the
   hover underline carry the affordance; the hero and every section below still
   have real buttons, so nothing is lost but a duplicate. */
.hdr-cta .arr{transition:transform .2s var(--tl-ease)}
.hdr-cta:hover{background:none}
.hdr-cta:hover span:first-child{text-decoration:underline;text-underline-offset:5px}
.hdr-cta:hover .arr{transform:translateX(3px)}
#hdr.scrolled .hdr-cta{visibility:visible;opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.hdr-cta{transform:none;transition:opacity .42s linear,visibility .42s}}
.burger{display:none;width:var(--hit);height:var(--hit);border-radius:var(--r);border:1px solid currentColor;background:none;flex-direction:column;gap:5px;align-items:center;justify-content:center}
.burger span{width:16px;height:1.5px;background:currentColor;transition:transform .22s var(--tl-ease)}
#hdr.open .burger span:first-child{transform:translateY(3.25px) rotate(45deg)}
#hdr.open .burger span:last-child{transform:translateY(-3.25px) rotate(-45deg)}
#drawer{display:none;position:absolute;left:var(--hdr-inset);right:var(--hdr-inset);top:calc(var(--hdr-strip) + 8px);border-radius:12px;background:var(--tl-white);color:var(--tl-navy);border:1px solid var(--tl-line-light);box-shadow:0 24px 64px -16px rgba(15,23,42,.2);padding:12px;flex-direction:column;gap:2px;overscroll-behavior:contain}
#hdr.open #drawer{display:flex}
#drawer a{display:flex;align-items:center;min-height:var(--hit);padding:0 12px;border-radius:var(--r);font-size:var(--fs-base);font-weight:500}
/* ============================================================ drawer groups
   Below 1024 both flyouts are display:none and this is the whole navigation.
   It carried six links and none of them reached an industry page, the chat
   page, the people page or the panel — 13 of 17 routes had no entry at all on
   a phone, which is the width most of this audience arrives at. The flyout's
   own destinations now live here as three collapsed groups: same hrefs, no new
   copy. Native <details> with a shared name, so one opens and the others close
   and the panel never grows past the screen.
   The drawer can now be taller than the viewport, so it scrolls. site.js's
   focus trap reads only visible links, so a collapsed group is skipped without
   it needing to know these exist. */
#drawer{max-height:calc(100svh - var(--hdr-strip) - 32px);overflow:auto}
#drawer .dgrp{border-radius:var(--r)}
#drawer .dgrp>summary{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:var(--hit);padding:0 12px;border-radius:var(--r);font-size:var(--fs-base);font-weight:500;cursor:pointer;list-style:none}
#drawer .dgrp>summary::-webkit-details-marker{display:none}
#drawer .dgrp>summary::after{content:"+";font-family:var(--tl-font-mono);font-size:var(--fs-lg);color:var(--tl-navy-muted);transition:transform .22s var(--tl-ease)}
#drawer .dgrp[open]>summary::after{transform:rotate(45deg)}
#drawer .dgrp>a{padding-left:24px;font-size:var(--fs-md);font-weight:400;color:var(--tl-navy-muted)}
#drawer .dgrp>a:hover{color:var(--tl-navy)}
#drawer .dgrp[open]{background:var(--tl-slate);padding-bottom:6px;margin-bottom:2px}
#drawer a:hover{background:var(--tl-slate)}
#drawer a.btn{margin-top:10px}
@media (max-width:1023px){#nav,.hdr-cta{display:none}.burger{display:flex}}

/* ---- mega-menu. Mechanics per docs/POSITIONING-v2.md §2.1: click toggles,
   Escape / the close button / the overlay dismiss, panel 4px under the strip,
   focus trapped while open, and below 1024 the drawer takes over. ---- */
.nav-t{display:inline-flex;align-items:center;gap:8px;padding:9px 12px;min-height:var(--hit);border:0;border-radius:4px;background:none;color:inherit;font-family:var(--tl-font-display);font-size:var(--fs-md);font-weight:500;letter-spacing:-.01em;opacity:.85;transition:opacity .18s ease,background-color .18s ease}
.nav-t:hover,.nav-t[aria-expanded="true"]{opacity:1;background:var(--tl-slate-2)}
#hdr.dark .nav-t:hover,#hdr.dark .nav-t[aria-expanded="true"]{background:var(--tl-line-2)}
/* solid triangle, 8 wide 5 tall, drawn with borders so it needs no glyph */
.chev{width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid currentColor;transform:translateY(1px);transition:transform .3s var(--tl-ease),border-top-color .18s ease}
.nav-t:hover .chev,.nav-t[aria-expanded="true"] .chev{border-top-color:var(--tl-accent-ink)}
#hdr.dark .nav-t:hover .chev,#hdr.dark .nav-t[aria-expanded="true"] .chev{border-top-color:var(--tl-accent)}
.nav-t[aria-expanded="true"] .chev{transform:translateY(0) rotate(180deg)}
/* The blur is animated, not just faded in. Holding backdrop-filter at 6px and
   ramping only opacity meant the page was already fully blurred on the first
   frame that had any opacity at all — it snapped. Animating the radius from 0
   lets the background settle out of focus over 800ms, and the dim rides with
   it on the same curve. */
/* The curve on the base rule governs CLOSING; the one on `.on` governs opening.
   Both used `--tl-ease-morph`, cubic-bezier(.3,.9,.25,1), which reaches 90% of
   its value in the first 30% of its time. Opening, that reads as responsive.
   Run backwards it un-blurs almost instantly and then crawls the last tenth,
   which is why closing felt abrupt while opening did not. Closing now uses a
   symmetric curve over a longer duration, so the blur holds a moment and then
   releases evenly. */
#fly-ov{position:fixed;inset:0;z-index:var(--z-overlay);background:rgba(11,12,16,.72);backdrop-filter:blur(0px);-webkit-backdrop-filter:blur(0px);opacity:0;transition:opacity .95s cubic-bezier(.45,.02,.45,1),backdrop-filter .95s cubic-bezier(.45,.02,.45,1),-webkit-backdrop-filter .95s cubic-bezier(.45,.02,.45,1)}
#fly-ov.on{opacity:1;backdrop-filter:blur(9px);-webkit-backdrop-filter:blur(9px);transition-duration:.8s;transition-timing-function:var(--tl-ease-morph)}
.fly{position:absolute;left:var(--hdr-inset);right:var(--hdr-inset);top:calc(var(--hdr-strip) + 4px);max-height:calc(100svh - var(--hdr-strip) - 24px);overflow:auto;overscroll-behavior:contain;border-radius:12px;background:var(--tl-white);color:var(--tl-navy);box-shadow:0 4px 10px rgba(0,0,0,.06),0 24px 64px -16px rgba(15,23,42,.28);padding:32px 24px;opacity:0;transform:translateY(6px);transition:opacity .7s cubic-bezier(.45,.02,.45,1),transform .7s cubic-bezier(.45,.02,.45,1)}
.fly.on{opacity:1;transform:none;transition-duration:.5s;transition-timing-function:var(--tl-ease)}
.fly:not(.on){pointer-events:none}
.fly-x{position:absolute;top:20px;right:20px;width:var(--hit);height:var(--hit);display:inline-flex;align-items:center;justify-content:center;border:0;background:none;color:var(--tl-navy-muted);border-radius:var(--r)}
.fly-x:hover{color:var(--tl-navy);background:var(--tl-slate)}
.fly-cols{display:grid;gap:4px;grid-template-columns:1fr}
/* Four columns, and the industry block takes two of them: ten industries in a
   single column runs taller than the panel, and the panel is full-bleed inside
   the header inset so there is width to spend. 64rem was leaving most of a
   1568px panel empty. */
@media (min-width:1024px){
  .fly-cols{grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;max-width:none}
  .fly-wide{grid-column:span 2}
  .fly-wide .two-up{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:28px}
}
/* the last row is the escape hatch for anyone who did not see themselves above */
.fly-col li a.last{color:var(--tl-accent-ink);font-weight:500}
.fly-col{padding:24px 28px;border-radius:12px;transition:background-color .2s ease}
.fly-col:hover{background:var(--tl-slate)}
.fly-h{display:inline-flex;align-items:center;gap:8px;font-family:var(--tl-font-display);font-size:var(--fs-lg);line-height:26px;font-weight:500;color:var(--tl-accent-ink);margin-bottom:12px}
.fly-h .arr{transition:transform .2s var(--tl-ease)}
.fly-h:hover .arr{transform:translateX(3px)}
.fly-col ul{list-style:none;margin:0;padding:0}
/* The menu rows underlined the whole label the instant the pointer arrived —
   text-decoration cannot be animated, so it was on or off. A short rule that
   draws itself from the left reads as the row responding to you rather than
   switching state. Kyndryl's menu is the reference: the mark is a fraction of
   the label, not its full width, so it points at the row instead of restating
   it. 24px, drawn in 700ms on the house easing, and inline-block so the rule
   sits under the text rather than under the whole column.
   scaleX is a compositor property; animating width would lay the row out on
   every frame. The global reduced-motion block clamps it to .01ms, so it
   arrives complete rather than moving for anyone who asked it not to. */
.fly-col li a{position:relative;display:inline-block;padding:6px 0;font-size:var(--fs-md);line-height:20px;color:var(--tl-navy-muted);transition:color .32s var(--tl-ease)}
.fly-col li a::after{content:"";position:absolute;left:0;bottom:2px;width:24px;height:1px;background:currentColor;transform:scaleX(0);transform-origin:left center;transition:transform .7s var(--tl-ease)}
.fly-col li a:hover{color:var(--tl-navy)}
.fly-col li a:hover::after,.fly-col li a:focus-visible::after{transform:scaleX(1)}

/* the page you are already on: stated, not styled into a button */
.fly-col li a.is-here,#nav a.is-here,#drawer a.is-here{color:var(--tl-navy);font-weight:600}
#hdr.dark #nav a.is-here{color:var(--tl-white)}
.fnav a.is-here{color:var(--tl-text)}
.fly-col li a.is-here::after{content:" ·";color:var(--tl-accent-on-light)}
html.fly-lock,html.fly-lock body{overflow:hidden}
/* P0, 2026-09-03. Opening either panel while scrolled put it off-screen.
   #hdr is position:sticky and both panels are position:absolute inside it, so
   they inherit whatever the header is doing. The panels' own scroll lock sets
   overflow:hidden on the root element — and a sticky element whose scrollport
   stops scrolling falls back to its static flow position, which for #hdr is the
   very top of the document. At 2500px down that puts the bar and the panel
   2500px above the viewport. The overlay is position:fixed, so it still covered
   the screen: the page dimmed, and nothing appeared.
   While a panel is open the header is pinned to the viewport instead of stuck to
   it, so the lock cannot move it. Both classes are already set by the JS —
   fly-open by the flyouts, open by the drawer — so nothing else changes. */
#hdr.fly-open,#hdr.open{position:fixed;top:0;left:0;right:0}
/* And the panels stop depending on the header's positioning at all. The values
   are unchanged — #hdr sits at viewport top whenever a panel is open, and its
   padding box starts at 0, so `top: hdr-strip + 4` measures the same distance
   from the viewport as it did from the header. Visually identical, and now
   immune to anything that moves #hdr. */
.fly{position:fixed}
#drawer{position:fixed}
/* the drawer needs the same lock the flyouts have. Without it the page scrolled
   0 -> 600 behind an open dialog, measured on every page. */
html.drawer-lock,html.drawer-lock body{overflow:hidden}
@media (max-width:1023px){.fly,#fly-ov{display:none}}
@media (prefers-reduced-motion:reduce){#fly-ov,.fly{transition:none}.fly{transform:none}#fly-ov{backdrop-filter:blur(9px);-webkit-backdrop-filter:blur(9px)}}

/* ============================================================ footer */
footer{position:relative;z-index:var(--z-content);background:var(--tl-black);color:var(--tl-text);border-top:1px solid var(--tl-line-2);padding:48px 0 calc(40px + env(safe-area-inset-bottom))}
.foot{display:flex;align-items:flex-start;justify-content:space-between;gap:48px;flex-wrap:wrap}
.fnav{display:flex;gap:32px;flex-wrap:wrap}
.fnav a,.flink{display:inline-flex;align-items:center;justify-content:center;min-width:var(--hit);min-height:var(--hit);font-size:var(--fs-md);color:var(--tl-text-3);transition:color .18s ease}
.fnav a:hover,.flink:hover{color:var(--tl-text)}
.fine{margin-top:32px;padding-top:24px;border-top:1px solid var(--tl-line-1);display:flex;flex-wrap:wrap;gap:12px 32px;font-family:var(--tl-font-mono);font-size:var(--fs-xs);letter-spacing:.12em;text-transform:uppercase;color:var(--tl-text-4)}



/* ============================================================ reveal */
.rv.armed{transition:opacity .7s var(--tl-ease),transform .7s var(--tl-ease);animation:rvfail .6s var(--tl-ease) 2.4s forwards}
.rv.armed:not(.on){opacity:0;transform:translateY(24px)}
.rv.rv-l.armed:not(.on){transform:translateX(-24px)}
.rv.rv-n.armed:not(.on){transform:none}
@keyframes rvfail{to{opacity:1;transform:none}}
.rv.armed.on{opacity:1;transform:none}
.stag>.rv:nth-child(2){transition-delay:.08s}.stag>.rv:nth-child(3){transition-delay:.16s}.stag>.rv:nth-child(4){transition-delay:.24s}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
  .rv,.rv.armed:not(.on){opacity:1;transform:none}
  .live::after{animation:none;opacity:0}
}

/* ============================================================ method rail */
.rail{display:grid;gap:32px;margin-top:56px;position:relative}
.ph,.go{display:flex;flex-direction:column;gap:10px}
.ph b{font-family:var(--tl-font-mono);font-weight:500;font-size:var(--fs-sm);letter-spacing:.06em;color:var(--tl-navy)}
.ph h3,.go h3{font-size:var(--fs-base);font-weight:600;letter-spacing:-.01em;font-family:var(--tl-font-body)}
.ph p,.go p{font-size:var(--fs-md);line-height:1.6}
.go b{font-family:var(--tl-font-mono);font-weight:500;font-size:var(--fs-sm);letter-spacing:.06em;color:var(--tl-accent-ink)}
@media (min-width:1024px){
  .rail{grid-template-columns:repeat(4,minmax(0,1fr));column-gap:40px}
  .rail::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;background:var(--tl-line-light)}
  .rail .ph{padding-top:26px;position:relative}
  .rail .ph::before{content:"";position:absolute;left:0;top:-3px;width:7px;height:7px;border-radius:50%;background:var(--tl-navy)}
  .rail .go{position:relative;padding-top:26px}
  .rail .go::before{content:"";position:absolute;left:-40px;right:0;top:0;height:2px;margin-top:-.5px;background:var(--tl-accent-on-light)}
  .rail .go .live{position:absolute;right:0;top:-3px}
}
@media (max-width:1023px){
  .rail{padding-left:22px;border-left:1px solid var(--tl-line-light)}
  .rail .ph{position:relative}
  .rail .ph::before{content:"";position:absolute;left:-25.5px;top:6px;width:7px;height:7px;border-radius:50%;background:var(--tl-navy)}
  .rail .go{position:relative;padding-top:20px;border-top:2px solid var(--tl-accent-on-light)}
  .rail .go .live{position:absolute;left:-25.5px;top:26px}
}


.pf{display:grid;gap:56px}
@media (min-width:1024px){.pf{grid-template-columns:repeat(12,minmax(0,1fr))}.pf>.pl{grid-column:1/span 5;position:sticky;top:6rem;align-self:start}.pf>.pr{grid-column:7/span 6}}
.faq{border-top:1px solid var(--tl-line-light)}
.faq details{border-bottom:1px solid var(--tl-line-light)}
.faq summary{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--hit);padding:18px 0;cursor:pointer;list-style:none;font-size:var(--fs-base);font-weight:500;letter-spacing:-.01em;color:var(--tl-navy)}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";flex:none;font-family:var(--tl-font-mono);font-size:var(--fs-lg);color:var(--tl-navy-muted);transition:transform .22s var(--tl-ease)}
.faq details[open] summary::after{transform:rotate(45deg)}
.faq summary{transition:color .18s ease}
.faq summary:hover{color:var(--tl-accent-ink)}
.faq summary:hover::after{color:var(--tl-accent-on-light)}
.faq p{padding:0 0 20px;max-width:62ch;font-size:var(--fs-md);line-height:1.65}


#book{background:var(--tl-black);color:var(--tl-white)}
.close-in{position:relative;max-width:820px;margin-inline:auto;display:flex;flex-direction:column;gap:22px;align-items:center;text-align:center}
.close-in h2{font-size:clamp(1.875rem,3.6vw,3.25rem);font-weight:500;line-height:1.1;letter-spacing:-.02em;max-width:18ch}
form{width:100%;max-width:560px;display:flex;flex-direction:column;gap:10px;align-items:stretch}
form label{text-align:left;color:var(--tl-text-4)}
.frow{display:flex;gap:8px}
#email{flex:1;min-width:0;min-height:52px;border:1px solid var(--tl-line-3);border-radius:var(--r);background:rgba(255,255,255,.04);padding:0 16px;font:inherit;font-size:var(--fs-base);color:var(--tl-white);transition:border-color .18s ease}
#email::placeholder{color:var(--tl-text-4);opacity:1}
#email{caret-color:var(--tl-accent)}
#email:focus{outline:none;border-color:var(--tl-accent);box-shadow:0 0 0 3px var(--tl-accent-ring)}
.frow.err #email{border-color:var(--tl-accent-hi)}
.fmsg{min-height:20px;font-size:var(--fs-sm);color:var(--tl-text-4);text-align:left}
.fmsg.bad{color:var(--tl-accent-hi)}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.done{display:none;flex-direction:column;gap:14px;align-items:center;padding:28px;border:1px solid var(--tl-line-3);border-radius:4px;max-width:560px;width:100%}
.done.show{display:flex}
.done .ok{width:44px;height:44px;border-radius:50%;background:var(--tl-accent-lo);color:var(--tl-on-accent);display:inline-flex;align-items:center;justify-content:center;font-size:var(--fs-lg)}
.done strong{font-size:var(--fs-lg);letter-spacing:-.02em;font-family:var(--tl-font-display);font-weight:500}
.done p{font-size:var(--fs-md);color:var(--tl-text-3);max-width:40ch}
@media (max-width:719px){.frow{flex-direction:column}.frow .btn{width:100%}}


/* the visually-hidden utility, index.html:418. Never display:none — a screen
   reader has to reach it. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}


/* ============================================================ icons
   Owner instruction, 2026-09-03: minimalist, lean, smooth, clean. One rule, and
   the markup carries nothing but geometry:

     <svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path d="…"/></svg>

   lean       1.5, not the 1.7 the landing's first set used. At 20px rendered
              that is a hair over one device pixel — present, never heavy.
   smooth     round caps and joins. The landing's first glyphs were butt/miter,
              which reads as cut-off at small sizes; round is what makes a
              stroke look drawn rather than clipped.
   clean      geometricPrecision, so a 24-unit grid does not get snapped onto
              the pixel grid and go lopsided at 18 and 20px.
   minimalist the constraint is on the drawing, not the CSS: three or four
              strokes, one idea, no second object to explain the first. If a
              glyph needs a fill, a second weight or a detail below 2 units to
              be legible, it is the wrong glyph.

   currentColor is not optional — it is how one glyph works on white, on slate
   and on panel without a variant. Never set a stroke colour on the element, and
   never load an icon through <img>: that breaks currentColor and the glyph
   renders the wrong colour on half the surfaces.

   No third-party set. Lucide, Heroicons and Feather all have their own grammar
   and it does not match this one. */
.ico{width:20px;height:20px;flex:none;display:block;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;shape-rendering:geometricPrecision}
.ico-sm{width:16px;height:16px}
.ico-lg{width:24px;height:24px;stroke-width:1.4}   /* larger box, same optical weight */
/* the accent is a graphic here, so it takes the graphic token, never the ink one */
.light .ico-accent,.light-alt .ico-accent{color:var(--tl-accent-on-light)}
.on-dark .ico-accent{color:var(--tl-accent-hi)}

/* ============================================================ page hero
   The landing's hero is a full-viewport canvas field and is frozen. A sub-page
   does not get one — it gets the same register (black ground, Space Grotesk at
   300, one outlined CTA, a mono eyebrow) at two thirds the height, so the
   header's dark state still has something to sit on and the page still opens on
   the brand rather than on a paragraph. Height is content-led with a floor, not
   a viewport unit: 100vh on a sub-page pushes the first real sentence below the
   fold for no reason. */
/* The landing's hero fills its right two thirds with a canvas field. A sub-page
   has no canvas — it is frozen and landing-only — so the same proportions left
   58-74% of every hero as empty black. Two changes rather than a decorative
   filler: the block is shorter, and the measure is wider, so what is left reads
   as margin instead of as a missing element. A per-page artefact in the right
   cell is the better answer and needs the owner to say what each page shows. */
#hero{background:var(--tl-black);color:var(--tl-text);padding-block:clamp(6rem,11vw,8.5rem) clamp(3.5rem,6vw,5rem);position:relative}
/* .ph-in constrains its CHILDREN, not itself. Putting max-width on the same
   element as .wide let .wide's margin-inline:auto centre the copy column, and
   the hero opened 260px right of the gutter every other section sits on. This
   way the class is correct whether it lands on .wide or inside it. */
.ph-in{display:flex;flex-direction:column;gap:24px;align-items:flex-start}
.ph-eyebrow{font-family:var(--tl-font-mono);font-size:var(--fs-sm);letter-spacing:.12em;text-transform:uppercase;color:var(--tl-text-4)}
.ph-in h1{font-family:var(--tl-font-display);font-weight:300;font-size:clamp(2.25rem,5.2vw,4rem);line-height:1.02;letter-spacing:var(--tl-track-display);text-wrap:balance;max-width:20ch}
.ph-in .lead{color:var(--tl-text-3);max-width:52ch}
#hero::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--tl-line-2)}
/* index.html gives #hero both the id override and class="on-dark", so the
   register supplies the paragraph colour and the white focus ring. Repeating the
   two rules here means a page that writes only the id still gets both — the ring
   in particular is a 3:1 WCAG floor, not a preference. */
#hero p{color:var(--tl-text-3)}
#hero :focus-visible{outline-color:var(--tl-white)}

/* ============================================================ feature list
   The services pattern from the landing, generalised: a hairline, a numeral or
   a glyph, a heading and one paragraph. No cards, no lifts — the rule carries
   the structure. */
.feat{display:grid;gap:36px;margin-top:48px}
@media (min-width:720px){.feat{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:clamp(32px,4vw,64px)}}
/* three items in two columns leave the third alone on its own row, which reads
   as a mistake rather than a list. Same answer .stats already uses: the odd one
   spans. Measured at 21 of 36 grids before this. */
@media (min-width:720px) and (max-width:1023px){.feat>li:nth-child(3):last-child{grid-column:1/-1}}
/* .three moves to 1024, not 1280: between the two the row was two-up with an
   orphan on every page that carried it */
@media (min-width:1024px){.feat.three{grid-template-columns:repeat(3,minmax(0,1fr))}.feat.three>li:nth-child(3):last-child{grid-column:auto}}
.feat li{display:flex;flex-direction:column;gap:10px;padding-top:20px;border-top:1px solid var(--tl-line-light)}
.on-dark .feat li{border-top-color:var(--tl-line-3)}
.feat h3{font-family:var(--tl-font-body);font-size:var(--fs-base);font-weight:600;letter-spacing:-.01em}
.feat p{font-size:var(--fs-md);line-height:1.6;max-width:44ch}
.feat .k{font-family:var(--tl-font-mono);font-size:var(--fs-sm);font-weight:500;letter-spacing:.06em;color:var(--tl-accent-ink)}
.on-dark .feat .k{color:var(--tl-accent-hi)}

/* ============================================================ scenes
   The strongest writing on the site is the timestamped triptych — 08:40, a queue
   at the window, the fourth caller hearing a busy tone. It rendered as .feat:
   the same three-up grid as the feature list directly beneath it, so the best
   content on the page looked like the most ordinary. It gets its own form — the
   time in a mono column on the left, the scene to its right, a hairline between.
   Editorial, and unmistakably not a card grid. */
.scenes{display:grid;gap:0;margin-top:48px;border-top:1px solid var(--tl-line-light)}
.on-dark .scenes{border-top-color:var(--tl-line-3)}
.scenes>li{display:grid;gap:8px;padding:28px 0;border-bottom:1px solid var(--tl-line-light)}
.on-dark .scenes>li{border-bottom-color:var(--tl-line-3)}
.scenes .k{font-family:var(--tl-font-mono);font-size:var(--fs-sm);font-weight:500;letter-spacing:.06em;color:var(--tl-accent-ink);white-space:nowrap}
.on-dark .scenes .k{color:var(--tl-accent-hi)}
.scenes h3{font-family:var(--tl-font-body);font-size:var(--fs-base);font-weight:600;letter-spacing:-.01em}
.scenes p{font-size:var(--fs-md);line-height:1.6;max-width:58ch}
@media (min-width:768px){
  .scenes>li{grid-template-columns:7.5rem minmax(0,1fr);column-gap:clamp(28px,3.4vw,56px);row-gap:8px;align-items:baseline}
  .scenes .k{grid-row:1/span 2}
  .scenes h3,.scenes p{grid-column:2}
}

/* ============================================================ feat, split
   A modifier, not a second component: same markup, one extra class. Where a page
   ran two .feat grids back to back the second takes .split and becomes full-width
   rows with the heading held left against its paragraph. Ten of sixteen pages
   were running the identical three-up grid two or three times in a row, which is
   what made the site read as one page repeating. */
@media (min-width:1024px){
  .feat.split{grid-template-columns:minmax(0,1fr);gap:0;border-top:1px solid var(--tl-line-light)}
  .on-dark .feat.split{border-top-color:var(--tl-line-3)}
  .feat.split>li{grid-column:auto;display:grid;grid-template-columns:minmax(0,4fr) minmax(0,7fr);column-gap:clamp(32px,4.5vw,72px);align-items:baseline;padding:30px 0;border-top:0;border-bottom:1px solid var(--tl-line-light)}
  .on-dark .feat.split>li{border-bottom-color:var(--tl-line-3)}
  .feat.split h3{grid-column:1}
  .feat.split p{grid-column:2;grid-row:1/span 2;max-width:62ch}
}

/* ============================================================ log
   The panel page's one artefact. It was 34 inline style attributes — more than
   the frozen index.html carries in the whole file — and a flex row that wrapped
   mid-table on a phone: 4.5em + 7.5em + 3em of minimums plus the outcome text
   came to ~415px against 342px of content width at 390.
   A grid instead: three sized columns and one that takes the rest, so the row
   breaks where it is told to. Below 560 it stacks into label/value pairs rather
   than wrapping into an unreadable ragged block. */
.log{margin:56px 0 0}
.log .hd,.log li{display:grid;grid-template-columns:4.5em 8em 3em minmax(0,1fr);gap:4px 18px;font-family:var(--tl-font-mono);font-size:var(--fs-sm);font-variant-numeric:tabular-nums}
.log .hd{color:var(--tl-text-4);padding-bottom:12px;border-bottom:1px solid var(--tl-line-3)}
.log ul{color:var(--tl-text-3)}
.log li{padding:14px 0;border-bottom:1px solid var(--tl-line-2)}
.log li .t{color:var(--tl-text)}
.log li .out{color:var(--tl-text-3)}
.log li .out.ok{color:var(--tl-accent-hi)}
.log figcaption{display:flex;align-items:center;flex-wrap:wrap;gap:10px 14px;padding-top:18px;font-size:var(--fs-sm);color:var(--tl-text-4)}
.log .chip{border-color:var(--tl-line-3);color:var(--tl-text-3)}
@media (max-width:559px){
  .log .hd{display:none}
  .log li{grid-template-columns:auto 1fr;row-gap:2px;padding:16px 0}
  .log li>*::before{content:attr(data-l) " ";color:var(--tl-text-5)}
  .log li>*{grid-column:1/-1}
}

/* ============================================================ prose
   Legal pages and anything else that is genuinely a document. One column, the
   body face, no display type below the h1 — a privacy policy that tries to look
   designed reads as though it is hiding something. */
.prose{max-width:68ch;display:flex;flex-direction:column;gap:28px}
.prose h2{font-size:clamp(1.25rem,2vw,1.625rem);font-weight:500;letter-spacing:-.015em;margin-top:12px}
.prose h3{font-family:var(--tl-font-body);font-size:var(--fs-base);font-weight:600}
.prose p,.prose li{font-size:var(--fs-md);line-height:1.7}
.prose ul{display:flex;flex-direction:column;gap:10px;padding-left:22px;list-style:disc}
.prose ul li::marker{color:var(--tl-accent-on-light)}
.prose .updated{font-family:var(--tl-font-mono);font-size:var(--fs-xs);letter-spacing:.1em;text-transform:uppercase;color:var(--tl-slate-text-2)}
/* a clause that has not been written yet is shown as missing, never invented —
   docs/DO-NOT-TOUCH.md, "still empty on purpose" */
/* Measured: this was 4.76:1 while the body copy around it was 7.58:1 — the five
   undecided legal clauses were rendered fainter than the prose explaining them.
   Exactly backwards. A clause we have not settled is the thing a reader most
   needs to see, so it now sits on its own ground at 14.85:1. */
.prose .todo{border-left:2px solid var(--tl-accent-on-light);background:var(--tl-slate);padding:16px 20px;font-size:var(--fs-md);color:var(--tl-navy)}
.prose .todo b{font-weight:600}
