/* ============================================================================
   Creoglyph homepage v2
   Dark, locked. One accent, rationed. Everything is built from frames, lines
   and nodes on a shared 4px sub-grid, which is the same vocabulary as the logo.

   Spacing rule: every padding, margin and gap comes from --s-* below. There are
   no off-scale values in this file. build/lint.py fails on any that appear.
   ============================================================================ */

@font-face{font-family:"Plus Jakarta Sans";src:url("../fonts/plus-jakarta-sans-var.woff2") format("woff2-variations");font-weight:200 800;font-style:normal;font-display:swap}
@font-face{font-family:"Manrope";src:url("../fonts/manrope-var.woff2") format("woff2-variations");font-weight:200 800;font-style:normal;font-display:swap}

:root{
  /* surfaces */
  --bg:#08080a; --bg-1:#0e0e11; --bg-2:#141418;
  /* ink */
  /* --ink-3 and --ink-4 were lifted in the accessibility pass. Measured against
     the darkest background they are used on, rgb(20,20,24), the old values gave
     3.86 and 2.23 against a 4.5 requirement. The new values give 5.06 and 4.51.
     See ACCESSIBILITY_CONTRAST_AUDIT.md for the blast radius. */
  --ink:#f7f7f8; --ink-2:#a0a0a9; --ink-3:#8a8a94; --ink-4:#7d7d87;
  /* lines */
  --line:rgba(255,255,255,.07); --line-2:rgba(255,255,255,.12); --line-3:rgba(255,255,255,.2);
  /* the single accent */
  --accent:#ff4a0a; --accent-hi:#ff7337;
  /* White on the brand orange measures 3.37, below the 4.5 required for a 15px
     600 label. The brand accent is unchanged everywhere it is decoration; only
     the surface that carries white text is darkened. Hover darkens further,
     because anything lighter fails again. */
  --accent-btn:#d93f08; --accent-btn-hi:#c4390a;
  --accent-dim:rgba(255,74,10,.08); --accent-bd:rgba(255,74,10,.45);

  --font-d:"Plus Jakarta Sans",ui-sans-serif,system-ui,sans-serif;
  --font-b:"Manrope",ui-sans-serif,system-ui,sans-serif;
  --font-m:ui-monospace,SFMono-Regular,Menlo,monospace;

  /* spacing scale. Nothing outside this list. */
  --s-1:4px;  --s-2:8px;  --s-3:12px; --s-4:16px; --s-5:20px; --s-6:24px;
  --s-7:32px; --s-8:40px; --s-9:48px; --s-10:64px; --s-11:80px; --s-12:96px;
  --s-13:120px; --s-14:160px;
  /* Semantic padding roles. Every card interior uses one of these four rather
     than picking a scale step ad hoc, which is how seven different values crept
     in. All on the 4pt grid. */
  --pad-lg:40px;   /* feature cards: the testimonial slider card */
  --pad:32px;      /* standard card interior */
  --pad-sm:24px;   /* compact cards, list rows, control interiors */
  --pad-xs:20px;   /* frames around media, tight blocks */

  --r-pill:999px; --r-lg:16px; --r-md:12px; --r-sm:8px;

  --nav-h:72px;
  --wrap:1200px;
  --gutter:var(--s-6);
  --sec:var(--s-12);

  --ease:cubic-bezier(.16,1,.3,1);
  --t-fast:150ms; --t:300ms; --t-slow:600ms;
  color-scheme:dark;
}
@media (min-width:1024px){:root{--gutter:var(--s-8)}}
@media (max-width:900px){:root{--sec:var(--s-10);--nav-h:64px}}
@media (max-width:600px){:root{--sec:var(--s-9);--gutter:var(--s-5)}}

/* ---------- reset ---------- */
*,*::before,*::after{box-sizing:border-box}
*{margin:0}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:calc(var(--nav-h) + var(--s-7))}
body{background:var(--bg);color:var(--ink);font-family:var(--font-b);font-size:17px;line-height:1.6;-webkit-font-smoothing:antialiased;overflow-x:clip}

/* Structural overflow guard.

   clip, not hidden. `overflow-x:hidden` makes the element a scroll container,
   which is what destabilises position:sticky for everything inside it, and this
   site has seven sticky surfaces: the app card deck, the ecosystem figure, the
   scrollytelling visual, the glossary rail, the article contents and the table
   head. `overflow:clip` clips without creating a scroll container, so sticky is
   untouched.

   Only the x axis. Clipping y would cut sticky elements off at the fold.

   main is the semantic top level container on all 70 pages, so this is the last
   line of defence: a single element that can never leak horizontally, whatever
   a child does. It does not replace fixing the cause, and every overflow found
   in this project has been fixed at source rather than hidden here. */
main{overflow-x:clip}
@supports not (overflow: clip){
  body,main{overflow-x:hidden}
}
body.is-locked{overflow:hidden}
img,svg{display:block;max-width:100%}
img{height:auto}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
a{color:inherit;text-decoration:none}
ul,ol{list-style:none;padding:0}
h1,h2,h3,h4{font-family:var(--font-d);font-weight:600;letter-spacing:-.028em;line-height:1.1}
dl,dd{margin:0}

/* ---------- a11y primitives ---------- */
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:3px}
:focus:not(:focus-visible){outline:none}
main:focus{outline:none}
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
.skip{position:fixed;top:var(--s-3);left:var(--s-3);z-index:90;padding:var(--s-3) var(--s-5);background:var(--ink);color:var(--bg);border-radius:var(--r-pill);font-weight:600;font-size:15px;transform:translateY(-200%);transition:transform var(--t) var(--ease)}
.skip:focus{transform:translateY(0)}

/* ---------- layout ---------- */
.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gutter)}
.sec{padding-block:var(--sec)}
.rule-top{border-top:1px solid var(--line)}

/* Section header. One consistent shape everywhere, so rhythm never drifts. */
.shead{max-width:44rem;margin-bottom:var(--s-10)}
.shead .h2{margin-bottom:var(--s-4)}
@media (max-width:900px){.shead{margin-bottom:var(--s-9)}}

.kicker{display:flex;align-items:center;gap:var(--s-3);line-height:1;margin-bottom:var(--s-5);font-family:var(--font-m);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3)}
.kicker::before{content:"";width:22px;height:2px;border-radius:1px;background:var(--accent);flex:none}

/* ---------- type ---------- */
.display{font-size:clamp(30px,5.2vw,64px);letter-spacing:-.038em;line-height:1.04;text-wrap:balance}
.h2{font-size:clamp(25px,3.2vw,42px);letter-spacing:-.032em;line-height:1.12;text-wrap:balance}
.h3{font-size:19px;letter-spacing:-.02em;line-height:1.3}
.h4{font-size:16px;letter-spacing:-.014em}
.lead{font-size:clamp(17px,1.4vw,20px);line-height:1.55;color:var(--ink-2);max-width:56ch;text-wrap:pretty}
.body{color:var(--ink-2);max-width:64ch;text-wrap:pretty}
.small{font-size:15px;line-height:1.6;color:var(--ink-2);max-width:58ch}
.tiny{font-size:13px;line-height:1.5;color:var(--ink-3)}
.mono{font-family:var(--font-m);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3)}
.num{font-variant-numeric:tabular-nums}

.ic{width:1em;height:1em;flex:none}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);white-space:nowrap;min-height:48px;padding:var(--s-3) var(--s-6);border:1px solid var(--line-2);border-radius:var(--r-pill);font-weight:600;font-size:15px;line-height:1;transition:background var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease),transform var(--t-fast) var(--ease)}
.btn:hover{border-color:var(--line-3);background:rgba(255,255,255,.05)}
.btn:active{transform:scale(.97)}
.btn-primary{background:var(--accent-btn);border-color:var(--accent-btn);color:#fff;box-shadow:inset 0 1px 0 rgba(255,255,255,.22)}
.btn-primary:hover{background:var(--accent-btn-hi);border-color:var(--accent-btn-hi)}
.btn-sm{min-height:40px;padding:var(--s-2) var(--s-4);font-size:14px}
.btn-block{width:100%}
.btn .ic{width:14px;height:14px;flex:none;fill:currentColor}

.tlink{display:inline-flex;align-items:center;gap:var(--s-2);font-size:15px;font-weight:600;padding-block:var(--s-1)}
.tlink .ic{width:12px;height:12px;fill:var(--ink-4)}


/* ============================================================================
   LABEL MOTION
   Carried over from the previous Creoglyph site, which ran a character scramble
   on its CTAs. Rebuilt so it can never move the layout:
     .fx-a  the real text, sits in flow, owns the width and the height
     .fx-b  absolutely positioned, so it contributes nothing to sizing
     .fx    a fixed mask with overflow hidden, so nothing can escape the box
   Character widths inside .fx-b are locked once in JS, so swapping a character
   for a glitch character cannot change a width either.
   ============================================================================ */
.fx{position:relative;display:inline-block;overflow:hidden;white-space:pre;
    height:1.35em;line-height:1.35em;vertical-align:middle}
.fx-a,.fx-b{display:block;height:1.35em;line-height:1.35em;white-space:pre;
    transition:transform 440ms var(--ease)}
.fx-b{position:absolute;left:0;top:0;transform:translateY(105%)}
.fx-b span{display:inline-block}

.btn:hover .fx-a, .btn:focus-visible .fx-a,
.nav-links a:hover .fx-a, .nav-links a:focus-visible .fx-a{transform:translateY(-105%)}
.btn:hover .fx-b, .btn:focus-visible .fx-b,
.nav-links a:hover .fx-b, .nav-links a:focus-visible .fx-b{transform:translateY(0)}

/* the glitch characters take the accent for the moment they exist, then resolve */
.fx-b span[data-g]{color:var(--accent)}
.btn-primary .fx-b span[data-g]{color:#ffd9c9}

/* ---------- text links: the rule leaves to the right, the accent enters from
     the left. Both are absolutely positioned pseudo elements, so the link box
     never changes. ---------- */
.tlink{position:relative;border-bottom:0;padding-bottom:var(--s-3)}
.tlink::after,.tlink::before{content:"";position:absolute;left:0;right:0;bottom:0;height:1px}

.tlink::before{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
    background:var(--line-2);transform:scaleX(1);transform-origin:0 50%}


.tlink:hover .ic,.tlink:focus-visible .ic{fill:var(--ink-2);transform:translate(2px,-2px)}
.tlink .ic{transition:fill 200ms var(--ease),transform 300ms var(--ease)}

@media (prefers-reduced-motion:reduce){
  .fx{overflow:visible;height:auto;line-height:inherit}
  .fx-a{transform:none !important}
  .fx-b{display:none}
  .tlink::before{transform:scaleX(0) !important}
  .tlink::after{transform:scaleX(1) !important}
  .tlink:hover::after{background:var(--accent)}
}

/* ---------- logo ---------- */
.logo{width:132px;height:23px;color:var(--ink)}
.logo path{fill:currentColor}
.brand{display:inline-flex;align-items:center;min-height:48px}
@media (max-width:600px){.logo{width:116px;height:20px}}

/* ---------- nav ---------- */
.nav{position:fixed;inset:0 0 auto 0;z-index:50;height:var(--nav-h);display:flex;align-items:center;border-bottom:1px solid transparent;transition:background var(--t) var(--ease),border-color var(--t) var(--ease)}
.nav.is-stuck{background:rgba(8,8,10,.72);backdrop-filter:saturate(150%) blur(14px);-webkit-backdrop-filter:saturate(150%) blur(14px);border-bottom-color:var(--line)}
@supports not (backdrop-filter:blur(2px)){.nav.is-stuck{background:rgba(8,8,10,.97)}}
@media (prefers-reduced-transparency:reduce){.nav.is-stuck{background:rgba(8,8,10,.98);backdrop-filter:none}}
.nav-in{display:flex;align-items:center;gap:var(--s-6)}
.nav-links{display:flex;align-items:center;gap:var(--s-1);margin-left:auto}
.nav-links a{display:inline-flex;align-items:center;min-height:40px;padding:0 var(--s-3);border-radius:var(--r-pill);font-size:15px;font-weight:500;color:var(--ink-2);transition:color var(--t-fast) var(--ease),background var(--t-fast) var(--ease)}
.nav-links a:hover{color:var(--ink);background:rgba(255,255,255,.05)}
.nav-toggle{display:none}
@media (max-width:960px){
  .nav-links,.nav-cta{display:none}
  .nav-toggle{display:inline-flex;align-items:center;justify-content:center;margin-left:auto;width:48px;height:48px;border:1px solid var(--line-2);border-radius:var(--r-pill)}
  .nav-toggle .ic{width:18px;height:18px;fill:currentColor}
}

/* ---------- mobile menu ---------- */
.menu{position:fixed;inset:0;z-index:60;background:var(--bg);display:flex;flex-direction:column;padding-block:var(--s-3) var(--s-8);overflow-y:auto;opacity:0;visibility:hidden;transition:opacity var(--t) var(--ease),visibility var(--t)}
.menu[data-open="true"]{opacity:1;visibility:visible}
.menu-top{display:flex;align-items:center;height:var(--nav-h)}
.menu-close{margin-left:auto;width:48px;height:48px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line-2);border-radius:var(--r-pill)}
.menu-close .ic{width:18px;height:18px;fill:currentColor}
.menu-nav{margin-top:var(--s-8);display:grid}
.menu-nav a{display:flex;align-items:center;justify-content:space-between;min-height:56px;font-family:var(--font-d);font-size:26px;font-weight:600;letter-spacing:-.03em;padding-block:var(--s-3);border-bottom:1px solid var(--line);color:var(--ink-2)}
.menu-nav a .ic{width:14px;height:14px;fill:var(--ink-4)}
.menu-foot{margin-top:auto;padding-top:var(--s-9);display:grid;gap:var(--s-3)}

/* ============================================================================
   1. HOME HERO
   Homepage header only. Every selector in this block starts with .home-hero or
   .hs-, and none of them matches an element on any other page. Other page
   openers are .phead, .app-hero, .art-hero and .blog-hero, all untouched.

   Two phases. The intro is a centered block in normal flow, so the opening
   state is exactly the required layout and nothing clever happens to it. The
   stage below it pins and runs the six beat sequence. Without JS or under
   reduced motion the stage does not pin, the canvas shows the fully composed
   system, and the six beats render as an ordered list. Motion is opt in: JS
   adds .is-motion and only then does anything hide.
   ============================================================================ */
.home-hero{padding-top:calc(var(--nav-h) + var(--s-9))}
@media (max-width:900px){.home-hero{padding-top:calc(var(--nav-h) + var(--s-9))}}

/* ---- opening state: centered, in flow ---- */
.home-hero-intro{display:flex;flex-direction:column;align-items:center;text-align:center;
  padding-bottom:var(--s-6)}
@media (max-width:900px){.home-hero-intro{padding-bottom:var(--s-8)}}
.home-hero-eyebrow{display:inline-flex;align-items:center;gap:var(--s-3);line-height:1;
  font-family:var(--font-m);font-size:11px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-3)}
.home-hero-eyebrow::before,.home-hero-eyebrow::after{content:"";width:22px;height:2px;
  border-radius:1px;background:var(--accent);flex:none}
@media (max-width:720px){
  /* Below this the eyebrow wraps, and a rule pinned to each end of a two line
     block reads as a stray mark. One rule above, centered, instead. */
  .home-hero-eyebrow{flex-direction:column;gap:var(--s-3);font-size:10px;letter-spacing:.12em;
    max-width:30ch;line-height:1.4;text-wrap:balance}
  .home-hero-eyebrow::after{display:none}
  .home-hero-eyebrow::before{width:28px}
}
.home-hero-h1{margin-top:var(--s-6);max-width:17ch}
.home-hero-lead{margin-top:var(--s-6);max-width:62ch}
.home-hero-cta{margin-top:var(--s-8);display:flex;flex-wrap:wrap;gap:var(--s-3);
  justify-content:center}

/* ---- the pinned stage ---- */
.home-hero-story{position:relative}
.home-hero-stage{padding-bottom:var(--s-9)}
.home-hero-stage-in{display:grid;gap:var(--s-8)}
.home-hero-fig{margin:0;position:relative}
.home-system-canvas{display:block;width:100%;height:auto;max-width:100%;overflow:visible}

/* ---- narration ----
   Attached to the window as a status bar rather than floating under it. The
   pips read as a progress track in product chrome, not marks in dead space. */
.home-hero-pips{display:none}
.home-hero-beats{margin:0;padding:0;list-style:none;display:grid;gap:var(--s-5);
  max-width:74ch;margin-inline:auto}
.home-hero-beat-h{font-family:var(--font-d);font-size:17px;font-weight:600;
  letter-spacing:-.02em;line-height:1.3;color:var(--ink)}
.home-hero-beat-b{margin-top:var(--s-1);font-size:14px;line-height:1.55;color:var(--ink-2)}

/* ---- motion mode. Only reached when JS is present and motion is allowed. ---- */
.home-hero.is-motion .home-hero-stage{min-height:100vh;display:grid;align-content:start;
  padding-top:calc(var(--nav-h) + var(--s-1));padding-bottom:var(--s-6)}
.home-hero.is-motion .home-hero-stage-in{gap:0}
.home-hero.is-motion .home-hero-fig{max-width:1120px;margin-inline:auto;width:100%}
@media (max-height:900px){.home-hero.is-motion .home-hero-fig{max-width:1000px}}
@media (max-height:800px){.home-hero.is-motion .home-hero-fig{max-width:880px}}
/* the caption bar sits flush under the window and shares its width */
.home-hero.is-motion .home-hero-narrate{max-width:1120px;margin-inline:auto;width:100%;
  margin-top:calc(var(--s-3) * -1);display:flex;align-items:center;gap:var(--s-6);
  border:1px solid var(--line-2);border-top:0;border-radius:0 0 var(--r-lg) var(--r-lg);
  background:var(--bg-1);padding:var(--s-5) var(--s-7)}
@media (max-height:900px){.home-hero.is-motion .home-hero-narrate{max-width:1000px}}
@media (max-height:800px){.home-hero.is-motion .home-hero-narrate{max-width:880px}}
.home-hero.is-motion .home-hero-pips{display:flex;gap:var(--s-1);flex:none;width:104px}
.home-hero.is-motion .home-hero-pip{width:10px;height:3px;border-radius:2px;
  background:var(--line-2);transition:background var(--t) var(--ease)}
.home-hero.is-motion .home-hero-pip.is-on{background:var(--ink-3)}
.home-hero.is-motion .home-hero-pip.is-live{background:var(--accent)}
.home-hero.is-motion .home-hero-beats{position:relative;display:block;min-height:48px;
  max-width:none;margin-inline:0;flex:1}
.home-hero.is-motion .home-hero-beat{position:absolute;inset:0;opacity:0;
  text-align:left;will-change:opacity}
.home-hero.is-motion .home-hero-beat.is-on{opacity:1}
.home-hero.is-motion .home-hero-beat-h{font-size:16px}
.home-hero.is-motion .home-hero-beat-b{font-size:13.5px;max-width:88ch}
@media (max-width:900px){
  .home-hero.is-motion .home-hero-stage{min-height:0;padding-top:0;padding-bottom:var(--s-9)}
  .home-hero.is-motion .home-hero-stage-in{gap:var(--s-7)}
  .home-hero.is-motion .home-hero-narrate{display:block;margin-top:0}
  .home-hero.is-motion .home-hero-pips{display:none}
  .home-hero.is-motion .home-hero-beats{position:static;display:grid;min-height:0}
  .home-hero.is-motion .home-hero-beat{position:static;inset:auto;opacity:1;text-align:center}
}
@media (prefers-reduced-motion:reduce){
  .home-hero.is-motion .home-hero-stage{min-height:0;padding-top:0}
  .home-hero.is-motion .home-hero-narrate{display:block}
  .home-hero.is-motion .home-hero-pips{display:none}
  .home-hero.is-motion .home-hero-beats{position:static;display:grid;min-height:0}
  .home-hero.is-motion .home-hero-beat{position:static;inset:auto;opacity:1;text-align:center}
}

/* ---- responsive canvas ---- */
@media (max-width:900px){
  .home-hero-stage-in{gap:var(--s-7)}
  .home-hero-beat-h{font-size:17px}
  .home-hero-beat-b{font-size:14px}
}
/* Two builds of the same system, never both on screen. A 1120 unit band cannot
   carry 11px labels at 342px, so the phone gets the portrait figure instead of a
   squeezed copy. display:none keeps the hidden one out of the a11y tree, so it
   is announced once. */
.home-system-canvas.-m{display:none}
@media (max-width:720px){
  .home-system-canvas{display:none}
  .home-system-canvas.-m{display:block;max-width:420px;margin-inline:auto}
}

/* ============================================================================
   2. CREDIBILITY STRIP
   Unequal columns, hairline dividers, no boxes. Weight comes from the numerals.
   ============================================================================ */
.cred-sec{padding-bottom:var(--s-4)}
.cred{display:grid;grid-template-columns:.7fr .7fr 1.6fr;border-top:1px solid var(--line);border-bottom:1px solid var(--line);align-items:start}
.cred-cell{padding:var(--s-8) var(--pad) var(--s-8) 0}
.cred-cell + .cred-cell{border-left:1px solid var(--line);padding-left:var(--s-7)}
.cred-n{font-family:var(--font-d);font-size:clamp(38px,4vw,54px);font-weight:600;letter-spacing:-.045em;line-height:1;font-variant-numeric:tabular-nums}
.cred-plus{color:var(--ink-3);font-size:.62em;vertical-align:.22em;margin-left:2px}
.cred-k{margin-top:var(--s-3);font-size:15px;line-height:1.4;color:var(--ink-2);max-width:24ch}
.cred-apps{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-top:var(--s-5)}
.cred-app{display:inline-flex;align-items:center;gap:var(--s-2);padding:var(--s-2) var(--s-3);border:1px solid var(--line);border-radius:var(--r-pill);font-size:13px;color:var(--ink-2)}

@media (max-width:760px){
  .cred{grid-template-columns:1fr}
  .cred-cell{padding:var(--pad) 0}
  .cred-cell + .cred-cell{border-left:0;border-top:1px solid var(--line);padding-left:0}
  .cred-n{font-size:40px}
  .cred-k{max-width:none}
}

/* ============================================================================
   3. THE SHIFT
   ============================================================================ */
.shift-list{display:grid;gap:0}
.shift-row{display:grid;grid-template-columns:320px minmax(0,1fr);gap:var(--s-9);align-items:baseline;padding-block:var(--s-8);border-top:1px solid var(--line)}
.shift-l{display:flex;align-items:baseline;gap:var(--s-4)}
.shift-row:last-child{border-bottom:1px solid var(--line)}
.shift-n{font-family:var(--font-m);font-size:12px;letter-spacing:.1em;color:var(--ink-4);flex:none}
.shift-row.is-now .shift-n{color:var(--accent)}

@media (max-width:820px){.shift-row{grid-template-columns:1fr;gap:var(--s-3);padding-block:var(--s-7)}}

/* ============================================================================
   4. ECOSYSTEM
   ============================================================================ */
.eco{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:var(--s-10);align-items:start}
@media (max-width:960px){.eco{grid-template-columns:1fr;gap:var(--s-9)}}
.eco-fig{position:sticky;top:calc(var(--nav-h) + var(--s-8))}
@media (max-width:960px){.eco-fig{position:static}}
.figbox{margin:0;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--bg-1);padding:var(--pad);box-shadow:inset 0 1px 0 rgba(255,255,255,.035)}
.figbox > svg{width:100%;height:auto;display:block}
.figcap{margin-top:var(--s-6);font-size:13px;line-height:1.5;color:var(--ink-3)}

.eco-steps{display:grid;gap:var(--s-9);padding-block:var(--pad-sm)}
@media (max-width:960px){.eco-steps{gap:var(--s-8);padding-block:0}}
.step{opacity:.4;padding-left:var(--s-6);border-left:2px solid rgba(255,255,255,.16);transition:opacity var(--t-slow) var(--ease),border-color var(--t) var(--ease)}
.step.is-on{opacity:1;border-left-color:var(--accent)}
.step-h{margin-bottom:var(--s-3)}
@media (max-width:960px){.step{opacity:1;padding-left:var(--s-5)}}

/* ============================================================================
   5. CREOGEN MODULES
   ============================================================================ */
.mods{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4)}
@media (max-width:900px){.mods{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:560px){.mods{grid-template-columns:1fr}}
.mod{display:flex;flex-direction:column;padding:var(--pad);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--bg-1);box-shadow:inset 0 1px 0 rgba(255,255,255,.03);transition:border-color var(--t) var(--ease)}
.mod:hover{border-color:var(--line-2)}
.mod-gl{width:44px;height:44px;margin-bottom:var(--s-6);flex:none}
.mod .h4{margin-bottom:var(--s-2)}
.mod p{font-size:14px;line-height:1.58;color:var(--ink-2)}
.mod-tags{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-top:auto;padding-top:var(--s-5)}
.mod-tag{font-family:var(--font-m);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-4);border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--s-1) var(--s-2)}

/* ============================================================================
   6. CREOBOT
   ============================================================================ */
.figbox-wide{margin:0}
.figbox-wide > svg{width:100%;height:auto;display:block}
.bot-points{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0 var(--s-8);margin-top:var(--s-9)}
.bot-point{padding-block:var(--pad-sm);border-top:1px solid var(--line)}
.bot-point dt{font-family:var(--font-d);font-size:15px;font-weight:600;color:var(--ink);margin-bottom:var(--s-2)}
.bot-point dd{font-size:14px;line-height:1.58;color:var(--ink-2)}
@media (max-width:860px){.bot-points{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:560px){.bot-points{grid-template-columns:1fr}}

/* ============================================================================
   7. APPS CARD STACK
   ============================================================================ */
.stack{display:block;isolation:isolate}
.stack-item + .stack-item{margin-top:var(--s-4)}
.stack-item{position:sticky;top:calc(var(--nav-h) + var(--s-6) + var(--i) * 16px)}
.appcard{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,320px);gap:var(--s-9);align-items:center;background:var(--bg-1);border:1px solid var(--line-2);border-radius:var(--r-lg);padding:var(--s-9);box-shadow:inset 0 1px 0 rgba(255,255,255,.04);transform-origin:center top;will-change:transform,filter;transition:transform var(--t) var(--ease),filter var(--t) var(--ease)}
/* Depth cue for cards sitting under the stack.
   NEVER opacity. These cards physically overlap, so any alpha below 1 lets the
   card underneath read straight through and the deck turns into ghost text.
   brightness() dims the rendered result without touching the alpha channel, so
   the background stays fully opaque and nothing bleeds. */
.appcard.is-under{transform:scale(.975);filter:brightness(.66)}
.app-top{display:flex;align-items:center;gap:var(--s-4);margin-bottom:var(--s-6)}
.app-gl{width:40px;height:40px;flex:none}
.app-idx{font-family:var(--font-m);font-size:11px;letter-spacing:.12em;color:var(--ink-4);margin-left:auto}
.appcard .h3{margin-bottom:var(--s-2)}
.app-line{font-size:17px;color:var(--ink);margin-bottom:var(--s-3)}
.appcard .small{margin-bottom:var(--s-6)}
.app-fig svg{width:100%;height:auto;display:block}
.app-act{display:flex;flex-wrap:wrap;gap:var(--s-5);align-items:center}
.tag{display:inline-block;width:fit-content;vertical-align:middle;font-size:12px;font-weight:600;color:var(--ink-3);border:1px solid var(--line);border-radius:var(--r-pill);padding:var(--s-1) var(--s-3)}
.app-fig{border:1px solid var(--line);border-radius:var(--r-md);background:var(--bg-2);padding:var(--s-5)}
.app-fig svg{width:100%;height:auto}
@media (max-width:860px){
  .appcard{grid-template-columns:1fr;gap:var(--s-6);padding:var(--s-7)}
  .stack-item{position:static}
}

/* ============================================================================
   8 and 9. PARTNER
   ============================================================================ */
.partner{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s-12);align-items:start}
@media (max-width:900px){.partner{grid-template-columns:1fr;gap:var(--s-9)}}
.partner-body{margin-top:var(--s-4)}
.partner-cta{margin-top:var(--s-8);display:flex;flex-wrap:wrap;gap:var(--s-3)}
.qual{display:grid;gap:var(--s-8)}
.qual-g .h4{margin-bottom:var(--s-5)}
.qual-g ul{display:grid;gap:var(--s-3)}
.qual-g li{display:flex;gap:var(--s-3);align-items:flex-start;font-size:15px;line-height:1.5;color:var(--ink-2)}
.qual-g li span{flex:1}
.qual-g li .ic{width:14px;height:14px;flex:none;margin-top:var(--s-1);fill:var(--ink-2)}
.qual-no li .ic{fill:var(--ink-4)}

/* ---------- footer ---------- */
.foot{border-top:1px solid var(--line);padding-block:var(--s-10)}
.foot-in{display:flex;flex-wrap:wrap;gap:var(--s-9);justify-content:space-between;align-items:flex-start}
.foot-line{margin-top:var(--s-4);max-width:32ch}
.foot-right{display:grid;gap:var(--s-4);justify-items:flex-start}
.foot-links{display:flex;flex-wrap:wrap;gap:var(--s-6)}
.foot-legal{color:var(--ink-4)}

/* ============================================================================
   ILLUSTRATION SYSTEM
   One stroke weight, one radius language, one accent element per figure.
   ============================================================================ */
.il-frame{fill:none;stroke:var(--line-2);stroke-width:1}
.il-fill{fill:rgba(255,255,255,.028);stroke:var(--line-2);stroke-width:1}
.il-solid{fill:rgba(255,255,255,.055);stroke:none}
.il-line{fill:none;stroke:var(--line-2);stroke-width:1}
.il-node{fill:var(--bg-2);stroke:var(--line-3);stroke-width:1}
.il-dot{fill:var(--ink-4)}
.il-t{font-family:var(--font-d);font-size:13px;font-weight:600;fill:var(--ink-2)}
.il-s{font-family:var(--font-b);font-size:11px;fill:var(--ink-4)}
.il-m{font-family:var(--font-m);font-size:9px;letter-spacing:.12em;fill:var(--ink-3)}
/* the one accent element per figure */
.il-on{stroke:var(--accent-bd)}
.il-on-f{fill:var(--accent-dim);stroke:var(--accent-bd);stroke-width:1}
.il-on-d{fill:var(--accent)}
.il-on-t{fill:var(--ink)}

/* module glyphs share one canvas and one stroke */
.gl{fill:none;stroke:var(--ink-3);stroke-width:1.25;stroke-linecap:round;stroke-linejoin:round}
.gl .a{stroke:var(--accent)}
.gl .f{fill:rgba(255,255,255,.05);stroke:none}

/* ecosystem diagram state */
.dg-node .fl{transition:stroke var(--t) var(--ease),fill var(--t) var(--ease)}
.dg-node .t,.dg-node .s,.dg-node .m{transition:fill var(--t) var(--ease)}
.dg-node.is-on .fl{fill:rgba(255,74,10,.1);stroke:rgba(255,74,10,.62)}
.dg-node.is-on .t{fill:var(--ink)}
.dg-node.is-on .m{fill:var(--accent)}

.quotes{display:grid;grid-template-columns:repeat(auto-fit,minmax(17rem,1fr));gap:var(--s-4)}
.quote{padding:var(--pad);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--bg-1)}
.quote blockquote{font-size:15px;line-height:1.6;color:var(--ink)}
.quote figcaption{margin-top:var(--s-5)}

/* ---------- reveal ---------- */
/* ============================================================================
   REVEAL

   Conservative on purpose. Only the [data-rise] hooks that are already in the
   markup, and there are none inside .home-hero, so the hero is excluded by not
   matching rather than by an override.

   A previous version excluded the hero with
   `.home-hero * { transform:none !important; opacity:1 !important }`.
   That reached into the inline SVG, cancelled `transform="translate(22 20)
   scale(.02357)"` on the Creogen mark so it drew at its native 921x763 units,
   and forced all six inspector panels visible at once. One rule, an unreadable
   hero. Never override transform or opacity on descendants to scope an effect.

   CSS declares only the pre state. GSAP owns the final state.
   ============================================================================ */
.js [data-rise]:not(.is-in){opacity:0;transform:translate3d(0,16px,0);filter:blur(10px)}
.js [data-rise]{transition:opacity var(--t-slow) var(--ease),transform var(--t-slow) var(--ease),filter var(--t-slow) var(--ease)}


/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  /* Gentler, not zero. Colour and opacity still transition, movement does not. */
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-property:opacity,color,background-color,border-color,fill,stroke !important;
    transition-duration:160ms !important}
  .js [data-rise]{opacity:1 !important;transform:none !important;filter:none !important}
  .step{opacity:1}
  .appcard.is-under{transform:none;filter:none}
}

/* ---------- print ---------- */
@media print{.nav,.menu,.skip{display:none !important}body{background:#fff;color:#000}}

/* ============================================================================
   PAGE SYSTEM
   Everything below extends the approved homepage system for inner pages. No new
   colours, no new radii, no new easing. Same tokens, same spacing scale.
   ============================================================================ */

/* ---------- page head: the inner page equivalent of the hero ---------- */
.phead{padding-top:calc(var(--nav-h) + var(--s-12));padding-bottom:var(--s-11)}
@media (max-width:900px){.phead{padding-top:calc(var(--nav-h) + var(--s-9));padding-bottom:var(--s-9)}}
.phead-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s-11);align-items:center}
.phead-grid.-wide{grid-template-columns:minmax(0,1fr) minmax(0,1.12fr)}
@media (max-width:960px){.phead-grid,.phead-grid.-wide{grid-template-columns:1fr;gap:var(--s-9)}}
.phead .display{font-size:clamp(30px,4.4vw,56px);max-width:16ch}
.phead .lead{margin-top:var(--s-6)}
.phead-cta{margin-top:var(--s-8);display:flex;flex-wrap:wrap;gap:var(--s-3)}
.crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2);font-size:13px;color:var(--ink-4);margin-bottom:var(--s-6)}
.crumbs a{color:var(--ink-3)}
.crumbs a:hover{color:var(--ink)}
.crumbs .ic{width:10px;height:10px;fill:var(--ink-4)}

/* ---------- direct answer block. One paragraph an answer engine can quote. ---------- */
/* The rule is drawn on a pseudo element sized to the text, not on the box, so
   it can never run past the last line. */
.answer{position:relative;padding-left:var(--s-8);max-width:62ch}
.answer::before{content:"";position:absolute;left:0;top:.42em;bottom:.42em;
  width:2px;border-radius:1px;background:var(--accent)}
.answer .lbl{display:block;font-family:var(--font-m);font-size:10.5px;letter-spacing:.15em;
  text-transform:uppercase;color:var(--ink-3);margin-bottom:var(--s-3)}
.answer p{color:var(--ink-2);font-size:17px;line-height:1.62;text-wrap:pretty}
.answer strong{color:var(--ink);font-weight:600}
@media (max-width:600px){.answer{padding-left:var(--s-5)}}

/* ---------- split row ---------- */
.split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s-11);align-items:start}
.split.-center{align-items:center}
@media (max-width:900px){.split{grid-template-columns:1fr;gap:var(--s-9)}}

/* ---------- cluster: a labelled group of statements, no card, no bullet ---------- */
.cluster{display:grid;gap:var(--s-8)}
.cluster-g{display:grid;grid-template-columns:minmax(0,200px) minmax(0,1fr);gap:var(--s-8);
  padding-top:var(--s-6);border-top:1px solid var(--line)}
.cluster-g ul{display:grid;gap:var(--s-3)}
.cluster-g li{display:flex;gap:var(--s-3);align-items:flex-start;font-size:15px;line-height:1.5;color:var(--ink-2)}
.cluster-g li .ic{width:14px;height:14px;flex:none;margin-top:var(--s-1);fill:var(--ink-2)}
.cluster-g.-no li .ic{fill:var(--ink-4)}
@media (max-width:760px){.cluster-g{grid-template-columns:1fr;gap:var(--s-4)}}

/* ---------- numbered rows, reused from the homepage shift section ---------- */
.rows{display:grid}
.row-i{display:grid;grid-template-columns:320px minmax(0,1fr);gap:var(--s-9);align-items:baseline;
  padding-block:var(--s-8);border-top:1px solid var(--line)}
.row-i:last-child{border-bottom:1px solid var(--line)}
.row-l{display:flex;align-items:baseline;gap:var(--s-4)}
.row-n{font-family:var(--font-m);font-size:12px;letter-spacing:.1em;color:var(--ink-4);flex:none}
.row-i.is-now .row-n{color:var(--accent)}
@media (max-width:820px){.row-i{grid-template-columns:1fr;gap:var(--s-3);padding-block:var(--s-7)}}

/* ---------- metrics ---------- */
.metrics.-two{grid-template-columns:repeat(2,minmax(0,1fr))}
.metrics{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.metrics > div{padding:var(--s-8) var(--pad) var(--s-8) 0}
.metrics > div + div{border-left:1px solid var(--line);padding-left:var(--s-7)}
.metrics dd{font-family:var(--font-d);font-size:clamp(34px,3.6vw,48px);font-weight:600;letter-spacing:-.045em;line-height:1;font-variant-numeric:tabular-nums}
.metrics dt{margin-top:var(--s-3);font-size:15px;line-height:1.4;color:var(--ink-2)}
.metrics .note{margin-top:var(--s-2);font-size:13px;color:var(--ink-4)}
@media (max-width:760px){
  .metrics{grid-template-columns:1fr}
  .metrics > div{padding:var(--pad) 0}
  .metrics > div + div{border-left:0;border-top:1px solid var(--line);padding-left:0}
}

/* ---------- FAQ: native details, no JS, keyboard operable by default ---------- */
.faq{border-top:1px solid var(--line)}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-6);
  padding-block:var(--pad-sm);cursor:pointer;list-style:none;
  font-family:var(--font-d);font-size:clamp(16px,1.5vw,18px);font-weight:600;letter-spacing:-.02em;
  line-height:1.35;color:var(--ink-2);transition:color var(--t-fast) var(--ease)}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{color:var(--ink)}
.faq details[open] summary{color:var(--ink)}
.faq summary .mk{flex:none;position:relative;width:14px;height:14px;margin-top:4px}
.faq summary .mk::before,.faq summary .mk::after{content:"";position:absolute;background:var(--ink-3);
  transition:transform var(--t) var(--ease),background var(--t) var(--ease)}
.faq summary .mk::before{left:0;top:6.5px;width:14px;height:1px}
.faq summary .mk::after{left:6.5px;top:0;width:1px;height:14px}
.faq details[open] summary .mk::after{transform:scaleY(0);background:var(--accent)}
.faq details[open] summary .mk::before{background:var(--accent)}
.faq-a{padding-bottom:var(--s-6);max-width:64ch;color:var(--ink-2);font-size:15px;line-height:1.62}
.faq-a p + p{margin-top:var(--s-3)}

/* ---------- prose, for legal and reference pages ---------- */
.prose{max-width:68ch}
.prose > * + *{margin-top:var(--s-4)}
.prose h2{font-family:var(--font-d);font-size:20px;font-weight:600;letter-spacing:-.024em;margin-top:var(--s-10)}
.prose h3{font-family:var(--font-d);font-size:16px;font-weight:600;margin-top:var(--s-7)}
.prose p,.prose li{color:var(--ink-2)}
.prose ul{display:grid;gap:var(--s-2);padding-left:var(--s-5);list-style:disc}
.prose ol{display:grid;gap:var(--s-2);padding-left:var(--s-6);list-style:decimal}
.prose a{color:var(--ink);text-decoration:underline;text-decoration-color:var(--line-3);text-underline-offset:3px}
.prose a:hover{text-decoration-color:var(--accent)}
.prose strong{color:var(--ink);font-weight:600}
.prose table{width:100%;border-collapse:collapse;font-size:15px}
.prose th,.prose td{text-align:left;padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--line)}
.prose th{color:var(--ink);font-weight:600}
.prose td{color:var(--ink-2)}

/* ---------- forms ---------- */
.form{display:grid;gap:var(--s-5)}
.field{display:grid;gap:var(--s-2)}
.field label{font-size:14px;font-weight:600;color:var(--ink)}
.field .req{color:var(--accent)}
.field .help{font-size:13px;color:var(--ink-3)}
.field input,.field textarea,.field select{width:100%;background:var(--bg-1);border:1px solid var(--line-2);
  border-radius:var(--r-md);padding:var(--s-3) var(--s-4);font-size:15px;color:var(--ink);
  transition:border-color var(--t-fast) var(--ease)}
.field textarea{min-height:140px;resize:vertical}
.field select{appearance:none;cursor:pointer}
.field input::placeholder,.field textarea::placeholder{color:var(--ink-4)}
.field input:hover,.field textarea:hover,.field select:hover{border-color:var(--line-3)}
.field input:focus,.field textarea:focus,.field select:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:transparent}
.field[data-invalid="true"] input,.field[data-invalid="true"] textarea,.field[data-invalid="true"] select{border-color:#ff8b6d}
.err-msg{font-size:13px;color:#ff8b6d;min-height:1px}
.err-msg:empty{display:none}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.form-note,.form-status{border:1px solid var(--line);border-radius:var(--r-md);background:var(--bg-1);
  padding:var(--pad-sm);font-size:14px;line-height:1.55;color:var(--ink-2)}
.form-status[hidden]{display:none}
.form-status.-ok{border-color:rgba(107,189,110,.4)}
.form-status.-err{border-color:rgba(255,139,109,.4)}
.form-status a,.form-note a{color:var(--ink);text-decoration:underline;text-underline-offset:3px}

/* ---------- status pages ---------- */
.status-page main{min-height:70vh;display:flex;align-items:center;padding-block:calc(var(--nav-h) + var(--s-12)) var(--s-12)}
.status-mark{width:52px;height:52px;border-radius:50%;display:grid;place-items:center;border:1px solid var(--line-2);margin-bottom:var(--s-6)}
.status-mark .ic{width:22px;height:22px}
.status-mark.-ok{border-color:rgba(107,189,110,.45)}
.status-mark.-ok .ic{fill:#6bbd6e}
.status-mark.-bad{border-color:rgba(255,139,109,.45)}
.status-mark.-bad .ic{fill:#ff8b6d}
.site-list{display:grid;gap:var(--s-3);margin-top:var(--s-8)}
.site-row{display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  padding:var(--s-4) var(--s-5);border:1px solid var(--line);border-radius:var(--r-md);background:var(--bg-1)}
.site-row .name{font-weight:600;font-size:15px}
.mono-block{font-family:var(--font-m);font-size:12px;line-height:1.6;color:var(--ink-2);word-break:break-word}

/* ---------- founders ---------- */
.people{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-4)}
@media (max-width:760px){.people{grid-template-columns:1fr}}
.person{display:flex;gap:var(--s-5);align-items:flex-start;padding:var(--pad);
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--bg-1)}
.person img{width:76px;height:76px;border-radius:50%;object-fit:cover;flex:none;border:1px solid var(--line)}
.person .links{display:flex;flex-wrap:wrap;gap:var(--s-4);margin-top:var(--s-4)}
/* Below 480 the 76px avatar was holding horizontal space the bio needed. At 320
   the column left for text was 192px: the name wrapped, the role line wrapped,
   and the bio ran to fourteen lines. Stack it instead. Avatar and identity sit
   on the top row, bio and links span the full card underneath, which takes the
   bio from 192px to 288px at 320. display:contents on the inner wrapper, which
   carries no styling of its own, promotes its children to grid items of
   .person without touching the markup. The reveal is on .people, not .person,
   so nothing in the animation system is affected. */
@media (max-width:480px){
  .person{display:grid;grid-template-columns:76px minmax(0,1fr);
    column-gap:var(--s-4);row-gap:0;align-items:start}
  .person > div{display:contents}
  .person img{grid-column:1;grid-row:1 / span 2}
  .person .h3{grid-column:2;grid-row:1;align-self:end}
  .person .tiny{grid-column:2;grid-row:2;align-self:start}
  .person .small{grid-column:1 / -1;grid-row:3}
  .person .links{grid-column:1 / -1;grid-row:4}
}

/* ---------- quotes, ready for verified testimonials ---------- */
.quotes{display:grid;grid-template-columns:repeat(auto-fit,minmax(17rem,1fr));gap:var(--s-4)}
.quote{padding:var(--s-7);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--bg-1)}
.quote blockquote{font-size:15px;line-height:1.6;color:var(--ink)}
.quote figcaption{margin-top:var(--s-5)}

/* ---------- utility ---------- */
.stack-note{margin-top:var(--s-8)}
.cta-band{border:1px solid var(--line-2);border-radius:var(--r-lg);background:var(--bg-1);
  padding:clamp(32px,5vw,64px);box-shadow:inset 0 1px 0 rgba(255,255,255,.04)}
.cta-band .h2{max-width:20ch}
.cta-band .body{margin-top:var(--s-4)}
.cta-actions{margin-top:var(--s-8);display:flex;flex-wrap:wrap;gap:var(--s-3)}

/* ---------- footer, extended for the site wide version ---------- */
.foot .brand{min-height:0;align-items:flex-start}
.foot-grid{display:grid;align-items:start;grid-template-columns:minmax(0,1.6fr) repeat(4,minmax(0,1fr));gap:var(--s-9)}
@media (max-width:960px){.foot-grid{grid-template-columns:repeat(2,1fr)}.foot-brand{grid-column:1 / -1}}
@media (max-width:520px){.foot-grid{grid-template-columns:1fr}}
.foot h2{font-family:var(--font-b);font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-4);font-weight:600;margin-bottom:var(--s-4)}
.foot ul{display:grid;gap:var(--s-3)}
.foot ul a{font-size:15px;color:var(--ink-2);display:inline-flex;align-items:center;gap:var(--s-1);
  transition:color var(--t-fast) var(--ease)}
.foot ul a:hover{color:var(--ink)}
.foot ul a .ic{width:11px;height:11px;fill:var(--ink-4)}
.foot-base{margin-top:var(--s-10);padding-top:var(--s-6);border-top:1px solid var(--line);
  display:flex;flex-wrap:wrap;gap:var(--s-5);align-items:center;justify-content:space-between}
.foot-cert img{height:26px;width:auto;opacity:.6;transition:opacity var(--t) var(--ease)}
.foot-cert:hover img{opacity:1}

/* ============================================================================
   GLOW BORDER
   The React GlowingEffect recreated in plain CSS and JS. One shared pointer
   loop in main.js writes --gx, --gy and --on onto whatever is near the cursor.
   Everything here is a pointer-events:none pseudo layer, so it can never
   change layout, block a click, or affect the readability of the content.
   The brand palette replaces the demo's four colour conic: one accent, rationed.
   ============================================================================ */
.glow{position:relative;isolation:isolate}

/* 1. the border itself: a conic sweep masked to a 1px ring */
.glow::before{
  content:"";position:absolute;inset:-1px;border-radius:inherit;pointer-events:none;
  opacity:var(--on,0);transition:opacity 320ms var(--ease);z-index:0;
  background:conic-gradient(from calc(var(--ga,0deg) - 26deg) at var(--gx,50%) var(--gy,50%),
    transparent 0deg, var(--accent) 12deg, var(--accent-hi) 26deg, transparent 52deg);
  -webkit-mask:linear-gradient(#000,#000) content-box,linear-gradient(#000,#000);
  mask:linear-gradient(#000,#000) content-box,linear-gradient(#000,#000);
  -webkit-mask-composite:xor;mask-composite:exclude;
  padding:1px;
}
/* 2. proximity wash: a soft radial that follows the cursor inside the card */
.glow::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  opacity:calc(var(--on,0) * .5);transition:opacity 320ms var(--ease);z-index:0;
  background:radial-gradient(220px circle at var(--gx,50%) var(--gy,50%),
    rgba(255,74,10,.09), transparent 62%);
}
.glow > *{position:relative;z-index:1}

/* the scan variant adds a single travelling edge highlight, used on CTA panels */
.glow.-scan::before{
  background:conic-gradient(from calc(var(--ga,0deg) - 26deg) at var(--gx,50%) var(--gy,50%),
    transparent 0deg, var(--accent) 8deg, #fff 20deg, var(--accent-hi) 32deg, transparent 60deg);
}

/* Primary buttons get a tighter, brighter ring and no wash. */
.btn.glow::before{inset:-1px;padding:1px;
  background:conic-gradient(from calc(var(--ga,0deg) - 30deg) at var(--gx,50%) var(--gy,50%),
    transparent 0deg, rgba(255,255,255,.95) 16deg, transparent 44deg)}
.btn.glow::after{display:none}

/* Touch: no cursor, so no glow. The resting card is the complete state. */
@media (hover:none),(pointer:coarse){
  .glow::before,.glow::after{display:none}
}
@media (prefers-reduced-motion:reduce){
  .glow::before,.glow::after{transition:none}
  .glow::before{opacity:calc(var(--on,0) * .55)}
}

/* ============================================================================
   CHIP CONTRAST FIX
   Chips were --ink-3 on --bg-1 at ~4.3:1 with a .07 border, which read as
   disabled. Now --ink-2 at 7.4:1 with a .16 border and more padding.
   ============================================================================ */
.tag{display:inline-block;width:fit-content;vertical-align:middle;font-size:12.5px;
  font-weight:600;letter-spacing:.005em;color:var(--ink-2);
  border:1px solid rgba(255,255,255,.16);border-radius:var(--r-pill);
  padding:var(--s-2) var(--s-3);line-height:1.2;background:rgba(255,255,255,.03)}
.tag.-live{color:var(--ink);border-color:rgba(255,74,10,.45);background:rgba(255,74,10,.08)}
.tag.-featured{display:inline-flex;align-items:center;gap:var(--s-2);color:var(--ink);
  border-color:rgba(255,255,255,.3);background:rgba(255,255,255,.06)}
.tag.-featured .wf{width:16px;height:auto;flex:none;fill:var(--ink);display:block}
/* The mark is 1080 by 674, so height is left to the viewBox rather than fixed.
   A square box on a 1.6 to 1 mark is what made the old badge unreadable. */
.tag.-soon{color:var(--ink-2)}
.mod-tag{font-family:var(--font-m);font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-2);border:1px solid rgba(255,255,255,.14);border-radius:var(--r-sm);
  padding:var(--s-1) var(--s-2);background:rgba(255,255,255,.025)}
.cred-app{color:var(--ink-2);border-color:rgba(255,255,255,.16)}
.app-idx{color:var(--ink-3)}

/* ============================================================================
   WORK CARDS, rebuilt
   Two up on desktop, one on mobile, even card heights, no giant mockups.
   ============================================================================ */
.work{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-4)}
@media (max-width:820px){.work{grid-template-columns:1fr}}
.work-card{display:flex;flex-direction:column;height:100%;background:var(--bg-1);
  border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;
  transition:border-color var(--t) var(--ease)}
.work-card:hover{border-color:var(--line-2)}
.work-shot{display:block;overflow:hidden;background:var(--bg-2);border-bottom:1px solid var(--line)}
.work-shot img{width:100%;height:auto;display:block;object-fit:cover;object-position:center top;
  transition:transform var(--t-slow) var(--ease)}
@media (hover:hover) and (pointer:fine){.work-card:hover .work-shot img{transform:scale(1.025)}}
.work-meta{display:flex;flex-direction:column;gap:var(--s-2);padding:var(--pad-sm);flex:1}
.work-meta .h4{display:flex;align-items:center;gap:var(--s-2)}
.work-meta .h4 .ic{width:12px;height:12px;fill:var(--ink-4);transition:fill var(--t) var(--ease),transform var(--t) var(--ease)}
.work-card:hover .work-meta .h4 .ic{fill:var(--accent);transform:translate(2px,-2px)}
.work-meta .tiny{color:var(--ink-2)}
.work-tags{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-top:auto;padding-top:var(--s-4)}

/* ============================================================================
   TESTIMONIALS, editorial bento rather than a carousel
   ============================================================================ */
.tst{display:grid;grid-template-columns:repeat(6,1fr);gap:var(--s-4)}
.tst > li{grid-column:span 2}
.tst > li.-wide{grid-column:span 3}
@media (max-width:1000px){.tst{grid-template-columns:repeat(2,1fr)}.tst > li,.tst > li.-wide{grid-column:span 1}}
@media (max-width:640px){.tst{grid-template-columns:1fr}.tst > li,.tst > li.-wide{grid-column:span 1}}
.tcard{display:flex;flex-direction:column;height:100%;gap:var(--s-5);padding:var(--pad);
  background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.035)}
.tcard blockquote{font-size:15px;line-height:1.62;color:var(--ink-2);margin:0}
.tcard.-wide blockquote{font-size:16.5px;color:var(--ink)}
.tcard figcaption{display:flex;align-items:center;gap:var(--s-3);margin-top:auto;padding-top:var(--s-2)}
.tcard img{width:40px;height:40px;border-radius:50%;object-fit:cover;flex:none;
  border:1px solid var(--line-2);background:var(--bg-2)}
.tcard figcaption > span{display:block;min-width:0}
.tcard .who{display:block;font-family:var(--font-d);font-size:14px;font-weight:600;color:var(--ink);line-height:1.3}
.tcard .role{display:block;font-size:12.5px;color:var(--ink-2);line-height:1.35;margin-top:2px}

/* ============================================================================
   APP DETAIL PAGE
   ============================================================================ */
.app-hero{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);gap:var(--s-11);align-items:center}
@media (max-width:960px){.app-hero{grid-template-columns:1fr;gap:var(--s-9)}}
.app-mark{width:64px;height:64px;border-radius:14px;border:1px solid var(--line-2);
  background:var(--bg-2);padding:var(--s-3);object-fit:contain}
.app-badges{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-top:var(--s-5)}

/* video frame: premium border, reserved ratio, nothing loads until clicked */
.vframe{position:relative;border:1px solid var(--line-2);border-radius:var(--r-lg);
  overflow:hidden;background:var(--bg-2);box-shadow:inset 0 1px 0 rgba(255,255,255,.05)}
.vframe .ratio{position:relative;width:100%;padding-top:56.25%}
.vframe iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
.vframe .poster{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:var(--s-4);cursor:pointer;background:
  radial-gradient(120% 90% at 50% 0%,rgba(255,255,255,.05),transparent 70%),var(--bg-2)}
.vframe .poster:hover .play{border-color:var(--accent);background:rgba(255,74,10,.12)}
.vframe .play{width:60px;height:60px;border-radius:50%;border:1px solid var(--line-2);
  display:grid;place-items:center;background:rgba(255,255,255,.04);
  transition:border-color var(--t) var(--ease),background var(--t) var(--ease)}
.vframe .play svg{width:20px;height:20px;fill:var(--ink);margin-left:2px}
.vframe .vlabel{font-size:13px;color:var(--ink-2)}
.vnote{margin-top:var(--s-3);font-size:13px;color:var(--ink-3)}

/* ============================================================================
   FEATURE SCROLLYTELLING
   Sticky visual on the left, stepped copy on the right. Sticky only, never
   pinned, so the reader can always scroll away.
   ============================================================================ */
.scrolly{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:var(--s-10);align-items:start}
.scrolly-vis{position:sticky;top:calc(var(--nav-h) + var(--s-7))}
.scrolly-frame{position:relative;border:1px solid var(--line-2);border-radius:var(--r-lg);
  background:var(--bg-2);overflow:hidden;height:0;padding-top:62.5%;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05)}
.scrolly-frame img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transform:scale(1.015);transition:opacity 480ms var(--ease),transform 640ms var(--ease)}
.scrolly-frame img.is-on{opacity:1;transform:scale(1)}
.scrolly-prog{display:flex;gap:var(--s-2);margin-top:var(--s-4)}
.scrolly-prog span{flex:1;height:2px;border-radius:1px;background:rgba(255,255,255,.12);
  transition:background var(--t) var(--ease)}
.scrolly-prog span.is-on{background:var(--accent)}
.scrolly-steps{display:grid;gap:var(--s-11);padding-block:var(--pad)}
.fstep{opacity:.42;padding-left:var(--s-6);border-left:2px solid rgba(255,255,255,.16);
  transition:opacity var(--t-slow) var(--ease),border-color var(--t) var(--ease)}
.fstep.is-on{opacity:1;border-left-color:var(--accent)}
.fstep .h3{margin-bottom:var(--s-3)}
.fstep-n{font-family:var(--font-m);font-size:11px;letter-spacing:.12em;color:var(--ink-3);
  display:block;margin-bottom:var(--s-2)}
/* mobile: no sticky, image travels with its own step, no scroll trap */
.fstep-img{display:none;margin-top:var(--s-5);border:1px solid var(--line-2);
  border-radius:var(--r-md);overflow:hidden;background:var(--bg-2)}
.fstep-img img{width:100%;height:auto;display:block}
@media (max-width:960px){
  .scrolly{grid-template-columns:1fr;gap:0}
  .scrolly-vis{display:none}
  .scrolly-steps{gap:var(--s-9);padding-block:0}
  .fstep{opacity:1;padding-left:var(--s-5)}
  .fstep-img{display:block}
}

/* related apps */
.rel{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4)}
@media (max-width:860px){.rel{grid-template-columns:1fr}}
.rel-card{display:flex;gap:var(--s-4);align-items:flex-start;padding:var(--pad-sm);
  background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-lg);
  transition:border-color var(--t) var(--ease)}
.rel-card:hover{border-color:var(--line-2)}
.rel-card img{width:40px;height:40px;border-radius:10px;border:1px solid var(--line);
  background:var(--bg-2);padding:var(--s-2);object-fit:contain;flex:none}

/* ---------- ecosystem canvas stage ---------- */
.stage{position:relative}
.stage canvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;
  opacity:0;transition:opacity 1.2s var(--ease)}
.stage canvas.is-live{opacity:1}
.stage > .stage-content{position:relative;z-index:1}

/* ============================================================================
   V4 EXPERIENCE PASS
   ============================================================================ */

/* ---------- canvas readability: fade the scan out under the headline ---------- */
.stage canvas{
  -webkit-mask-image:linear-gradient(100deg,rgba(0,0,0,.18) 0%,rgba(0,0,0,.28) 26%,#000 62%,#000 100%);
  mask-image:linear-gradient(100deg,rgba(0,0,0,.18) 0%,rgba(0,0,0,.28) 26%,#000 62%,#000 100%);
  opacity:0}
.stage canvas.is-live{opacity:.9}

/* ============================================================================
   TESTIMONIAL SLIDER
   Active card in front, neighbours set back. Real buttons, real keyboard, all
   cards stay in the DOM and reachable. Depth is transform only, so no layout
   shift and nothing reflows as it moves.
   ============================================================================ */
.tsl{position:relative}
.tsl-viewport{position:relative;overflow:hidden;padding-block:var(--pad);
  perspective:1400px;perspective-origin:50% 50%}
.tsl-track{position:relative;height:var(--tsl-h,340px)}
.tsl-slide{position:absolute;top:0;bottom:0;left:50%;width:min(560px,86vw);
  transform:translate3d(-50%,0,0);transform-style:preserve-3d;
  transition:transform 620ms var(--ease),opacity 620ms var(--ease),filter 620ms var(--ease);
  will-change:transform,opacity}
.tsl-slide[data-pos="0"]{transform:translate3d(-50%,0,0) scale(1);opacity:1;z-index:3;filter:none}
.tsl-slide[data-pos="-1"]{transform:translate3d(calc(-50% - 300px),0,-180px) rotateY(13deg) scale(.93);opacity:.45;z-index:2;filter:brightness(.7)}
.tsl-slide[data-pos="1"]{transform:translate3d(calc(-50% + 300px),0,-180px) rotateY(-13deg) scale(.93);opacity:.45;z-index:2;filter:brightness(.7)}
.tsl-slide[data-pos="-2"]{transform:translate3d(calc(-50% - 520px),0,-340px) rotateY(18deg) scale(.86);opacity:.16;z-index:1;filter:brightness(.5)}
.tsl-slide[data-pos="2"]{transform:translate3d(calc(-50% + 520px),0,-340px) rotateY(-18deg) scale(.86);opacity:.16;z-index:1;filter:brightness(.5)}
.tsl-slide[data-pos="far"]{transform:translate3d(-50%,0,-500px) scale(.8);opacity:0;z-index:0;pointer-events:none}
.tsl-slide:not([data-pos="0"]) .tcard{pointer-events:none}

.tsl-card{height:100%;display:flex;flex-direction:column;gap:var(--s-5);
  padding:var(--s-9);background:var(--bg-1);border:1px solid var(--line-2);
  border-radius:var(--r-lg);box-shadow:inset 0 1px 0 rgba(255,255,255,.05)}
.tsl-card blockquote{font-size:17px;line-height:1.62;color:var(--ink);margin:0;flex:1}
.tsl-card figcaption{display:flex;align-items:center;gap:var(--s-4);padding-top:var(--s-4);
  border-top:1px solid var(--line)}
.tsl-card img{width:48px;height:48px;border-radius:50%;object-fit:cover;flex:none;
  border:1px solid var(--line-2);background:var(--bg-2)}
.tsl-card .who{display:block;font-family:var(--font-d);font-size:15px;font-weight:600;color:var(--ink)}
.tsl-card .role{display:block;font-size:13px;color:var(--ink-2);margin-top:4px}

.tsl-controls{display:flex;align-items:center;justify-content:center;gap:var(--s-5);
  margin-top:var(--s-7);flex-wrap:wrap}
@media (max-width:520px){.tsl-controls{gap:var(--s-3)}.tsl-dots{order:3;width:100%;justify-content:center}}
.tsl-btn{width:48px;height:48px;border-radius:var(--r-pill);border:1px solid var(--line-2);
  display:inline-flex;align-items:center;justify-content:center;color:var(--ink-2);
  transition:border-color var(--t-fast) var(--ease),color var(--t-fast) var(--ease),background var(--t-fast) var(--ease)}
.tsl-btn:hover{border-color:var(--line-3);color:var(--ink);background:rgba(255,255,255,.05)}
.tsl-btn .ic{width:16px;height:16px;fill:currentColor}
.tsl-dots{display:flex;gap:var(--s-2)}
/* the bar is 4px but the control is 44px tall, so it is tappable */
.tsl-dot{width:32px;height:44px;border:0;padding:0;background:none;display:grid;place-items:center;
  cursor:pointer;border-radius:var(--r-sm)}
.tsl-dot::before{content:"";display:block;width:28px;height:4px;border-radius:2px;
  background:rgba(255,255,255,.22);transition:background var(--t) var(--ease),width var(--t) var(--ease)}
.tsl-dot[aria-current="true"]::before{background:var(--accent);width:40px}
.tsl-dot:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

.tsl-count{font-family:var(--font-m);font-size:12px;color:var(--ink-2);letter-spacing:.08em;
  font-variant-numeric:tabular-nums}
@media (max-width:820px){
  .tsl-viewport{perspective:none;padding-block:var(--s-5)}
  .tsl-slide[data-pos="-1"],.tsl-slide[data-pos="1"],
  .tsl-slide[data-pos="-2"],.tsl-slide[data-pos="2"]{opacity:0;transform:translate3d(-50%,0,0) scale(.96);pointer-events:none}
  .tsl-card{padding:var(--s-7)}
  .tsl-card blockquote{font-size:15.5px}
}
@media (prefers-reduced-motion:reduce){
  .tsl-slide{transition:none}
}

/* ============================================================================
   COUNTERS
   ============================================================================ */
.counts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border-top:1px solid var(--line);
  border-bottom:1px solid var(--line)}
.counts > div{padding:var(--s-8) var(--pad) var(--s-8) 0}
.counts > div + div{border-left:1px solid var(--line);padding-left:var(--s-7)}
.counts dd{font-family:var(--font-d);font-size:clamp(34px,3.6vw,50px);font-weight:600;
  letter-spacing:-.045em;line-height:1;font-variant-numeric:tabular-nums;margin:0}
.counts .sfx{color:var(--ink-3);font-size:.62em;vertical-align:.22em;margin-left:2px}
.counts dt{margin-top:var(--s-3);font-size:14.5px;line-height:1.4;color:var(--ink-2)}
.counts .note{margin-top:var(--s-2);font-size:12.5px;color:var(--ink-3)}
@media (max-width:820px){.counts{grid-template-columns:1fr}
  .counts > div{padding:var(--pad) 0}
  .counts > div + div{border-left:0;border-top:1px solid var(--line);padding-left:0}}
@media (max-width:560px){.counts{grid-template-columns:1fr}
  .counts > div{padding:var(--pad) 0}
  .counts > div + div{border-left:0;border-top:1px solid var(--line);padding-left:0}}

/* ============================================================================
   BENTO
   Varied sizes on a 6 column base. Every span is declared, none are random.
   ============================================================================ */
.bento{display:grid;grid-template-columns:repeat(6,1fr);gap:var(--s-4)}
.bento > *{grid-column:span 2;display:flex;flex-direction:column;min-height:100%}
.bento > .-w3{grid-column:span 3}
.bento > .-w4{grid-column:span 4}
.bento > .-w6{grid-column:span 6}
.bento > .-tall{grid-row:span 2}
@media (max-width:980px){.bento{grid-template-columns:repeat(4,1fr)}
  .bento > *{grid-column:span 2}.bento > .-w3,.bento > .-w4,.bento > .-w6{grid-column:span 4}
  .bento > .-tall{grid-row:auto}}
@media (max-width:620px){.bento{grid-template-columns:1fr}
  .bento > *,.bento > .-w3,.bento > .-w4,.bento > .-w6{grid-column:span 1}}
.bento .mod{height:100%}
.bento-lead{gap:var(--s-5)}
.bento-lead .h3{font-size:clamp(20px,2.2vw,26px)}
.bento-stat{display:flex;flex-direction:column;justify-content:center;gap:var(--s-2)}
.bento-stat .v{font-family:var(--font-d);font-size:clamp(30px,3.4vw,44px);font-weight:600;
  letter-spacing:-.04em;line-height:1;font-variant-numeric:tabular-nums}
.bento-stat .k{font-size:14px;color:var(--ink-2);line-height:1.45}

/* ============================================================================
   FAQ, animated
   ============================================================================ */
.faq details{border-bottom:1px solid var(--line);border-radius:var(--r-md);
  transition:background var(--t) var(--ease),border-color var(--t) var(--ease)}
.faq details[open]{background:rgba(255,255,255,.022)}
.faq summary{padding:var(--s-6) var(--s-5)}
.faq details[open] summary{color:var(--ink)}
/* minmax(0, ...) is load bearing. A bare 0fr track keeps an automatic
   min-content minimum, which left every closed answer 24px tall and leaking a
   line of text through the card. */
.faq-wrap{display:grid;grid-template-rows:minmax(0,0fr);
  transition:grid-template-rows 280ms var(--ease)}
.faq-wrap > *{overflow:hidden;min-height:0}
.faq-item.is-open .faq-wrap{grid-template-rows:minmax(0,1fr)}
@media (prefers-reduced-motion:reduce){.faq-wrap{transition:none}}
.no-js .faq-wrap{grid-template-rows:minmax(0,1fr)}
.faq-a{padding:0 var(--s-5) var(--s-6);max-width:64ch;color:var(--ink-2);font-size:15.5px;line-height:1.65}
.faq summary:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;border-radius:var(--r-md)}

/* ============================================================================
   CTA PANEL, upgraded
   ============================================================================ */
.cta-band{position:relative}
/* The wash does not depend on the cursor, so it belongs on the element rather
   than on ::before. It used to occupy ::before and override .glow's proximity
   ring, which is why every CTA border was inert. */
.cta-band{background-image:radial-gradient(90% 140% at 12% 0%,rgba(255,74,10,.07),transparent 60%)}
.cta-band > *{position:relative;z-index:1}
.cta-band .h2{max-width:22ch}
.cta-band .body{max-width:56ch}
/* a slow edge sweep so the panel is never flat, independent of the cursor */
.cta-band::after{content:"";position:absolute;inset:-1px;border-radius:inherit;pointer-events:none;z-index:0;
  padding:1px;opacity:.55;
  background:conic-gradient(from var(--sweep,0deg) at 50% 50%,transparent 0deg,
    rgba(255,74,10,.75) 20deg,rgba(255,255,255,.5) 34deg,transparent 62deg);
  -webkit-mask:linear-gradient(#000,#000) content-box,linear-gradient(#000,#000);
  mask:linear-gradient(#000,#000) content-box,linear-gradient(#000,#000);
  -webkit-mask-composite:xor;mask-composite:exclude;
  animation:sweep 9s linear infinite}
@keyframes sweep{to{--sweep:360deg}}
@property --sweep{syntax:'<angle>';inherits:false;initial-value:0deg}
@media (prefers-reduced-motion:reduce){.cta-band::after{animation:none;opacity:.3}}

/* ---------- glow, steadier ---------- */
.glow::before{transition:opacity 260ms var(--ease)}
.glow::after{transition:opacity 260ms var(--ease)}

/* ---------- chips, final contrast pass ---------- */
.tag,.mod-tag,.cred-app{color:var(--ink-2)}
.mod-tag{font-size:11px;letter-spacing:.07em;padding:var(--s-1) var(--s-3);
  border-color:rgba(255,255,255,.18);background:rgba(255,255,255,.04)}
.tag{border-color:rgba(255,255,255,.2)}
.app-idx{color:var(--ink-2);font-size:11.5px}
.fstep-n,.scrolly-prog + .figcap{color:var(--ink-2)}

/* ---------- work grid control ---------- */
.work-shot img{aspect-ratio:16/10;object-fit:cover}
@supports not (aspect-ratio:1/1){.work-shot{height:0;padding-top:62.5%;position:relative}
  .work-shot img{position:absolute;inset:0;height:100%}}

/* ============================================================================
   V5 PASS
   ============================================================================ */

/* ---------- FAQ, button plus region ---------- */
.faq{border-top:0;display:grid;gap:var(--s-3)}
.faq-item{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--bg-1);
  overflow:hidden;transition:border-color var(--t) var(--ease),background var(--t) var(--ease)}
.faq-item.is-open{border-color:var(--line-2);background:rgba(255,255,255,.028)}
.faq-q{margin:0}
.faq-btn{display:flex;width:100%;align-items:flex-start;justify-content:space-between;
  gap:var(--s-6);padding:var(--pad-sm);text-align:left;cursor:pointer;
  font-family:var(--font-d);font-size:clamp(16px,1.5vw,18px);font-weight:600;
  letter-spacing:-.02em;line-height:1.35;color:var(--ink-2);
  transition:color var(--t-fast) var(--ease)}
.faq-btn:hover{color:var(--ink)}
.faq-item.is-open .faq-btn{color:var(--ink)}
.faq-btn:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;border-radius:var(--r-lg)}
.faq-btn .mk{flex:none;position:relative;width:14px;height:14px;margin-top:4px}
.faq-btn .mk::before,.faq-btn .mk::after{content:"";position:absolute;background:var(--ink-3);
  transition:transform var(--t) var(--ease),background var(--t) var(--ease)}
.faq-btn .mk::before{left:0;top:6.5px;width:14px;height:1.5px;border-radius:1px}
.faq-btn .mk::after{left:6.25px;top:0;width:1.5px;height:14px;border-radius:1px}
.faq-item.is-open .mk::after{transform:scaleY(0);background:var(--accent)}
.faq-item.is-open .mk::before{background:var(--accent)}
/* minmax(0, ...) is load bearing. A bare 0fr track keeps an automatic
   min-content minimum, which left every closed answer 24px tall and leaking a
   line of text through the card. */
.faq-wrap{display:grid;grid-template-rows:minmax(0,0fr);
  transition:grid-template-rows 280ms var(--ease)}
.faq-wrap > *{overflow:hidden;min-height:0}
.faq-item.is-open .faq-wrap{grid-template-rows:minmax(0,1fr)}
@media (prefers-reduced-motion:reduce){.faq-wrap{transition:none}}
.no-js .faq-wrap{grid-template-rows:minmax(0,1fr)}
.faq-a{padding:0 var(--pad-sm) var(--pad-sm);max-width:64ch;color:var(--ink-2);
  font-size:15.5px;line-height:1.65}
.no-js .faq-wrap{height:auto !important}

/* ---------- footer and secondary link underline ----------
   Previously an animation with `forwards`, which left the underline sitting at
   55% opacity after the pointer had gone. Now a plain transition on transform,
   so it retracts the moment you leave. Gap raised from 2px to 6px because at 2px
   the rule was touching the descenders. */
.foot ul a,.foot-links a{position:relative;padding-bottom:var(--s-3)}
.tlink{position:relative;padding-bottom:var(--s-3)}
.foot ul a::after,.tlink::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:linear-gradient(90deg,transparent,var(--accent),transparent);
  transform:scaleX(0);transform-origin:0 50%;
  transition:transform 340ms var(--ease)}
.foot ul a:hover::after,.foot ul a:focus-visible::after,
.tlink:hover::after,.tlink:focus-visible::after{transform:scaleX(1)}
.foot ul a:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:3px}
.foot-links{gap:var(--s-6) var(--s-7)}
.foot-brand .foot-links{margin-top:var(--s-7);display:grid;gap:var(--s-5);justify-items:start}
.foot-social{display:flex;flex-wrap:wrap;gap:var(--s-5)}
.foot-mail{display:block}
.foot-mail .lbl{display:block;font-family:var(--font-m);font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-4);margin-bottom:var(--s-3)}
@media (prefers-reduced-motion:reduce){
  .foot ul a::after,.tlink::after{transition:none}
}

/* ---------- bento: equal heights, no holes ---------- */
.bento{grid-auto-rows:1fr;align-items:stretch}
.bento > *{height:100%}
.bento .mod{justify-content:flex-start}
.bento .mod p{flex:1}
@media (max-width:980px){.bento{grid-auto-rows:auto}}

/* ---------- slider: equal card heights ---------- */
.tsl-slide{height:100%}
.tsl-card{height:100%;justify-content:space-between}
.tsl-card blockquote{display:block}

/* ---------- external affordance ---------- */
.foot ul a[target="_blank"] .ic,.tlink[target="_blank"] .ic{opacity:.85}

/* ---------- form states ---------- */
/* The status card. The mark sits beside the body; everything inside the body
   is normal text flow. The previous version was display:flex with bare text
   nodes injected into it, so each text run became a flex item and the message
   rendered as three narrow columns. */
.form-status{display:flex;gap:var(--s-4);align-items:flex-start;
  padding:var(--s-5);max-width:56ch}
.form-status .fs-mark{width:18px;height:18px;flex:none;margin-top:2px;
  border-radius:50%;display:grid;place-items:center;
  font:600 11px/1 var(--font-m);color:var(--bg)}
.form-status .fs-body{flex:1 1 auto;min-width:0}
.form-status .fs-title{margin:0 0 var(--s-2);font-size:15px;font-weight:600;
  color:var(--ink);line-height:1.35}
.form-status .fs-msg{margin:0;font-size:14px;line-height:1.6;color:var(--ink-2)}
.form-status .fs-inbox{display:block;margin-top:var(--s-2);
  font-family:var(--font-m);font-size:13px;color:var(--ink);
  overflow-wrap:anywhere}
.form-status .fs-next{margin:var(--s-3) 0 0;font-size:13.5px;line-height:1.55}
.form-status .ic{width:16px;height:16px;flex:none;margin-top:2px}
.form-status.-ok{border-color:rgba(107,189,110,.45);background:rgba(107,189,110,.06)}
.form-status.-ok .ic{fill:#6bbd6e}
.form-status.-ok .fs-mark{background:#6bbd6e}
.form-status.-err .fs-mark,.form-status.-warn .fs-mark,
.form-status.-config .fs-mark,.form-status.-spam .fs-mark{background:#e6b455}
.form-status.-fail .fs-mark,.form-status.-offline .fs-mark{background:#e08a7a}
@media (max-width:420px){
  .form-status{padding:var(--s-4);gap:var(--s-3)}
  .form-status .fs-title{font-size:14.5px}
}
/* The card must never be the thing that widens the page. min-width:0 lets the
   flex body shrink below its content, and the wrap rules give long addresses
   somewhere to break. Without these the card held the page open at 320. */
.form-status{max-width:min(56ch,100%);box-sizing:border-box}
.form-status .fs-body{min-width:0;overflow-wrap:break-word}
.form-status .fs-title,.form-status .fs-msg{overflow-wrap:break-word}
.form-status.-err{border-color:rgba(255,139,109,.45);background:rgba(255,139,109,.06)}
.form-status.-err .ic{fill:#ff8b6d}
.form-status.-busy .ic{fill:var(--ink-2)}
.btn[aria-disabled="true"]{opacity:.6;pointer-events:none}

/* ---------- sitewide starfield ----------
   Deep space. Stars everywhere, a few blinking, one falling every few seconds.
   No mask over the copy is needed, because there is no bright structure to mask:
   the field is even, so nothing ever crosses a line of text. */
.stars{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;contain:strict}
.stars::before{content:"";position:absolute;inset:0;
  background:radial-gradient(ellipse 120% 80% at 50% 108%,rgba(30,28,38,.45),transparent 62%)}
.stars canvas{position:absolute;inset:0;opacity:0;transition:opacity 1.4s var(--ease)}
.stars canvas.is-live{opacity:.9}
main,.foot{position:relative;z-index:1}

/* ---------- CTA panel: copy plus a product signal, one conversion moment ---------- */
.cta-band{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,300px);
  gap:clamp(24px,4vw,64px);align-items:center}
.cta-copy{min-width:0}
.cta-vis{min-width:0}
.cta-sig{width:100%;height:auto;display:block}
.cta-proof{margin-top:var(--s-6);padding-top:var(--s-4);border-top:1px solid var(--line);
  font-size:13.5px;color:var(--ink-2);max-width:46ch}
.cta-band .cta-actions{margin-top:var(--s-7)}
@media (max-width:860px){
  .cta-band{grid-template-columns:1fr}
  .cta-vis{order:-1;max-width:300px}
}
/* the flow inside the CTA signal moves, so the panel is never inert */
.cta-flow{stroke-dasharray:3 4;animation:ctaflow 2.6s linear infinite}
@keyframes ctaflow{to{stroke-dashoffset:-28}}
.cta-hot{animation:ctahot 2.6s var(--ease) infinite}
@keyframes ctahot{0%,55%{stroke-opacity:.35}70%{stroke-opacity:1}100%{stroke-opacity:.35}}
@media (prefers-reduced-motion:reduce){
  .cta-flow,.cta-hot{animation:none}
}

/* contextual inline CTA, used where a panel would be a duplicate */
.partner-inline{margin-top:var(--s-8)}

/* ---------- About, on the record ----------
   Was two half width cards floating in a lot of empty space. Now a dense fact
   table beside a single supporting card, which fills the row and reads faster. */
.facts{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,.75fr);gap:var(--s-4);align-items:start}
@media (max-width:900px){.facts{grid-template-columns:1fr}}
.facts-table{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--bg-1);
  overflow:hidden;box-shadow:inset 0 1px 0 rgba(255,255,255,.035)}
.facts-table > div{display:grid;grid-template-columns:minmax(0,150px) minmax(0,1fr);
  gap:var(--s-5);padding:var(--pad-sm);align-items:baseline}
.facts-table > div + div{border-top:1px solid var(--line)}
.facts-table dt{font-family:var(--font-d);font-size:14px;font-weight:600;color:var(--ink-2)}
.facts-table dd{margin:0;font-size:15px;color:var(--ink);line-height:1.5}
@media (max-width:620px){
  .facts-table > div{grid-template-columns:1fr;gap:var(--s-1);padding:var(--s-5)}
}
.facts-cert{display:flex;flex-direction:column;height:100%}
.facts-cert p{font-size:14px;line-height:1.58;color:var(--ink-2)}
.facts-cert-link{margin-top:auto;padding-top:var(--s-5)}

/* ---------- work card scan, so the proof grid is not inert ---------- */
.work-shot{position:relative}
.work-scan{position:absolute;left:0;right:0;top:0;height:34%;pointer-events:none;
  background:linear-gradient(180deg,transparent,rgba(255,74,10,.055) 55%,transparent);
  transform:translateY(-40%);opacity:0;transition:opacity 320ms var(--ease)}
.work-card:hover .work-scan{opacity:1;animation:workscan 2.4s var(--ease) infinite}
@keyframes workscan{0%{transform:translateY(-40%)}100%{transform:translateY(320%)}}
@media (hover:none),(pointer:coarse){.work-scan{display:none}}
@media (prefers-reduced-motion:reduce){
  .work-card:hover .work-scan{animation:none;opacity:.4;transform:translateY(60%)}
}

/* ---------- CTA: a signature corner rule, so the panel is not a plain box ---------- */
.cta-band{border-color:rgba(255,255,255,.14)}
.cta-band .cta-copy{position:relative}
.cta-band .cta-copy::before{content:"";position:absolute;left:calc(var(--s-7) * -1);top:.1em;
  width:2px;height:2.2em;border-radius:1px;
  background:linear-gradient(180deg,var(--accent),rgba(255,74,10,0))}
@media (max-width:860px){.cta-band .cta-copy::before{display:none}}
.cta-band .cta-copy{padding-left:var(--s-7)}
@media (max-width:860px){.cta-band .cta-copy{padding-left:0}}

/* ---------- tabbed FAQ: one accordion, several labelled groups ---------- */
.faq-tabs{display:grid;gap:var(--s-6)}
.faq-tablist{display:flex;flex-wrap:wrap;gap:var(--s-2);
  border-bottom:1px solid var(--line);padding-bottom:var(--s-4)}
.faq-tab{display:inline-flex;align-items:center;gap:var(--s-2);min-height:44px;
  padding:var(--s-2) var(--s-5);border:1px solid var(--line);border-radius:var(--r-pill);
  background:rgba(255,255,255,.02);color:var(--ink-2);font-size:14.5px;font-weight:600;
  cursor:pointer;transition:color var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease),background var(--t-fast) var(--ease)}
.faq-tab:hover{color:var(--ink);border-color:var(--line-2)}
.faq-tab[aria-selected="true"]{color:var(--ink);border-color:rgba(255,74,10,.5);
  background:rgba(255,74,10,.08)}
.faq-tab .n{font-family:var(--font-m);font-size:11px;color:var(--ink-3)}
.faq-tab[aria-selected="true"] .n{color:var(--accent)}
.faq-tab:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.faq-panel[hidden]{display:none}

/* ============================================================================
   BREATHING ROOM PASS
   Text was sitting hard against rules, card edges and section dividers in
   several places. Every value below is on the spacing scale.
   ============================================================================ */

/* the direct answer block: the rule was close to the copy and the copy was
   close to the section divider underneath it */
.answer{padding-left:var(--s-9);padding-block:var(--s-2)}
.answer .lbl{margin-bottom:var(--s-4)}
.answer p + p{margin-top:var(--s-4)}
.sec-tight{padding-block:var(--s-10)}
@media (max-width:900px){.sec-tight{padding-block:var(--s-9)}}
@media (max-width:600px){.answer{padding-left:var(--s-6)}}

/* FAQ: the answer began immediately under the button, and the last line sat on
   the card edge */
.faq-a{padding:var(--s-2) var(--pad-sm) var(--pad)}
.faq-btn{padding:var(--pad-sm)}
.faq-item.is-open .faq-btn{padding-bottom:var(--s-4)}
.faq{gap:var(--s-4)}

/* testimonial card: the caption was tight against its divider */
.tcard figcaption{padding-top:var(--s-5);gap:var(--s-4)}
.tcard .role{margin-top:var(--s-1)}
.tsl-card figcaption{padding-top:var(--s-5)}
.tsl-card{gap:var(--s-6);padding:var(--pad-lg)}

/* form note and status: 16px was not enough for four lines of copy */
.form-note,.form-status{line-height:1.6}
.field{gap:var(--s-3)}
.field .help{margin-top:var(--s-1)}

/* module and mod cards: tags were close to the bottom edge */
.mod{padding:var(--pad)}
.mod-tags{padding-top:var(--s-6)}

/* cluster lists and fact rows */
.cluster-g{padding-top:var(--s-7)}
.facts-table > div{padding:var(--pad-sm)}

/* CTA proof line sat directly on its rule */
.cta-proof{margin-top:var(--s-7);padding-top:var(--s-5)}

/* ---------- Turnstile ---------- */
.cf-turnstile{min-height:0}
.cf-turnstile[hidden]{display:none}
.cf-turnstile iframe{border-radius:var(--r-md)}

/* ============================================================================
   HOVER BASELINE
   The proximity ring is built from conic-gradient plus mask plus mask-composite.
   That is three newer CSS features stacked, and if any one of them does not
   paint the card gets no hover response at all. This is the floor underneath it:
   plain border-colour and box-shadow, which rasterise everywhere. The ring sits
   on top when it is available.
   ============================================================================ */
@media (hover: hover) and (pointer: fine) {
  .glow{transition:border-color 200ms var(--ease),box-shadow 200ms var(--ease),
    background-color 200ms var(--ease)}
  .mod.glow:hover,.appcard.glow:hover,.rel-card.glow:hover,
  .tcard.glow:hover,.tsl-card.glow:hover,.faq-item.glow:hover,
  .work-card:hover,.cta-band.glow:hover{
    border-color:rgba(255,255,255,.24);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.06),
               0 0 0 1px rgba(255,74,10,.16),
               0 18px 44px -28px rgba(0,0,0,.9)}
  .btn.glow:hover{box-shadow:inset 0 1px 0 rgba(255,255,255,.22),
               0 0 0 1px rgba(255,255,255,.16)}
}
@media (prefers-reduced-motion:reduce){.glow{transition:none}}

/* ============================================================================
   GLOSSARY
   Search, an A to Z rail, and the terms. Everything is server rendered, so with
   JavaScript off every definition is present; the script only filters.
   ============================================================================ */
.glossary{display:grid;gap:var(--s-9)}
.gl-search{position:relative}
.gl-search input{width:100%;background:var(--bg-1);border:1px solid var(--line-2);
  border-radius:var(--r-lg);padding:var(--s-6) var(--s-6) var(--s-6) var(--s-12);
  font-family:var(--font-b);font-size:17px;color:var(--ink);
  transition:border-color var(--t-fast) var(--ease)}
.gl-search input::placeholder{color:var(--ink-4)}
.gl-search input:hover{border-color:var(--line-3)}
.gl-search input:focus{outline:2px solid var(--accent);outline-offset:2px;border-color:transparent}
.gl-search input::-webkit-search-cancel-button{filter:invert(1) opacity(.4)}
.gl-icon{position:absolute;left:var(--s-6);top:50%;transform:translateY(-50%);
  width:18px;height:18px;pointer-events:none}
.gl-icon .ic{width:18px;height:18px;fill:var(--ink-4)}
.gl-count{margin-top:var(--s-4);font-family:var(--font-m);font-size:11px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3)}

.gl-body{display:grid;grid-template-columns:minmax(0,64px) minmax(0,1fr);gap:var(--s-9)}
.gl-rail{position:sticky;top:calc(var(--nav-h) + var(--s-7));align-self:start;
  display:grid;gap:var(--s-1);max-height:70vh;overflow:auto}
.gl-letter{display:grid;place-items:center;width:36px;height:36px;border-radius:var(--r-sm);
  font-family:var(--font-m);font-size:12px;color:var(--ink-3);
  transition:color var(--t-fast) var(--ease),background var(--t-fast) var(--ease)}
.gl-letter:hover{color:var(--ink);background:rgba(255,255,255,.06)}
.gl-letter.is-empty{opacity:.3;pointer-events:none}
.gl-letter:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

.gl-groups{display:grid;gap:var(--s-10);min-width:0}
.gl-group{scroll-margin-top:calc(var(--nav-h) + var(--s-7))}
.gl-group[hidden]{display:none}
.gl-initial{font-family:var(--font-d);font-size:clamp(28px,3vw,38px);font-weight:600;
  letter-spacing:-.03em;color:var(--ink-4);margin-bottom:var(--s-6);
  padding-bottom:var(--s-4);border-bottom:1px solid var(--line)}
.gl-list{display:grid;gap:var(--s-4);margin:0}
.gl-term{padding:var(--pad);border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--bg-1);box-shadow:inset 0 1px 0 rgba(255,255,255,.035);
  transition:border-color var(--t) var(--ease)}
.gl-term[hidden]{display:none}
.gl-term dt{font-family:var(--font-d);font-size:18px;font-weight:600;letter-spacing:-.02em;
  color:var(--ink);scroll-margin-top:calc(var(--nav-h) + var(--s-8))}
.gl-term dd{margin:var(--s-4) 0 0;font-size:15.5px;line-height:1.65;color:var(--ink-2);max-width:68ch}
.gl-term mark{background:rgba(255,74,10,.22);color:var(--ink);border-radius:2px;padding:0 2px}
.gl-empty{padding:var(--pad-lg);border:1px dashed var(--line-2);border-radius:var(--r-lg);
  color:var(--ink-2);text-align:center}
@media (hover:hover) and (pointer:fine){.gl-term:hover{border-color:var(--line-2)}}
@media (max-width:820px){
  .gl-body{grid-template-columns:1fr;gap:var(--s-7)}
  .gl-rail{position:static;display:flex;flex-wrap:wrap;max-height:none;
    padding-bottom:var(--s-4);border-bottom:1px solid var(--line)}
  .gl-search input{padding:var(--s-5) var(--s-5) var(--s-5) var(--s-11);font-size:16px}
}

/* ============================================================================
   CONTENT SYSTEM: glossary, blog overview, article template
   ============================================================================ */

/* ---------- shared filter chips ---------- */
.gl-chips{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-top:var(--s-5)}
.gl-chip{display:inline-flex;align-items:center;gap:var(--s-2);min-height:40px;
  padding:var(--s-2) var(--s-5);border:1px solid var(--line);border-radius:var(--r-pill);
  background:rgba(255,255,255,.02);color:var(--ink-2);font-size:13.5px;font-weight:600;cursor:pointer;
  transition:color var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease),background var(--t-fast) var(--ease)}
.gl-chip:hover{color:var(--ink);border-color:var(--line-2)}
.gl-chip[aria-pressed="true"]{color:var(--ink);border-color:rgba(255,74,10,.5);background:rgba(255,74,10,.08)}
.gl-chip .n{font-family:var(--font-m);font-size:11px;color:var(--ink-3)}
.gl-chip[aria-pressed="true"] .n{color:var(--accent)}
.gl-chip:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

/* ---------- glossary ---------- */
/* The gap between the standfirst and the search box measured 144px, which was
   80 from .phead plus 64 from .sec-tight. Both are global, so the reduction is
   scoped to -hub, which appears on glossary.html only. */
.phead.-hub{padding-bottom:var(--s-8)}
.sec-tight.-hub{padding-top:var(--s-2)}
.glossary{display:grid;gap:var(--s-8)}
.gl-search{position:relative}
.gl-search input{width:100%;background:var(--bg-1);border:1px solid var(--line-2);
  border-radius:var(--r-lg);padding:var(--s-6);font-family:var(--font-b);font-size:17px;color:var(--ink)}
.gl-search input::placeholder{color:var(--ink-4)}
.gl-search input:focus{outline:2px solid var(--accent);outline-offset:2px;border-color:transparent}
.gl-count{margin-top:var(--s-4);font-family:var(--font-m);font-size:11px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3)}
.gl-body{display:grid;grid-template-columns:minmax(0,56px) minmax(0,1fr);gap:var(--s-8)}
.gl-rail{position:sticky;top:calc(var(--nav-h) + var(--s-7));align-self:start;display:grid;gap:2px;
  max-height:72vh;overflow:auto}
.gl-letter{display:grid;place-items:center;width:34px;height:32px;border-radius:var(--r-sm);
  font-family:var(--font-m);font-size:12px;color:var(--ink-3);
  transition:color var(--t-fast) var(--ease),background var(--t-fast) var(--ease)}
.gl-letter:hover{color:var(--ink);background:rgba(255,255,255,.06)}
.gl-letter.is-empty{opacity:.25;pointer-events:none}
/* letters the glossary holds no term for: present, dimmed, not focusable */
.gl-letter.is-off{color:var(--ink-4);opacity:.42;pointer-events:none;cursor:default}
.gl-letter.is-current{color:var(--ink);background:var(--accent-dim);
  box-shadow:inset 0 0 0 1px var(--accent-bd)}
.gl-letter:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* a deep linked term gets a brief mark so you can see where you landed */
.gl-term.is-target{box-shadow:inset 0 0 0 1px var(--accent-bd);
  background:var(--accent-dim);border-radius:var(--r-lg)}
@media (prefers-reduced-motion:no-preference){
  .gl-term.is-target{transition:background var(--t-slow) var(--ease),box-shadow var(--t-slow) var(--ease)}}
.gl-groups{display:grid;gap:var(--s-9);min-width:0}
.gl-group{scroll-margin-top:calc(var(--nav-h) + var(--s-7))}
.gl-group[hidden]{display:none}
.gl-initial{font-family:var(--font-d);font-size:clamp(26px,3vw,34px);font-weight:600;
  letter-spacing:-.03em;color:var(--ink-4);margin-bottom:var(--s-6);padding-bottom:var(--s-4);
  border-bottom:1px solid var(--line)}
.gl-list{display:grid;gap:var(--s-4);margin:0}
.gl-term{padding:var(--pad);border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--bg-1);box-shadow:inset 0 1px 0 rgba(255,255,255,.035)}
.gl-term[hidden]{display:none}
.gl-term dt{display:flex;align-items:baseline;gap:var(--s-4);flex-wrap:wrap;
  font-family:var(--font-d);font-size:18px;font-weight:600;letter-spacing:-.02em;color:var(--ink);
  scroll-margin-top:calc(var(--nav-h) + var(--s-8))}
.gl-cat{font-family:var(--font-m);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-3);border:1px solid var(--line);border-radius:var(--r-sm);padding:2px var(--s-3)}
.gl-term dd{margin:var(--s-4) 0 0}
.gl-def{font-size:15.5px;line-height:1.65;color:var(--ink-2);max-width:68ch}
.gl-matters{margin-top:var(--s-4);font-size:14.5px;line-height:1.6;color:var(--ink-2);max-width:68ch}
.gl-matters .lbl,.gl-rels .lbl,.gl-notice .lbl,.gl-mistake .lbl,.gl-note .lbl{display:block;font-family:var(--font-m);font-size:10px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--ink-4);margin-bottom:var(--s-2)}
.gl-rels{margin-top:var(--s-5);display:flex;flex-wrap:wrap;gap:var(--s-2)}
.gl-rels .lbl{width:100%}
.gl-rel{font-size:12.5px;color:var(--ink-2);border:1px solid var(--line);border-radius:var(--r-pill);
  padding:var(--s-1) var(--s-4);transition:color var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease)}
.gl-rel:hover{color:var(--ink);border-color:var(--line-2)}
.gl-rel:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.gl-link{display:inline-block;margin-top:var(--s-5)}
.gl-empty{padding:var(--pad-lg);border:1px dashed var(--line-2);border-radius:var(--r-lg);
  color:var(--ink-2);text-align:center}
@media (max-width:820px){
  .gl-body{grid-template-columns:1fr;gap:var(--s-6)}
  .gl-rail{position:static;display:flex;flex-wrap:wrap;max-height:none;
    padding-bottom:var(--s-4);border-bottom:1px solid var(--line)}
}

/* ---------- blog overview ---------- */
.blog-subhead{margin-bottom:var(--s-6)}
/* Full width hero article, then three balanced cards. Four children, four
   cells, nothing empty.
   The previous version left track 3 of row 2 empty at 362x629 because it had
   three children in a three track grid with the lead spanning all three.
   A two track version with the lead spanning both rows also failed: it made
   the lead 1349px tall against a natural content height of about 760, and a
   16:9 figure cannot grow to fill a 666px panel, so it traded an empty grid
   cell for 327px of dead panel plus 264px of stretched excerpt. */
.feat-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4);
  align-items:stretch}
.feat-grid > li{display:flex;min-width:0}
.feat-grid > li > .post-card{width:100%}
.feat-grid > li:first-child{grid-column:span 3}
@media (max-width:900px){.feat-grid{grid-template-columns:1fr}
  .feat-grid > li:first-child{grid-column:span 1}}
.blog-controls{margin-top:var(--s-11);padding-top:var(--s-9);border-top:1px solid var(--line);
  display:grid;gap:var(--s-5)}
.post-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4);margin-top:var(--s-8)}
.post-grid.-three{grid-template-columns:repeat(3,minmax(0,1fr))}
@media (max-width:980px){.post-grid,.post-grid.-three{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:680px){.post-grid,.post-grid.-three{grid-template-columns:1fr}}
.post-grid > li[hidden]{display:none}
.blog-more{margin-top:var(--s-8);text-align:center}
.post-card{display:flex;flex-direction:column;height:100%;background:var(--bg-1);
  border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;
  transition:border-color var(--t-fast) var(--ease)}
.post-card:hover{border-color:var(--line-2)}
/* Figure panels were free height, so a small illustration sat in a tall box
   next to a large one. Fixed ratio and centred, so the row reads evenly. */
.post-shot{display:grid;place-items:center;background:var(--bg-2);
  border-bottom:1px solid var(--line);padding:var(--pad-xs);
  aspect-ratio:16/9;position:relative}
.post-shot svg{width:100%;height:auto;max-height:100%;display:block}
/* the accent is rationed: one hairline, only on pointer hover or keyboard focus */
.post-shot::after{content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:linear-gradient(90deg,var(--accent),transparent 62%);
  transform:scaleX(0);transform-origin:left;
  transition:transform var(--t) var(--ease)}
@media (hover:hover){.post-card:hover .post-shot::after{transform:scaleX(1)}}
.post-card:focus-within .post-shot::after{transform:scaleX(1)}
/* The house contract is that reduced motion keeps colour and opacity and drops
   movement. The global block strips transform from the transition list, which
   would make this line snap in. Fade it instead, so the affordance still
   reads without anything moving. */
@media (prefers-reduced-motion:reduce){
  .post-shot::after{transform:scaleX(1);opacity:0}
  @media (hover:hover){.post-card:hover .post-shot::after{opacity:1}}
  .post-card:focus-within .post-shot::after{opacity:1}
}
/* hero article: -wide gives the side by side split, -lead the type scale.
   The figure panel keeps its natural height here rather than a fixed ratio,
   because it is beside the copy rather than above it. */
.post-card.-lead .post-shot{aspect-ratio:auto;padding:var(--pad)}
.post-card.-lead .post-body{padding:var(--pad-lg);justify-content:center}
.post-card.-lead .post-excerpt{font-size:16px;line-height:1.65;flex:0 0 auto}
.post-card.-wide{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);align-items:center}
.post-card.-wide .post-shot{border-bottom:0;border-right:1px solid var(--line);height:100%}
@media (max-width:900px){.post-card.-wide{grid-template-columns:1fr}
  .post-card.-wide .post-shot{border-right:0;border-bottom:1px solid var(--line)}}
.post-body{display:flex;flex-direction:column;gap:var(--s-3);padding:var(--pad);flex:1}
.post-meta{display:flex;align-items:center;gap:var(--s-3);font-family:var(--font-m);
  font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3)}
.post-cat{color:var(--accent)}
.post-dot{width:3px;height:3px;border-radius:50%;background:var(--ink-4)}
.post-title{font-family:var(--font-d);font-size:clamp(18px,1.8vw,21px);font-weight:600;
  letter-spacing:-.025em;line-height:1.3;color:var(--ink)}
.post-card.-wide .post-title{font-size:clamp(22px,2.4vw,30px)}
.post-card.-lead .post-title{font-size:clamp(24px,2.7vw,34px);line-height:1.22}
.post-title a:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:4px}
.post-excerpt{font-size:14.5px;line-height:1.6;color:var(--ink-2);flex:1;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.post-card.-lead .post-excerpt{-webkit-line-clamp:4}
.post-tags{display:flex;flex-wrap:wrap;gap:var(--s-2);padding-top:var(--s-3)}
.post-by{display:flex;flex-wrap:wrap;gap:var(--s-3);align-items:baseline;
  padding-top:var(--s-5);border-top:1px solid var(--line);margin-top:auto}
.post-by .who{font-size:13.5px;font-weight:600;color:var(--ink-2)}
.post-by .when{font-family:var(--font-m);font-size:11px;color:var(--ink-4)}

/* ---------- hub cards on resources ---------- */
.hub{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-4)}
@media (max-width:820px){.hub{grid-template-columns:1fr}}
.hub-card{display:flex;flex-direction:column}
.hub-go{margin-top:auto;padding-top:var(--s-6);color:var(--accent);font-size:14px;font-weight:600;
  display:inline-flex;align-items:center;gap:var(--s-2)}
.hub-go .ic{width:12px;height:12px;fill:currentColor}

/* ---------- article ---------- */
.art-hero{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.9fr);gap:var(--s-10);align-items:center}
@media (max-width:960px){.art-hero{grid-template-columns:1fr;gap:var(--s-8)}}
.art-title{max-width:20ch}
.art-figure{border:1px solid var(--line-2);border-radius:var(--r-lg);background:var(--bg-2);
  padding:var(--pad-sm);box-shadow:inset 0 1px 0 rgba(255,255,255,.05)}
.art-figure svg,.art-fig{width:100%;height:auto;display:block}
.art-by{display:flex;flex-wrap:wrap;gap:var(--s-2) var(--s-5);align-items:baseline;margin-top:var(--s-7)}
.art-by .who{font-family:var(--font-d);font-size:15px;font-weight:600;color:var(--ink)}
.art-by .role{font-size:13.5px;color:var(--ink-2)}
.art-by .when{font-family:var(--font-m);font-size:11px;color:var(--ink-4)}
.art-share{display:flex;align-items:center;gap:var(--s-3);margin-top:var(--s-7)}
.art-share .lbl{font-family:var(--font-m);font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-4)}
.share-btn{display:grid;place-items:center;width:40px;height:40px;border-radius:var(--r-pill);
  border:1px solid var(--line);color:var(--ink-2);font-size:12px;font-weight:600;cursor:pointer;
  background:none;transition:color var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease)}
.share-btn:hover{color:var(--ink);border-color:var(--line-2)}
.share-btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.share-btn.is-copied{color:var(--accent);border-color:rgba(255,74,10,.5)}

/* TOC moved to the right rail. Body now starts at the wrap edge, so the
   article title and the first paragraph share a left edge and the hero stops
   reading as a detached block. */
.art-layout{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,240px);gap:var(--s-10);
  padding-block:var(--s-10)}
.art-layout > .art-toc{grid-column:2;grid-row:1}
.art-layout > .art-body{grid-column:1;grid-row:1}
@media (max-width:960px){.art-layout{grid-template-columns:1fr;gap:var(--s-7);padding-block:var(--s-8)}
  .art-layout > .art-toc{grid-column:1;grid-row:1}
  .art-layout > .art-body{grid-column:1;grid-row:2}}
.art-toc{position:sticky;top:calc(var(--nav-h) + var(--s-7));align-self:start;
  max-height:calc(100vh - var(--nav-h) - var(--s-12));overflow-y:auto;
  overscroll-behavior:contain}
@media (max-width:960px){.art-toc{position:static;max-height:none;overflow:visible;
  border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--pad-sm);background:var(--bg-1)}}
.art-toc-h{font-family:var(--font-m);font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-4);margin-bottom:var(--s-5)}
.art-toc ol{display:grid;gap:var(--s-3);counter-reset:toc;margin:0}
.art-toc li{list-style:none}
.art-toc a{display:block;font-size:14px;line-height:1.45;color:var(--ink-3);
  padding-left:var(--s-4);border-left:2px solid var(--line);
  transition:color var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease)}
.art-toc a:hover{color:var(--ink-2);border-left-color:var(--line-2)}
.art-toc a.is-current{color:var(--ink);border-left-color:var(--accent)}
.art-toc a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.art-toc-cta{margin-top:var(--s-8);padding-top:var(--s-6);border-top:1px solid var(--line)}
.art-toc-cta .tiny{margin-bottom:var(--s-4)}

/* Prose stays at 68ch. Block furniture breaks out to the full track via
   .art-bleed, which is what gives the page narrow prose and wider furniture. */
.art-body{min-width:0}
.art-body > *{max-width:68ch}
.art-body > .art-bleed,.art-body > .art-takeaways,
.art-body > .art-terms,.art-body > .art-author{max-width:none}
.art-body h2{font-family:var(--font-d);font-size:clamp(21px,2.4vw,27px);font-weight:600;
  letter-spacing:-.025em;line-height:1.28;color:var(--ink);
  margin-top:var(--s-10);margin-bottom:var(--s-5);scroll-margin-top:calc(var(--nav-h) + var(--s-7))}
.art-body h2:first-of-type{margin-top:var(--s-9)}
/* Section gaps were 48, 72 or 88px depending on which element happened to come
   before the heading. One value now, whatever precedes it. :not() is here to
   outrank .art-body h2:first-of-type, which otherwise wins at (0,2,1) and
   leaves the first section gap at 48 while every other gap is 64. */
.art-body > * + h2:not(.art-take-h){margin-top:var(--s-10)}
.art-body p{font-size:16.5px;line-height:1.72;color:var(--ink-2);margin-bottom:var(--s-5)}
.art-list{display:grid;gap:var(--s-3);margin:0 0 var(--s-6);padding-left:var(--s-6)}
.art-list li{font-size:16px;line-height:1.65;color:var(--ink-2)}
.art-list li::marker{color:var(--accent)}
.art-quote{margin:var(--s-8) 0;padding:var(--pad);padding-left:var(--pad-lg);border-left:2px solid var(--accent);
  background:rgba(255,255,255,.022);border-radius:0 var(--r-md) var(--r-md) 0}
.art-quote p{font-family:var(--font-d);font-size:clamp(17px,2vw,20px);line-height:1.5;
  color:var(--ink);margin:0;letter-spacing:-.015em}
.art-takeaways{border:1px solid var(--line-2);border-radius:var(--r-lg);padding:var(--pad-sm);
  background:var(--bg-1);box-shadow:inset 0 1px 0 rgba(255,255,255,.04)}
/* .art-take-h is an h2 inside .art-body, so the bare class (0,1,0) lost every
   shared property to .art-body h2 (0,1,1) regardless of source order. It was
   rendering at 27px display type with 48px of inherited section margin inside
   a 24px padded box. Element plus class here so the component rule wins. */
.art-body h2.art-take-h,.art-take-h{font-family:var(--font-m);font-size:10px;font-weight:500;
  line-height:1.2;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-4);margin:0 0 var(--s-4)}
.art-takeaways ol{display:grid;gap:var(--s-4);margin:0;padding-left:0;list-style:none;
  counter-reset:take}
.art-takeaways li{font-size:15.5px;line-height:1.6;color:var(--ink-2);
  display:grid;grid-template-columns:22px minmax(0,1fr);gap:var(--s-3);counter-increment:take}
.art-takeaways li::before{content:counter(take,decimal-leading-zero);
  font-family:var(--font-m);font-size:11px;letter-spacing:.06em;color:var(--accent);
  line-height:1.85}
.art-terms{margin-top:var(--s-10);padding-top:var(--s-7);border-top:1px solid var(--line)}
/* same collision as .art-take-h: a bare class on an h2 inside .art-body loses
   every shared property to .art-body h2. Found by measuring, not by reading. */
.art-body h2.art-terms-h,.art-terms-h{font-family:var(--font-m);font-size:10px;font-weight:500;
  line-height:1.2;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-4);margin:0 0 var(--s-5)}
.art-terms p{display:flex;flex-wrap:wrap;gap:var(--s-2)}
.art-author{margin-top:var(--s-9);padding:var(--pad);border:1px solid var(--line);
  border-radius:var(--r-lg);background:var(--bg-1)}
.art-author .lbl{font-family:var(--font-m);font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-4);margin-bottom:var(--s-4)}
.art-author .who{font-family:var(--font-d);font-size:17px;font-weight:600;color:var(--ink);margin-bottom:2px}
.art-author .role{font-size:13.5px;color:var(--accent);margin-bottom:var(--s-4)}
.art-author .bio{font-size:15px;line-height:1.65;color:var(--ink-2);margin:0}
.art-wrap{max-width:68ch}

/* ============================================================================
   V3 CONTENT SYSTEM: article furniture, blog masthead, glossary below fold
   Every selector below appears on blog.html, glossary.html or blog/* only.
   ========================================================================= */

/* ---------- article table ---------- */
.art-tablewrap{margin:var(--s-8) 0;overflow-x:auto;border:1px solid var(--line);
  border-radius:var(--r-lg);background:var(--bg-1)}
.art-tablewrap:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.art-table{width:100%;border-collapse:collapse;font-size:14.5px;line-height:1.55}
.art-table caption{caption-side:top;text-align:left;padding:var(--pad-sm) var(--pad-sm) 0;
  font-family:var(--font-m);font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-4)}
.art-table th,.art-table td{padding:var(--s-4) var(--s-5);text-align:left;vertical-align:top;
  border-bottom:1px solid var(--line)}
.art-table thead th{font-family:var(--font-m);font-size:10px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-3);font-weight:500;
  border-bottom:1px solid var(--line-2);white-space:nowrap}
.art-table tbody th{color:var(--ink);font-weight:600;font-size:14.5px;min-width:10ch}
.art-table td{color:var(--ink-2)}
.art-table tbody tr:last-child th,.art-table tbody tr:last-child td{border-bottom:0}

/* ---------- article checklist ---------- */
.art-check{margin:var(--s-8) 0;padding:var(--pad-sm);border:1px solid var(--line-2);
  border-radius:var(--r-lg);background:var(--bg-1);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.04)}
.art-check-h{font-family:var(--font-m);font-size:10px;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-4);margin:0 0 var(--s-5)}
.art-check ul{display:grid;gap:var(--s-4);margin:0;padding:0;list-style:none}
.art-check li{display:grid;grid-template-columns:18px minmax(0,1fr);gap:var(--s-4);
  font-size:15.5px;line-height:1.6;color:var(--ink-2)}
.art-check li::before{content:"";width:12px;height:12px;margin-top:var(--s-1);border-radius:3px;
  border:1px solid var(--accent-bd);background:var(--accent-dim)}

/* ---------- blog hero masthead ---------- */
.phead-grid.-mast{grid-template-columns:minmax(0,1fr) minmax(0,.82fr);align-items:center;
  gap:var(--s-10)}
@media (max-width:900px){.phead-grid.-mast{grid-template-columns:1fr;gap:var(--s-8)}}
.blog-mast{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--bg-1);
  padding:var(--pad);display:grid;gap:var(--s-5)}
.mast-h{font-family:var(--font-m);font-size:10px;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-4);margin:0}
.mast-split{display:grid;gap:var(--s-4);margin:0;padding:0;list-style:none}
.mast-who{display:grid;gap:var(--s-4);align-items:baseline}
.mast-who .w{display:grid;gap:2px;min-width:0}
.mast-who .nm{font-family:var(--font-d);font-size:15px;font-weight:600;color:var(--ink)}
.mast-who .ln{font-size:13px;line-height:1.45;color:var(--ink-3)}
.mast-foot{margin:0;font-family:var(--font-m);font-size:10px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--ink-4)}
.phead.-hub{padding-bottom:var(--s-8)}
.sec-tight.-hub{padding-top:var(--s-2)}

/* ---------- glossary below the fold ---------- */
/* The page used to run 54,000px as one undifferentiated column. Detail now
   collapses, letter groups carry a count and a way back up, and an active
   search force opens every match so nothing hides behind the control. */
.gl-ghead{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-5);
  padding-bottom:var(--s-4);border-bottom:1px solid var(--line);margin-bottom:var(--s-6);
  position:sticky;top:calc(var(--nav-h) + var(--s-2));z-index:2;
  background:linear-gradient(var(--bg) 72%,rgba(8,8,10,0))}
.gl-ghead .gl-initial{margin:0}
.gl-gmeta{display:flex;align-items:baseline;gap:var(--s-5);font-family:var(--font-m);
  font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-4)}
.gl-top{color:var(--ink-3)}
.gl-top:hover{color:var(--ink)}
.gl-top:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.gl-more{display:inline-flex;align-items:center;gap:var(--s-2);margin-top:var(--s-4);
  padding:0;background:none;border:0;cursor:pointer;
  font-family:var(--font-m);font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-4);transition:color var(--t-fast) var(--ease)}
.gl-more:hover{color:var(--ink-2)}
.gl-more:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.gl-more .mk{width:7px;height:7px;border-right:1px solid currentColor;
  border-bottom:1px solid currentColor;transform:rotate(45deg) translate(-2px,-2px);
  transition:transform var(--t-fast) var(--ease)}
.gl-more[aria-expanded="true"] .mk{transform:rotate(-135deg) translate(-2px,-2px)}
.gl-detail[hidden]{display:none}
.gl-list{display:grid;gap:var(--s-4)}
@media (min-width:1100px){
  .gl-list{grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}
  .gl-term.is-open{grid-column:span 2}
}
.blog-subnote{margin:var(--s-3) 0 var(--s-7);font-size:14.5px;color:var(--ink-3);max-width:52ch}
.blog-feat{margin-bottom:var(--s-4)}
.blog-subhead{margin-bottom:0}
.blog-mast{gap:var(--s-4);padding:var(--pad-sm)}

/* ============================================================================
   SHARED CTA, BUTTON AND CARD SYSTEM
   These were scoped behind .-content in the content polish pass so that
   product pages could not move. That scoping is now removed: the fixes apply
   to the shared CTA, button and card components sitewide, and an expected-diff
   guard proves nothing outside those components moved.
   See SHARED_CTA_BUTTON_CARD_AUDIT.md.
   ========================================================================= */

/* ---------- full card click ---------- */
/* The card has one destination and no interactive descendants (tags are
   spans), which is the condition that makes card-as-anchor safe. */
.post-card{position:relative;cursor:pointer;color:inherit;text-decoration:none}
.post-card .post-body,.post-card .post-shot,.post-card .post-meta,
.post-card .post-tags,.post-card .post-by{display:flex}
.post-card .post-excerpt{display:-webkit-box}
/* The ring was drawn at inset:-1px, one pixel outside the border box, and the
   card's own overflow:hidden clipped it away. Moving the clip to .post-shot
   unclipped it and cost 48px of card height, which shifted everything below,
   so the clip stays on the card and the ring comes inside instead. A 1px band
   on the inside edge is visually equivalent and changes no geometry. */
/* The ring is a 1px band at inset:0, which is exactly where the card's own 1px
   border sits, so on hover two lines occupied one pixel and the corner radius
   clipped the overlap into a ragged edge. Fading the base border out while the
   ring is lit means one clean line at a time. border-color only, so nothing
   moves. */
.post-card.glow::before{inset:0;border-radius:var(--r-lg)}
/* Fading the base border out on hover was worse: the ring only lights when the
   pointer is outside the 55 percent centre dead zone, so a card hovered in the
   middle lost its edge entirely. The border stays. The ring is opaque accent
   over a 0.07 white hairline, so it simply covers it. */
@media (hover:hover){
  .post-card:hover{border-color:var(--line-3);background-color:rgba(255,255,255,.024)}
  .post-card:hover .post-title{color:var(--ink)}
}
.post-card:focus-visible{outline:2px solid var(--accent);outline-offset:3px;
  border-color:var(--line-3)}
/* focus must produce the same border treatment as hover, not a weaker one */
.post-card:focus-visible{--on:1}

/* ---------- the TOC rail must not style things that are not TOC links ------ */
/* .art-toc a (0,1,1) was beating .btn (0,1,0) and forcing display:block on the
   CTA button placed inside the rail. */
.art-toc ol a{display:block;font-size:14px;line-height:1.45;color:var(--ink-3);
  padding-left:var(--s-4);border-left:2px solid var(--line);
  transition:color var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease)}
.art-toc ol a:hover{color:var(--ink-2);border-left-color:var(--line-2)}
.art-toc ol a.is-current{color:var(--ink);border-left-color:var(--accent)}
.art-toc-cta .btn{display:inline-flex;padding-left:var(--s-4);padding-right:var(--s-4);
  border-left-width:1px;text-align:center}

/* ---------- centre the label, not the label plus icon ---------- */
/* A centred flex row centres the whole group, so a trailing icon pushes the
   label left by the icon plus the gap, measured at 22px. Compensating with a
   22px pad is off scale and lint rejects it, correctly. Taking the icon out of
   the centring flow needs no magic number. Now shared, not scoped. */
.cta-actions .btn{position:relative;padding-inline:var(--s-8)}
.cta-actions .btn > .ic{position:absolute;right:var(--s-6);top:50%;
  transform:translateY(-50%)}

/* ---------- the CTA panel ---------- */
/* The old accent was 2px wide, sized 2.2em and offset .1em against .cta-copy
   at 17px while the heading is 42px with a 47px line box, so it resolved
   against the wrong font entirely and floated 32px clear of the text. It now
   spans the copy block it marks, at every width. */
/* The accent bar is removed. It was a 2px rule floating 32px from the text and
   64px from the panel edge, anchored to nothing. Making it taller in the last
   pass made it more visible, not more intentional. The panel already carries a
   border, a radial wash, an idle sweep and a proximity ring; a fifth decorative
   element was decoration for its own sake. The copy now aligns to the panel
   padding like every other block on the site. */
.cta-band .cta-copy::before{display:none}
.cta-band .cta-copy{padding-left:0}
/* The figure was centred against a copy column up to 341px tall, leaving over
   100px of dead space above and below it. */
.cta-band{align-items:start}
.cta-band .cta-vis{align-self:stretch;display:grid;align-content:start}
.cta-band .cta-proof{max-width:none}
/* The decorative visual led the panel on a phone, pushing the heading below
   it. Rendered order now matches the DOM order screen readers already used. */
@media (max-width:920px){
  .cta-band .cta-vis{order:0;max-width:none;margin-top:var(--s-8)}
}

/* ---------- FAQ answer sat high in its panel ---------- */
/* Kept scoped to content FAQ sections. This pass covers CTA, button and card
   only, so the FAQ padding must not change on product pages that were not part
   of it. Removing the .-content scoping wholesale had swept this in too. */
[aria-labelledby="h-afaq"] .faq-a,
[aria-labelledby="h-bfaq"] .faq-a,
[aria-labelledby="h-gfaq"] .faq-a{padding-top:var(--s-3);padding-bottom:var(--s-6)}


/* ---------- G4: three optional glossary detail blocks ---------- */
/* Same treatment as .gl-matters so a term with all three reads as one block
   rather than four competing paragraphs. */
.gl-notice,.gl-mistake,.gl-note{margin-top:var(--s-4);font-size:14.5px;
  line-height:1.6;color:var(--ink-3)}
/* No own typography. The existing .gl-detail .lbl rule already defines the
   label treatment, so these three inherit it and cannot drift from it. */

/* ---------- form status: one treatment per state ---------- */
/* Before this pass there were two, -ok and -err, and the 501 not-configured
   state borrowed -ok. Its words said nothing had been sent while its colour
   said the opposite. Every state that has not sent now reads as not sent. */
.form-status.-warn,.form-status.-config{border-color:rgba(255,196,84,.34);
  background:rgba(255,196,84,.07);color:var(--ink-2)}
.form-status.-spam{border-color:rgba(255,196,84,.34);
  background:rgba(255,196,84,.07);color:var(--ink-2)}
.form-status.-fail,.form-status.-offline{border-color:rgba(255,90,90,.34);
  background:rgba(255,90,90,.07);color:var(--ink-2)}
.form-status.-config strong,.form-status.-warn strong{color:var(--ink)}

/* ============================================================================
   BUTTONS AT 480 AND BELOW
   Every CTA row on the site is already a wrapping flex row, so giving the
   button itself a full width forces one per line without changing any
   container's direction. The nav CTA is excluded because it is already hidden
   below 900, and .btn-sm inside a card keeps its own height.
   ============================================================================ */
@media (max-width:480px){
  /* flex-basis as well as width: inside a wrapping flex CTA row a width alone
     is still shrinkable, which left the app card buttons at 284px of 350. */
  .btn{width:100%;flex:1 0 100%}
  .nav .btn,.nav-cta{width:auto;flex:none}
  .app-act{gap:var(--s-3)}
}

/* ============================================================================
   SPACING AND CARD HEIGHT FIXES
   ============================================================================ */

/* .split is align-items:start, which is right when the two columns are a block
   of text beside a figure. When the children are cards it left them sized to
   their own content: 272px beside 250px on support. Cards in a split stretch. */
.split > .mod{align-self:stretch}

/* The article FAQ heading is a bare h2 rather than a .shead, so it carried no
   space above the first question. Measured 20px against the 64px this site
   gives every other section header. */
.art-wrap > .h2 + .faq{margin-top:var(--s-10)}
@media (max-width:900px){.art-wrap > .h2 + .faq{margin-top:var(--s-9)}}

/* ============================================================================
   MEASURED RESPONSIVE FIXES
   Both were present in the approved baseline and are fixed here because this is
   the pre deploy pass. Neither is a redesign; each is one rule.
   ============================================================================ */
/* Measured cause: .btn is white-space:nowrap with min-width:auto, so a label as
   long as "Install AI CMS Helper Text Generator" cannot shrink below its own
   single line width. Below 400px the label is allowed to wrap. Nothing above
   400px changes, so no approved layout moves. */
@media (max-width:400px){
  .btn{white-space:normal;overflow-wrap:anywhere;min-width:0;text-align:center;
       padding-inline:var(--s-4)}
  .app-top{flex-wrap:wrap;row-gap:var(--s-2)}
}

/* ---------------------------------------------------------------------------
   min-width:auto overflow

   A grid or flex child defaults to min-width:auto, which refuses to shrink
   below its own min-content. Three long standing overflows were all this one
   bug, not three separate problems:

     .appcard-body   272px inside a 216px content box, index and apps at 320
     .cta-band > h2  339px inside a 256px content box, the AI CMS page below 390
     .site-row .btn  308px starting at x=119 in a 350px row, the two app
                     callback pages at 320, 360 and 390

   The last one was mine: the full width button rule below 480 gives the button
   flex-basis 100%, which in a row that does not wrap pushes it past the edge.
   The row wraps now.
   --------------------------------------------------------------------------- */
@media (max-width:480px){
  .appcard > *,
  .cta-band > *,
  .cta-copy > *,
  .site-row > *{min-width:0}
  .site-row{flex-wrap:wrap;row-gap:var(--s-3)}
  .cta-band h2,.cta-band .h2,.phead h2,.phead .h2{overflow-wrap:anywhere}

  /* .fx is a fixed height mask with overflow:hidden and white-space:pre, which
     is what makes the label swap work. It also means a long label cannot wrap,
     it just gets cut: "Install AI CMS Helper Te". Below 400 the mask is stood
     down and the label becomes plain wrapping text. The swap is a hover effect
     and there is no hover here. */
  .btn .fx{overflow:visible;white-space:normal;display:inline}
  .btn .fx-a{height:auto;line-height:1.3;white-space:normal;display:inline}
  .btn .fx-b{display:none}
}

/* ============================================================================
   6. FLASH OF UNSTYLED CONTENT

   [data-enter] elements were painted by the browser, then hidden by hero() and
   animated back in. That is the flash: text appears, vanishes, reappears.
   Hiding them in CSS means they are never painted visible in the first place.
   Guarded by .js so no-JS visitors still see everything, and the reduced motion
   override below wins for anyone who asks for it.
   ============================================================================ */
.js [data-enter]{opacity:0}
@media (prefers-reduced-motion:reduce){.js [data-enter]{opacity:1 !important}}

/* ============================================================================
   4. CARD INTERIORS BELOW 480

   Overriding the two tokens rather than each card, so every surface that uses
   them moves together and nothing drifts out of the spacing scale.
   ============================================================================ */


/* ============================================================================
   3. TOUCH SWIPE

   The slider listened for pointerdown then pointerup. On a touch screen a
   horizontal swipe is claimed by the browser as a scroll gesture, which fires
   pointercancel instead of pointerup, so the swipe never registered.
   touch-action:pan-y gives horizontal movement to the slider and leaves
   vertical scrolling to the page.
   ============================================================================ */
.tsl-viewport{touch-action:pan-y}

/* ============================================================================
   2. HERO BEATS AS A SLIDER BELOW 900

   Eight stacked paragraphs made the mobile hero enormous. One card at a time,
   swipeable, using native scroll snap so there is no JS and no dependency.
   ============================================================================ */
@media (max-width:900px){
  .home-hero.is-motion .home-hero-beats,
  .home-hero .home-hero-beats{
    /* No negative margin. Pulling the track outside the wrap widened the
       document by 600px at 320, because a scroll container still contributes
       its own overflow to the page. It stays inside the gutter. */
    display:flex;overflow-x:auto;overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;
    gap:var(--s-4);margin:0;padding:0 0 var(--s-2);
    max-width:100%;min-width:0;scrollbar-width:none}
  .home-hero .home-hero-beats::-webkit-scrollbar{display:none}
  .home-hero.is-motion .home-hero-beat,
  .home-hero .home-hero-beat{
    flex:0 0 88%;min-width:0;scroll-snap-align:center;
    border:1px solid var(--line);border-radius:var(--r-md);
    padding:var(--s-5);background:var(--bg-1);text-align:left;
    position:static;inset:auto;opacity:1}
  .home-hero .home-hero-beat-h{font-size:15px}
  .home-hero .home-hero-beat-b{font-size:13.5px;margin-top:var(--s-2)}
}

/* ============================================================================
   1. DEAD SPACE AFTER THE PINNED HERO
   ============================================================================ */
.home-hero.is-motion .home-hero-stage{padding-bottom:var(--s-3)}
.home-hero{padding-bottom:0}

/* ============================================================================
   MOBILE HERO STAGE WIDTH

   .home-hero-stage-in is a grid, so its children default to min-width:auto and
   cannot shrink below their own min-content. The inline SVG's intrinsic width
   pushed the track to 1040px inside a 390px viewport, and the fig and narrate
   max-widths of 1000px held it there. Cap them to the wrap below 900.

   An earlier sweep reported this page clean because it sampled 300ms after
   domcontentloaded, before .is-motion was applied. It was measuring the static
   layout, not the one a visitor sees.
   ============================================================================ */
@media (max-width:900px){
  .home-hero.is-motion .home-hero-stage-in,
  .home-hero.is-motion .home-hero-stage-in > *{min-width:0}
  .home-hero.is-motion .home-hero-fig,
  .home-hero.is-motion .home-hero-narrate{max-width:100%;width:100%}
  .home-hero.is-motion .home-system-canvas{max-width:100%;height:auto}
}

/* Below 900 the beats are cards in their own right, so the caption panel's
   border, radius and background would nest a card inside a card. Drop the
   chrome and give the track the full gutter width. */
@media (max-width:900px){
  .home-hero.is-motion .home-hero-narrate{border:0;background:none;padding:0;
    border-radius:0;margin-top:var(--s-6)}
}

/* ============================================================================
   RESPONSIVE CARD INTERIORS

   One rule for the whole site. As the screen narrows, interior padding is the
   first thing that should give: a 32px inset inside a 288px card leaves 224px
   of usable width, which is why long words wrapped badly and buttons ran out of
   room at 320.

     above 992    40 / 32 / 24   unchanged
     480 to 992   32 / 24 / 24
     480 and below      16 / 16 / 16

   Set on the three semantic roles rather than on each card, so anything using
   --pad-lg, --pad or --pad-sm moves with it. Every card interior on the site
   now routes through one of the three, including the ones that used to hardcode
   a scale step: .mod, .quote, .person, .tcard, .figbox, .tsl-card,
   .tsl-viewport, .scrolly-steps, .eco-steps, .bot-point, .work-meta,
   .art-figure, .faq summary, .faq-a, .cred-cell, .metrics, .counts.

   .btn keeps its own padding: a button is not a card, and its interior is
   already handled by the sub-400 rule that lets long labels wrap.
   ============================================================================ */
@media (max-width:992px){
  :root{--pad-lg:24px; --pad:24px; --pad-sm:24px}
}
@media (max-width:480px){
  :root{--pad-lg:16px; --pad:16px; --pad-sm:16px}
}

/* The two outliers. .appcard sat at 48px and .cta-band at a clamp topping out
   at 64px, so neither followed the rule below 992. They are cards, so they do
   now. Above 992 both keep the value they were designed with. --pad is already
   24px between 480 and 992 and 16px below it, so one rule covers both bands. */
@media (max-width:992px){
  .appcard{padding:var(--pad)}
  .cta-band{padding:var(--pad)}
  .cta-band .cta-copy{padding-left:0}
}

/* The scramble layer is never initialised inside a form, so it would sit on top
   of the real label as an uninitialised duplicate. Hide it there. */
form .fx-b{display:none}
