/* ============================================================
   askotter v6.0 — editorial hairline-band system
   Inter Tight (display/body) + DM Mono (eyebrows/data/terminal)

   Structure: the page is a vertical stack of full-bleed bands,
   each separated by a 5%-ink hairline. No heavy fills, no hard
   borders. Restraint is the whole point: one saturated accent,
   used only on links, live dots and small pills. Solid fills are
   ink, never accent. Display type is REGULAR weight at large
   sizes with negative tracking — the size does the work, not the
   weight. Content bands are left-aligned; only the closing CTA
   centers.
   ============================================================ */
*{box-sizing:border-box;margin:0;padding:0}
/* Backstop for decorative transforms. `clip` rather than `hidden` on purpose:
   hidden makes the root a scroll container and breaks position:sticky, which
   the top bar and nav both depend on. clip creates no scrollport, so sticky
   survives while stray horizontal bleed can no longer widen the document. */
html{overflow-x:clip}
body{font-family:'Archivo',system-ui,-apple-system,'Helvetica Neue',sans-serif;background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased;transition:background .35s ease, color .35s ease;overflow-x:clip}
a{color:inherit;text-decoration:none}
img{max-width:100%;height:auto;display:block}

/* ==================== FOCUS ====================
   An audit found roughly half of all links and buttons had no visible focus
   state at all — keyboard users could not tell where they were. One baseline
   ring for everything focusable. :focus-visible, so it appears for keyboard and
   never on a mouse click. Coral is 3.66:1 against paper, clearing the 3:1 that
   WCAG 1.4.11 asks of non-text indicators. */
:where(a,button,input,select,textarea,summary,[tabindex]):focus-visible{
  outline:2px solid var(--brand);outline-offset:3px;border-radius:0}
/* These three set outline:none on the element or its :focus state, which would
   otherwise beat the :where() baseline above. */
.fi:focus-visible,.chat-input input:focus-visible,.inline-capture input:focus-visible{
  outline:2px solid var(--brand);outline-offset:2px}

/* Skip link: visually out of the way until it takes focus. */
.skip-link{position:absolute;left:12px;top:-64px;z-index:2000;
  background:var(--ink);color:var(--bg);padding:10px 16px;border-radius:0;
  font-family:var(--f-sans);font-size:.875rem;transition:top .18s ease}
.skip-link:focus{top:12px}
/* main takes focus programmatically from the skip link; never ring it. */
main:focus{outline:none}

/* ==================== v8 — WARM PAPER, ONE SURFACE ====================
   2026-08-09. v8 returns the site to a light surface and applies it to the
   WHOLE site, consoles included. v6 was warm paper with dark product islands;
   v7 (2026-08-07) inverted everything to dark; v8 is warm paper again with NO
   dark islands left. All three keep the same structure — bands not fills,
   regular-weight display type, left-aligned content, the eyebrow dot,
   asymmetric hover — only the surface has ever moved.

   Why one surface rather than the marketing-light / console-dark split that
   was proposed: a split is a two-theme system wearing a different hat. It has
   the exact failure mode v7 deleted `[data-theme]` to escape — every component
   gets checked twice and the less-trafficked half drifts. One surface means a
   card is a card wherever it lands.

   --ink is still semantic, not literal. It means "the high-contrast fill",
   so every rule that says `background:var(--ink);color:var(--bg)` reads
   correctly through all three systems without being touched. That is why the
   surface can invert at the token layer instead of at 2,000 call sites.

   CONTRAST IS MEASURED, NOT EYEBALLED. Every value below was checked against
   the WORST surface it can land on (--bg-alt #F2F0EA, the recessed band — a
   caption inside an alt band is the case that actually fails, not the same
   caption on the page). Re-run scratchpad/contrast.js before shifting any of
   these. Two values from the source brief were rejected here because they do
   not survive that check, noted inline. */
/* ==================== v10 "MODERNIST" — FLAT, ARCHITECTURAL, ONE RED ========
   2026-08-16. v10 replaces v9 "Lane" at the token layer. The structure of the
   site does not change: it is still a vertical stack of full-bleed bands with
   left-aligned content. What changes is what separates them and what fills
   them.

   The one-sentence version: STRUCTURE IS THE DECORATION. Visible grid cells,
   2px rules, flush-left everything, zero radius, no shadows. Borders do the
   work cards used to ask shadows to do.

   ── WHAT v10 DELETES, AND WHY IT IS NOT A REGRESSION ──────────────────────
   v9's colour law (coral = proposed · green = verified · ink = fact) is gone.
   That law existed to make TWO chromatic colours each carry information. v10
   has ONE, so the law has nothing left to arbitrate. Green is deleted outright
   rather than kept as an alias — every green mark in the old system meant
   "measured reality", and measured reality is now ink, which is the same claim
   made without a second hue. The tokens survive as red so no template breaks;
   see --accent below.

   v9's WIDTH-AXIS accent device is also gone. `.hl` went width+weight in v9
   precisely so that colour would be free to mean one thing; with one colour
   in the system that trade no longer pays, and the approved design puts the
   accent phrase back in red. Consequence for the font request in
   includes/header.php: the wdth axis is NO LONGER loaded, and --wd-* are all
   100. Do not re-add font-stretch to a headline — Archivo at 800 is the
   emphasis now.

   ── WHAT IS CARRIED FORWARD, UNCHANGED ────────────────────────────────────
   CONTRAST IS MEASURED, NOT EYEBALLED. Same law as v8/v9, and it is the reason
   three values below differ from the approved palette. Each deviation is
   marked DEVIATION with its measurement. Re-run scratchpad/c.js before moving
   any of them. The ramps themselves are ported VERBATIM from the design source
   — only the assignment of a ramp rung to a TEXT role was adjusted, and only
   where the source rung failed WCAG AA at the size it is actually used.

   --ink is still semantic, not literal: it means "the high-contrast fill", so
   every `background:var(--ink);color:var(--bg)` rule in the 3,500 lines below
   reads correctly through v6/v7/v8/v9/v10 without being touched. That is why
   the surface can change at the token layer instead of at 2,000 call sites.
   ========================================================================= */
:root{
  /* ---- GROUND ---------------------------------------------------------------
     Two surfaces, alternating band by band. Not three: v8/v9 had a third
     "raised" surface (white) for cards, and a flat system has no raised
     anything — a card is a cell in a framed grid, marked by its border. So
     --bg-card and --bg-raised both resolve to the tint, which is what keeps
     264 existing .card elements from floating off a system that no longer has
     elevation. */
  --bg:#f3f2f2;            /* ground — .section, nav, footer                  */
  --bg-alt:#f8f4f4;        /* tint — .section-alt, highlighted cells          */
  --bg-card:#f8f4f4;
  --bg-raised:#f8f4f4;
  --bg-page:#f3f2f2;--bg-subtle:#f8f4f4;--bg-elevated:#f8f4f4;
  --ink:#201e1d;

  /* ---- TEXT ROLES -----------------------------------------------------------
     Measured against the TINT (#f8f4f4) and the GROUND (#f3f2f2); the figures
     below are the ground, which is the worse of the two by ~2%.
       --text        14.86   --text-muted    5.00
       --text-body    5.83   --text-faint    4.64
     DEVIATION (1 of 3): the source assigns neutral-600 #7d7979 to muted body
     copy and neutral-500 #9b9797 to fine print. They measure 3.85 and 2.59 on
     this ground. #9b9797 carries 11px source lines and disclaimers — exactly
     the size AA exists for, and exactly the bug the v6 light system shipped
     and v8 fixed. Re-shipping it knowingly would be a regression, so the two
     TEXT roles move one rung darker (#6b6767 / #706c6c, both AA) while the
     literal ramp below keeps #7d7979 and #9b9797 verbatim for the NON-TEXT
     jobs they are safe in: cell borders, dividers, rules, dot fills. */
  --text:#201e1d;--text-body:#605d5d;--text-muted:#676363;--text-faint:#696565;
  --text-head:#201e1d;

  /* ---- RULES AND BORDERS ----------------------------------------------------
     The system has exactly two weights of line and they are not interchangeable:
       --rule      2px, ink at 40%  — separates SECTIONS and frames a grid.
       --hairline  1px, #d7d3d3     — separates CELLS inside a frame.
     Getting these the wrong way round is the single most legible way to break
     this design: a 1px section rule reads as a mistake, a 2px cell border reads
     as a table from 1997. --border is an alias of --hairline because the card
     ring and the cell border are now the same object. */
  --rule:color-mix(in srgb,#201e1d 40%,transparent);
  --rule-w:2px;
  --hairline:#d7d3d3;
  --border:#d7d3d3;--border-hover:#bab6b6;--border-focus:#201e1d;

  /* ---- THE ONE RED ----------------------------------------------------------
     Used SPARINGLY and only for: step numerals, kicker labels, checkmarks,
     corner flags ("Most popular", "The gate"), the blinking cursor, the
     wordmark period, the accent phrase in a display headline, and ONE full-red
     poster band per page (the closing CTA). Never a background tint behind
     body copy, never a second red.

     Three rungs, measured on the ground:
       --brand       #1d4ed8   3.76  non-text and >=24px display type only
       --brand-text  #1e40af   6.41  every red word inside a sentence, every
                                     red link, every 11-14px red label
       --brand-deep  #1e3a8a   9.59  red on the tint where it must sit beside
                                     ink body copy at small size
     DEVIATION (2 of 3): --btn-fill is accent-600 #1a45c0, not the raw accent.
     White on #1d4ed8 is 4.20 and a 14px/800 button label is not "large text"
     under WCAG (that needs 18.66px bold), so the raw accent fails AA on the
     single most-clicked element on the site. #1a45c0 measures 4.74 with white
     and is one ramp step away — indistinguishable in place, and the ramp
     relationship still holds because the source already designates accent-600
     as the primary-button hover. Hover therefore moves to accent-700 (7.17).
     The POSTER band keeps the raw #1d4ed8: its type is 40-72px display, which
     clears the 3:1 that size is held to. */
  --brand:#1d4ed8;--brand-text:#1e40af;--brand-vivid:#1d4ed8;
  --brand-deep:#1e3a8a;
  --btn-fill:#1a45c0;--btn-fill-hover:#1e40af;
  --brand-soft:#eff4ff;--brand-tint:#eff4ff;
  --brand-border:#bfd3fe;
  --brand-glow:transparent;

  /* Green is deleted. These four names survive ONLY so the ~40 templates that
     reference them keep resolving; every one of them now renders red or ink.
     Do not reintroduce a second hue through this door — if something needs to
     read as "measured", it is ink, and if it needs to read as "do this", it is
     --brand-text. */
  --accent:#1e40af;--accent-hover:#1e3a8a;--accent-bright:#1d4ed8;
  --accent-soft:#eff4ff;--accent-border:#bfd3fe;--accent-glow:transparent;
  --ok:#1e40af;--signal-up:#1e40af;
  /* Status rungs. Every use of these three in this codebase is a STATUS CHIP
     or a delta figure at 8-12px — grep them: .pm-badge, .ps-verdict, .ci-http,
     .pp-tile b. They are never display type. So they take the TEXT rungs, not
     the graphic ones: --danger at #1d4ed8 measured 3.76 and failed AA on every
     chip that used it, which is what an audit of six pages turned up after the
     colour remap. If a large graphic ever needs a danger fill, use --brand
     directly rather than lightening this back. */
  --success:#0e6b38;--warning:#8f4408;--danger:#b91c1c;

  /* ---- RAMPS — PORTED VERBATIM FROM THE DESIGN SOURCE ------------------------
     Both names are defined for every rung: the short --n- / --acc- form used
     by this stylesheet, and the long --color- form used by the approved
     design file, so markup can be lifted out of the reference and pasted into
     a template without a rename pass. */
  --n-100:#f8f4f4;--n-200:#eae7e7;--n-300:#d7d3d3;--n-400:#bab6b6;--n-500:#9b9797;
  --n-600:#7d7979;--n-700:#605d5d;--n-800:#444141;--n-900:#2d2b2b;
  --acc-100:#eff4ff;--acc-200:#dbe6fe;--acc-300:#bfd3fe;--acc-400:#8db4fc;
  --acc-500:#5b8cff;--acc-600:#1a45c0;--acc-700:#1e40af;--acc-800:#1e3a8a;--acc-900:#14245c;

  --color-bg:#f3f2f2;--color-surface:#f8f4f4;--color-text:#201e1d;
  /* --color-accent-2 (#e15b47) deleted in the v10.1 sweep. A one-hue system
     cannot have a second accent token sitting in :root waiting to be used; it
     had zero consumers and every one it might have gained would have been a
     bug. If you need a second emphasis, it is weight or fill, not hue. */
  --color-accent:#1d4ed8;
  --color-divider:color-mix(in srgb,#201e1d 40%,transparent);
  --color-neutral-100:#f8f4f4;--color-neutral-200:#eae7e7;--color-neutral-300:#d7d3d3;
  --color-neutral-400:#bab6b6;--color-neutral-500:#9b9797;--color-neutral-600:#7d7979;
  --color-neutral-700:#605d5d;--color-neutral-800:#444141;--color-neutral-900:#2d2b2b;
  --color-accent-100:#eff4ff;--color-accent-200:#dbe6fe;--color-accent-300:#bfd3fe;
  --color-accent-400:#8db4fc;--color-accent-500:#5b8cff;--color-accent-600:#1a45c0;
  --color-accent-700:#1e40af;--color-accent-800:#1e3a8a;--color-accent-900:#14245c;
  --font-heading:'Archivo',system-ui,-apple-system,'Helvetica Neue',sans-serif;
  --font-body:'Archivo',system-ui,-apple-system,'Helvetica Neue',sans-serif;

  /* Categorical data colours. A one-hue system cannot also run a five-hue
     chart palette, so these become a VALUE ramp in ink plus the one red at the
     top — series are told apart by lightness, which is how a flat, printed
     system has always done it and which survives greyscale printing. */
  --c1:#201e1d;--c1-soft:#eae7e7;--c1-border:#d7d3d3;
  --c2:#605d5d;--c2-soft:#f8f4f4;--c2-border:#d7d3d3;
  --c3:#9b9797;--c3-soft:#f8f4f4;--c3-border:#d7d3d3;
  --c4:#1e40af;--c4-soft:#eff4ff;--c4-border:#bfd3fe;
  --c5:#bab6b6;--c5-soft:#f8f4f4;--c5-border:#d7d3d3;

  /* ---- ELEVATION IS GONE ----------------------------------------------------
     "No box shadows on cards — borders do the work." The tokens resolve to
     `none` rather than being deleted so the ~95 rules and 89 inline styles
     that reference them keep parsing. The two RING tokens keep their inset
     border, because that inset IS the card's 1px cell border in the new
     system — zeroing those would strip the border off every .card on the site
     rather than flattening it. */
  --shadow-sm:none;--shadow-md:none;--shadow-lg:none;
  --shadow-btn:none;--shadow-btn-hover:none;
  --glow-brand:none;--glow-accent:none;
  --shadow-ring:inset 0 0 0 1px var(--border);
  --shadow-card:inset 0 0 0 1px var(--border);
  --shadow-key:inset 0 0 0 1px var(--border);

  /* ---- TYPE — ONE FAMILY ----------------------------------------------------
     Archivo at 400 / 600 / 800, and nothing else. Inter Tight and DM Mono are
     both dropped from the font request, which is a net LOSS of two variable
     families and one width axis from the critical path.

     The four family tokens stay distinct even though all four now resolve to
     Archivo. They encode the JOB each face was doing, and collapsing them to a
     single --font would throw away the only record of which text is a claim,
     which is explanation, and which is machine output — information the next
     system will need if it ever wants two families again.

     --f-mono is the exception that is genuinely different: code, diffs and log
     surfaces need real tabular monospace, and the source specifies the SYSTEM
     mono stack rather than a webfont. Nothing to download. */
  --f-display:'Archivo',system-ui,-apple-system,'Helvetica Neue',sans-serif;
  --f-sans:'Archivo',system-ui,-apple-system,'Helvetica Neue',sans-serif;
  --f-label:'Archivo',system-ui,-apple-system,'Helvetica Neue',sans-serif;
  --f-accent:'Archivo',system-ui,-apple-system,'Helvetica Neue',sans-serif;
  --f-mono:ui-monospace,Menlo,Monaco,'Cascadia Mono','Roboto Mono',monospace;
  --font-mono:ui-monospace,Menlo,Monaco,'Cascadia Mono','Roboto Mono',monospace;

  /* Weight replaces width as the emphasis axis. 800 is the display weight and
     700 the accent; the width axis is NOT loaded, so these three stay at 100
     and font-stretch resolves to normal everywhere it is still declared. */
  --wd-display:100;--wd-accent:100;--wd-ui:100;
  --wt-display:800;--wt-accent:800;

  /* ---- TYPE SCALE -----------------------------------------------------------
     Straight from the source: hero clamp(44,5.2vw,72), section h2
     clamp(30,3.4vw,46). Everything below h2 steps down from there. The v9 rule
     still applies and is the one to measure against — if a display headline
     GAINS a line after a type change, the scale is wrong, not the copy.
     Archivo 800 at normal width sets narrower than v9's 116%-width 500, so
     these numbers can go back up without the wrap regressing. */
  --fs-display:clamp(2.75rem,5.2vw,4.5rem);        /* 44 -> 72  hero h1      */
  --fs-h1:clamp(2.25rem,4.2vw,3.5rem);             /* 36 -> 56  page h1      */
  --fs-h2:clamp(1.875rem,3.4vw,2.875rem);          /* 30 -> 46  band heading */
  --fs-h3:clamp(1.1875rem,1.4vw,1.375rem);         /* 19 -> 22  card heading */
  --fs-lead:clamp(1rem,1.1vw,1.0625rem);           /* 16 -> 17  intro para   */
  --fs-body:0.9375rem;                             /* 15        body copy    */
  --fs-mid:0.875rem;--fs-small:0.8125rem;--fs-lg:1.0625rem;
  --fs-mono:0.8125rem;--fs-mono-sm:0.78125rem;--fs-mono-xs:0.75rem;

  /* Tracking: -0.02 to -0.03em on display, per the source. Looser than v9's
     -.036em because that value was compensating for a WIDENED face adding
     sidebearing to every glyph; at normal width it over-tightens and Archivo's
     counters start to close. Caps labels take +.06 to .08em. */
  --tr-display:-.03em;--tr-h2:-.02em;--tr-h3:-.015em;
  --tr-ui:-.005em;--tr-body:0;
  --tr-caps:.07em;

  /* Display leading ~1.0 — the source sets .98 on the hero. This is tight on
     purpose: a flush-left display headline in a heavy grotesque is a block of
     type, and the block wants to be solid. */
  --lh-display:.98;--lh-head:1.05;--lh-body:1.65;--lh-ui:1.5;

  /* ---- CORNERS: ZERO, EVERYWHERE --------------------------------------------
     v9 kept buttons on --r-pill deliberately so a pill would read as "this one
     is pressable" next to a 4px panel. That reasoning does not survive here:
     in a system where NOTHING is round, a square filled red rectangle is
     unambiguous, and a single pill would be the one soft object on the page.
     All eight radius tokens are 0 and there is no exception. */
  --radius:0;--radius-sm:0;--radius-md:0;--radius-lg:0;--radius-xl:0;
  --r-md:0;--r-lg:0;--r-pill:0;

  /* ---- MOTION ---------------------------------------------------------------
     One easing curve for the whole site, per the source: cubic-bezier(.2,.7,.3,1).
     --hov-ease and --ease-out are aliases of it so the two cannot drift, which
     is the same guard v8 put on them. The asymmetric hover timing from v8
     survives untouched — it solved footer links strobing like a chase light
     when a cursor swept them, and nothing about a flat surface un-solves that. */
  --ease-out:cubic-bezier(.2,.7,.3,1);
  --hov-ease:cubic-bezier(.2,.7,.3,1);
  --hov-in:170ms;--hov-in-delay:65ms;--hov-out:400ms;
  --dur-1:.2s;--dur-2:.35s;--dur-3:.5s;--dur-4:.7s;

  /* ---- LAYOUT ---------------------------------------------------------------
     1280px content width and 48px side padding, from the source. --band-y is
     the 96px section padding. */
  --content-width:1280px;--container-width:1280px;
  --band-y:6rem;
  --nav-bg:#f3f2f2;

  /* The graph-paper background texture is off. v8.1 spent two rounds dialling
     it to the threshold of visible; a system whose whole proposition is that
     STRUCTURE is the decoration should not also have a decorative background,
     and the 2px band rules are the grid now. Kept as a transparent token
     rather than deleted so .grid-bg still parses. */
  --grid-line:transparent;
  --btn-gradient:#1a45c0;
  --btn-gradient-hover:#1e40af;
}


.container{max-width:var(--content-width);margin:0 auto;padding:0 2rem}
@media(max-width:768px){.container{padding:0 1.25rem}}
.grid-bg{position:fixed;inset:0;z-index:-1;background-image:linear-gradient(var(--grid-line) 1px,transparent 1px),linear-gradient(90deg,var(--grid-line) 1px,transparent 1px);background-size:60px 60px;pointer-events:none}

/* ==================== TOP BAR ==================== */
.top-bar{background:var(--bg);border-bottom:1px solid var(--hairline);position:sticky;top:0;z-index:1001;transition:background .35s ease,border-color .35s ease}
.top-bar-inner{max-width:var(--content-width);margin:0 auto;padding:0 2rem;height:34px;display:flex;align-items:center;justify-content:space-between}
/* The v6/v8 marquee and utility rules that stood here (.top-bar-values,
   .top-bar-item, .top-bar-sep, .top-bar-util, .top-bar-login/.top-bar-start
   and their three breakpoints) are deleted. The header stopped emitting every
   one of those classes in v10.1 — verified against all 107 templates — so they
   were ~58 lines styling nothing. The strip that replaced them is in the
   enforcement layer, section 9, under THE MARKET STRIP. */

/* ==================== NAV ==================== */
/* v10.1: was a bare `nav{}`. The site emits FOUR <nav> elements — .main-nav,
   .gp-breadcrumbs, .pm-nav inside the portal mock, and .tb-markets in the top
   bar — and this rule was pinning every one of them sticky at top:34px with a
   backdrop blur. It was written when .main-nav was the only <nav> on the page.
   The market strip is what finally made it visible: its links rendered 34px
   below the strip, on top of the logo. Scoped to the element it describes. */
.main-nav{position:sticky;top:34px;z-index:1000;background:var(--nav-bg);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-bottom:1px solid var(--hairline);transition:background .35s ease,border-color .35s ease}
@media(max-width:768px){nav{top:32px}}
.nav-inner{max-width:var(--content-width);margin:0 auto;padding:0 2rem;height:76px;display:flex;align-items:center;justify-content:space-between;gap:1.75rem}
/* The wordmark is an inline SVG logotype (includes/logo.php), not live text:
   it is split at ask|otter with the second half in brand coral, which no
   single run of text can express. The ink half inherits --ink through
   currentColor and the coral half reads --brand, so there is no per-theme
   asset swap. Height is set here; width follows the 4.63:1 aspect. */
/* v9: the lockup is mark + word. gap is optical, not mathematical — the mark's
   legs splay outward at the baseline, so an even gap reads too wide. */
.nav-logo{display:flex;align-items:center;gap:.44rem;color:var(--ink)}
.nav-logo .wm{height:19px;width:auto;display:block;overflow:visible}
.nav-logo .wm-lane{flex:none;display:block}
@media(max-width:1024px){.nav-logo .wm{height:17px}.nav-logo .wm-lane{width:23px;height:23px}}
/* One-colour contexts (embroidery, a partner's mono deck, a fax) drop the dash
   to currentColor as well. The wordmark itself is already single-colour in v9,
   so this rule now only has the mark to neutralise. */
.wm--mono .wm-dash,.wm--mono .wm-otter{fill:currentColor}

/* Entrance: the whole word wipes in left-to-right, so `ask` arrives before
   `otter` for free; then the coral lands on the second half as its own beat.
   The mark assembles, then it gets its colour. Runs once per page load and
   only above the collapse width, where the nav has room to be noticed. */
/* animation-fill-mode is `backwards`, never `forwards`, and the hidden state
   lives ONLY inside the keyframes. The resting state of both elements is fully
   visible, so if the animation never runs — reduced motion, an old engine, a
   paint the compositor skips — the logo is simply there. Parking the hidden
   state on the element itself is how you ship an invisible logo. */
/* v9: the second beat moved. It used to be coral landing on `otter`; the word
   is one colour now, so the beat is the lane dash arriving in the mark — the
   marking being painted onto the road. Same choreography, and it is now the
   single coral event in the entire identity. */
.nav-logo .wm-type{animation:wm-wipe .62s cubic-bezier(.16,1,.3,1) .08s backwards}
.nav-logo .wm-dash{animation:wm-dash .42s cubic-bezier(.16,1,.3,1) .5s backwards;
  transform-box:fill-box;transform-origin:center}
@keyframes wm-wipe{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
@keyframes wm-dash{from{opacity:0;transform:scaleX(.2)}to{opacity:1;transform:none}}
.nav-links{display:flex;align-items:center;gap:.4375rem}
/* Plain text, no pill. Colour is the only hover affordance.
   Links sit on --text-body, not --text-faint: faint measured 4.6:1 on paper,
   which is a pass-by-a-hair for 14px nav labels. --text-body is ~7.3:1. */
/* Hover draws a coral rule under the label rather than swapping its colour.
   It is the same geometry as the .active underline below, so hovering reads as
   a preview of the selected state instead of an unrelated flash — and a moving
   1.5px line is far quieter than eight labels changing colour at once.
   Painted with a background gradient, not a pseudo-element: ::after already
   carries the dropdown chevron and ::before the active rule. */
/* :not(.nav-cta) is load-bearing. The CTA is an <a> inside .nav-links, so this
   rule ([0,1,1]) used to outrank .nav-cta ([0,1,0]) and win every property the
   two share — colour, padding, weight, radius. "Try it free" rendered as
   --text-body (#bab6b6) on the cream ink fill: ~1.8:1, which reads as a
   disabled button. It also inherited the coral underline gradient below. Same
   cascade collision flattened the button in the mobile drawer, where muted text
   in a full-width bordered box reads as an empty input. The nav-link treatment
   simply should not apply to the button, so exclude it rather than out-specify. */
.nav-links>a:not(.nav-cta),.nav-dd>a:not(.nav-cta){font-family:var(--f-sans);font-size:.9375rem;font-weight:400;color:var(--text-body);
  padding:10px 15px;border-radius:0;letter-spacing:var(--tr-ui);white-space:nowrap;
  background-image:linear-gradient(var(--brand),var(--brand));background-repeat:no-repeat;
  background-position:15px calc(100% - 5.5px);background-size:0 1.5px;
  transition:color var(--hov-out) var(--hov-ease),background-size var(--hov-out) var(--hov-ease)}
.nav-links>a:not(.nav-cta):hover,.nav-dd>a:not(.nav-cta):hover,.nav-links>a.active,.nav-dd>a.active{color:var(--text)}
.nav-links>a:not(.nav-cta):hover,.nav-dd>a:not(.nav-cta):hover{
  background-size:calc(100% - 30px) 1.5px;
  transition:color var(--hov-in) var(--hov-ease) var(--hov-in-delay),
             background-size var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
/* The active item already owns an ink rule; don't stack a coral one under it. */
.nav-links>a.active,.nav-dd>a.active{background-size:0 1.5px}
@media(min-width:1025px){
  .nav-dd>a::after{content:'';display:inline-block;width:4px;height:4px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(45deg);margin-left:4px;margin-bottom:2px;opacity:.75;transition:transform .2s,opacity .2s}
  .nav-dd:hover>a::after{transform:rotate(-135deg);opacity:1}
  .nav-links>a.active,.nav-dd>a.active{position:relative}
  .nav-links>a.active::before{content:'';position:absolute;bottom:4px;left:15px;right:15px;height:1.5px;background:var(--ink);border-radius:0}
}

/* Theme toggle - sun/moon visibility */
.theme-toggle{background:none;border:1px solid var(--border);border-radius:0;width:38px;height:38px;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-muted);transition:all .3s cubic-bezier(.22,1,.36,1);flex-shrink:0;line-height:1;position:relative;overflow:hidden}
.theme-toggle:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 3px var(--accent-glow)}
.theme-toggle svg{width:18px;height:18px;position:absolute;transition:all .4s cubic-bezier(.22,1,.36,1)}
.icon-sun{opacity:0;transform:rotate(90deg) scale(0.5)}
.icon-moon{opacity:1;transform:rotate(0) scale(1)}
.mega-menu{box-shadow:var(--shadow-lg);border-color:var(--border)}

.nav-toggle{display:none;align-items:center;justify-content:center;min-width:44px;min-height:44px;background:none;border:1px solid var(--border);border-radius:0;padding:6px 10px;cursor:pointer;color:var(--text);transition:border-color .2s}
.nav-toggle:hover{border-color:var(--border-hover)}
.nav-sep{display:none}
/* Secondary nav action: plain text, sits left of the ink primary. */
.nav-trial{font-family:var(--f-sans);font-size:.9375rem;font-weight:400;color:var(--text-body);padding:10px 15px;margin-left:.5rem;letter-spacing:var(--tr-ui);white-space:nowrap;transition:color var(--hov-out) var(--hov-ease)}
.nav-trial:hover{color:var(--text);transition:color var(--hov-in) var(--hov-ease) var(--hov-in-delay)}

/* ==================== SECTIONS (hairline bands) ====================
   Every band is separated by a single hairline rather than an
   alternating gray fill. .section-alt keeps its faint surface tint but
   drops the hard 1px borders it used to carry, so consecutive bands
   read as one continuous column. Many pages set an inline
   `border-top`/`border-bottom` with the old --border value; that token
   is now a 9%-ink hairline, so those inline rules degrade gracefully
   into the same visual language instead of needing 38 file edits. */
.section{padding:var(--band-y) 0;border-bottom:1px solid var(--hairline)}
.section-alt{padding:var(--band-y) 0;background:var(--bg-alt);border-bottom:1px solid var(--hairline);border-top:none;transition:background .35s ease}
.section:last-of-type,.section-alt:last-of-type{border-bottom:none}
.page-hero{padding:4.5rem 0 3.5rem;border-bottom:1px solid var(--hairline);text-align:left}
.page-hero .eyebrow,.page-hero .hero-sub,.page-hero .desc,.page-hero .ssub{text-align:left;margin-left:0;margin-right:0}
.hero-sub{font-size:var(--fs-body);color:var(--text-body);line-height:var(--lh-body);max-width:62ch}

/* Display type: the size and the negative tracking do the work.
   Regular/medium weight only — bold display is the single biggest
   giveaway of the old system. */
h1,h2,h3,h4{font-optical-sizing:auto;text-wrap:balance}
/* h1/h2 are the brand voice and take Archivo. h3 stays Inter Tight: at
   ~19px a card heading is closer to UI than to headline, and setting it in the
   serif made every card look like a pull quote. The rule is legible — serif
   tells the story, sans runs the product, mono shows the receipts. */
h1{font-family:var(--f-display);font-size:var(--fs-h1);font-weight:var(--wt-display);line-height:var(--lh-head);color:var(--text);letter-spacing:var(--tr-display);margin-bottom:1rem}
h2{font-family:var(--f-display);font-size:var(--fs-h2);font-weight:var(--wt-display);line-height:var(--lh-head);margin-bottom:.875rem;color:var(--text);letter-spacing:var(--tr-h2);text-align:left}
h3{font-family:var(--f-sans);font-size:var(--fs-h3);font-weight:600;line-height:1.43;margin-bottom:.4rem;color:var(--ink);letter-spacing:var(--tr-h3)}

/* The one editorial gesture: an accented word set in serif italic inside an
   otherwise neutral grotesque headline. Never use it for a whole heading.

   Was Instrument Serif italic 400 at .94em. Instrument is a high-contrast
   DISPLAY serif: its thin strokes read as skinny and washed-out beside Inter
   Tight 450, and the .94em shrink made the accented word look weaker still —
   emphasis that de-emphasised. Newsreader is a low-contrast TEXT serif, so at
   500 its stems carry the same visual weight as the surrounding sans. The
   voice still changes; the colour of the line does not.

   Sizing runs the opposite way too: Instrument set optically LARGER than the
   sans so it was pulled back to .94em, whereas Newsreader's x-height is
   shorter, so it needs 1.04em to match. Matched on x-height, not em box. */
/* Same family as the headline now, so the x-height compensation Newsreader
   needed (1.04em) is gone — a sibling italic is already matched by design.
   Weight drops one step below the upright so the accent reads as a change of
   voice rather than a change of emphasis. */
.ital{font-family:var(--f-accent);font-style:italic;font-weight:600;
  font-size:1em;letter-spacing:-.01em;padding-right:.04em}
/* Variable-font fallback. This block mattered under v9, when the request asked
   for a variable Archivo with a width axis and a 500 weight: an engine without
   variable-font support got system-ui, which has neither a wdth axis nor a
   real 500, so both had to be pinned back.

   v10 requests three STATIC cuts (400/600/800) rather than a variable range,
   so there is nothing left to degrade — 800 is a weight every system face
   has, and no font-stretch is declared anywhere to be synthesised. The block
   is kept only to hold 800 for the case where Archivo fails to load entirely
   and a system face inherits --wt-display; it is no longer a variable-font
   guard, which is why the @supports condition has gone with it. */
h1,h2,.hl,.vband-h .hl,.pband-line .hl{font-stretch:normal}

/* Eyebrow: small caps label preceded by a live dot. The dot is the
   signature — it reads as telemetry rather than decoration. */
.eyebrow{display:flex;align-items:center;gap:.5rem;font-family:var(--f-mono);font-size:.6875rem;letter-spacing:.09em;text-transform:uppercase;margin-bottom:1rem;color:var(--text-faint);text-align:left}
/* Coral dot, not green. One rule, and it is the single change that makes the
   whole site read v7-warm — the eyebrow appears on 37 templates. */
/* v9 COLOUR LAW: the eyebrow dot was the single most-repeated illegal coral on
   the site — one per band, ~240 bands, on a mark that proposes nothing. It is
   ink now. The dot itself stays because dot + mono caps is the v6 signature
   and the law is about hue, not about deleting devices. */
.eyebrow::before{content:'';flex:none;width:6px;height:6px;border-radius:0;background:var(--ink);box-shadow:0 0 0 3px rgba(32, 30, 29,.07)}
.eyebrow.no-dot::before{display:none}
/* Centred variant, for the closing CTA band. .eyebrow is a flex row, so a
   centred context has to say justify-content — text-align alone won't move
   the dot + label pair. */
.tc .eyebrow,.cta .eyebrow,.eyebrow.tc,
[data-align="center"] .eyebrow,
[style*="text-align:center"] > .eyebrow,
[style*="text-align: center"] > .eyebrow{justify-content:center;text-align:center}

.desc{font-size:var(--fs-body);color:var(--text-body);line-height:var(--lh-body);max-width:66ch;margin:0 0 2.5rem;text-align:left}
.ssub{font-size:var(--fs-body);color:var(--text-body);line-height:var(--lh-body);max-width:66ch;margin:0;text-align:left}

/* Centering utility — content bands are left-aligned by default, so
   anything that genuinely needs to center opts in. */
.tc{text-align:center}
.tc h1,.tc h2,.tc h3{text-align:center}
.tc .desc,.tc .ssub,.tc .hero-sub{margin-left:auto;margin-right:auto;text-align:center}
.tc .btn-row,.tc .hero-actions{justify-content:center}

/* ==================== HERO ==================== */
.hero{background:var(--bg);padding:5.5rem 0 5rem;border-bottom:1px solid var(--hairline);transition:background .35s ease}
.hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:4.5rem;align-items:center}
.hero-grid>*{min-width:0}
.hero h1,.hb-head h1{font-size:var(--fs-display);letter-spacing:var(--tr-display);line-height:var(--lh-display)}
.hero-desc{font-size:var(--fs-body);color:var(--text-body);line-height:var(--lh-body);margin-bottom:2rem;max-width:56ch}
.hero-actions{display:flex;gap:.75rem;margin-bottom:1.75rem;flex-wrap:wrap;align-items:center}

/* ==================== BUTTONS (v6 — ink, not accent) ====================
   Primary is a flat ink fill with a 1px inner top highlight. No
   gradient, no neon, no lift-on-hover; it just darkens. The accent
   colour never fills a large surface — it only appears on text links,
   small pills and live dots. */
.btn-p{
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  padding:12px 22px;
  background:var(--ink);color:var(--bg);border:1px solid var(--ink);
  border-radius:0;
  font-family:var(--f-sans);font-size:.875rem;font-weight:600;
  cursor:pointer;
  box-shadow:var(--shadow-btn);
  transition:background .2s ease,box-shadow .2s ease,border-color .2s ease;
  letter-spacing:-.005em;line-height:1.2;
}
/* The intensify direction follows the fill, and the fill has now flipped twice.
   v6: ink fill on paper, hover darkens to #000. v7: paper fill on dark, hover
   brightens to #FFF. v8 is back to an ink fill, so it darkens again — leaving
   the v7 rule in place would have painted a white fill under a paper-coloured
   label, i.e. an invisible button, on every page. */
.btn-p:hover{background:#000000;border-color:#000000;box-shadow:var(--shadow-btn-hover)}
.btn-p:active{box-shadow:inset 0 1px 0 rgba(255,255,255,.06)}
.btn-p:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

.btn-s{
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  padding:12px 22px;
  background:var(--bg-card);color:var(--text);
  border:1px solid var(--border);
  border-radius:0;
  font-family:var(--f-sans);font-size:.875rem;font-weight:600;
  cursor:pointer;
  box-shadow:none;
  transition:background .2s ease,border-color .2s ease,color .2s ease;
  letter-spacing:-.005em;line-height:1.2;
}
.btn-s:hover{border-color:var(--border-hover);background:var(--bg-alt)}
.btn-s:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* Text link with a trailing arrow — mai's tertiary action. */
.btn-t{display:inline-flex;align-items:center;gap:5px;font-family:var(--f-sans);font-size:.90625rem;font-weight:600;color:var(--accent);letter-spacing:-.005em;transition:color var(--hov-out) var(--hov-ease)}
.btn-t:hover{text-decoration:underline;text-underline-offset:3px}
.btn-t::after{content:'→';font-size:.95em;transition:transform var(--hov-out) var(--hov-ease)}
.btn-t:hover::after{transform:translateX(3px);transition:transform var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.btn-t:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:0}

/* Small round pill — filters, tags, status chips. */
.pill{display:inline-flex;align-items:center;gap:5px;flex:none;border-radius:0;border:1px solid var(--border);background:transparent;color:var(--text-muted);padding:6px 13px;font-family:var(--f-sans);font-size:.71875rem;font-weight:600;cursor:pointer;transition:color var(--hov-out) var(--hov-ease),background-color var(--hov-out) var(--hov-ease),border-color var(--hov-out) var(--hov-ease)}
.pill:hover{color:var(--text);border-color:var(--border-hover);transition:color var(--hov-in) var(--hov-ease) var(--hov-in-delay),background-color var(--hov-in) var(--hov-ease) var(--hov-in-delay),border-color var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.pill.is-on{background:var(--accent);border-color:var(--accent);color:var(--bg)}
.pill:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

.btn-row{display:flex;gap:.75rem;flex-wrap:wrap;align-items:center}

.nav-cta{
  padding:9px 18px;
  background:var(--ink);border:1px solid var(--ink);
  border-radius:0;color:var(--bg);
  font-family:var(--f-sans);font-size:.875rem;font-weight:600;
  cursor:pointer;letter-spacing:var(--tr-ui);line-height:1.2;white-space:nowrap;
  box-shadow:var(--shadow-btn);
  transition:background .2s ease,box-shadow .2s ease;
  /* It is an <a> now, not a <button> — the underline has to be turned off. */
  text-decoration:none;
  display:inline-flex;align-items:center;gap:6px;
}
/* Same flip as .btn-p — see the note there. The fill is ink again on v8, so the
   intensify is downward and #FFFFFF would have erased the label. */
.nav-cta:hover{background:#000000;border-color:#000000;box-shadow:var(--shadow-btn-hover)}
.nav-cta:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* Form success banners used an <h2>, so on /contact, /get-started and
   /assessment the hidden "Thank you!" line was the page's first or only h2 —
   it sat in the document outline (and in any headings list a screen reader
   built) describing a state the visitor had not reached. It looks like a
   heading but is not one: styled here, marked up as text, announced via
   role="status" on the container when it actually appears. */
.form-success-title{font-family:var(--f-sans);font-size:clamp(1.35rem,2.4vw,1.75rem);
  font-weight:800;letter-spacing:-.03em;line-height:1.2;color:var(--text);margin:0 0 .75rem}

/* ==================== SCROLL ANIMATIONS ==================== */
/* Above-fold content should never be hidden by reveal */
.page-hero .reveal,.hero .reveal,.hero-enter{opacity:1!important;transform:none!important}
.reveal{opacity:0;transform:translateY(28px);transition:opacity .8s cubic-bezier(.22,1,.36,1),transform .8s cubic-bezier(.22,1,.36,1)}
.reveal.visible{opacity:1;transform:translateY(0)}
.reveal-stagger>*{opacity:0;transform:translateY(20px);transition:opacity .6s cubic-bezier(.22,1,.36,1),transform .6s cubic-bezier(.22,1,.36,1)}
.reveal-stagger.visible>*{opacity:1;transform:translateY(0)}
.reveal-stagger.visible>*:nth-child(1){transition-delay:.06s}
.reveal-stagger.visible>*:nth-child(2){transition-delay:.13s}
.reveal-stagger.visible>*:nth-child(3){transition-delay:.2s}
.reveal-stagger.visible>*:nth-child(4){transition-delay:.27s}
.reveal-stagger.visible>*:nth-child(5){transition-delay:.34s}
.reveal-stagger.visible>*:nth-child(6){transition-delay:.41s}
.reveal-stagger.visible>*:nth-child(7){transition-delay:.48s}
.reveal-stagger.visible>*:nth-child(8){transition-delay:.55s}
.reveal-scale{opacity:0;transform:scale(.92);transition:opacity .7s cubic-bezier(.22,1,.36,1),transform .7s cubic-bezier(.22,1,.36,1)}
.reveal-scale.visible{opacity:1;transform:scale(1)}
.reveal-left{opacity:0;transform:translateX(-30px);transition:opacity .8s cubic-bezier(.22,1,.36,1),transform .8s cubic-bezier(.22,1,.36,1)}
.reveal-right{opacity:0;transform:translateX(30px);transition:opacity .8s cubic-bezier(.22,1,.36,1),transform .8s cubic-bezier(.22,1,.36,1)}
.reveal-left.visible,.reveal-right.visible{opacity:1;transform:translateX(0)}

/* Hero entrance - plays once on load */
.hero-enter .eyebrow{opacity:0;transform:translateY(16px);animation:heroFade .6s cubic-bezier(.22,1,.36,1) .1s forwards}
.hero-enter h1{opacity:0;transform:translateY(20px);animation:heroFade .7s cubic-bezier(.22,1,.36,1) .2s forwards}
.hero-enter .hero-desc{opacity:0;transform:translateY(16px);animation:heroFade .7s cubic-bezier(.22,1,.36,1) .35s forwards}
.hero-enter .hero-actions{opacity:0;transform:translateY(16px);animation:heroFade .6s cubic-bezier(.22,1,.36,1) .5s forwards}
.hero-enter .llm-grid{opacity:0;transform:translateY(12px);animation:heroFade .6s cubic-bezier(.22,1,.36,1) .6s forwards}
.hero-enter .img-placeholder{opacity:0;transform:translateX(30px);animation:heroSlideIn .8s cubic-bezier(.22,1,.36,1) .3s forwards}
@keyframes heroFade{to{opacity:1;transform:translateY(0)}}
@keyframes heroSlideIn{to{opacity:1;transform:translateX(0)}}

/* ==================== LLM BADGES ==================== */
.llm-grid{display:flex;flex-wrap:wrap;gap:.5rem}
.llm-badge{font-family:var(--f-label);font-size:var(--fs-mono-xs);padding:5px 12px;border:1px solid var(--border);border-radius:0;color:var(--text-muted);display:flex;align-items:center;gap:8px;transition:all .25s}
.llm-badge:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}
.llm-dot{width:6px;height:6px;border-radius:0;background:var(--accent);animation:pulse-dot 2.5s ease infinite}
@keyframes pulse-dot{0%,100%{opacity:.6;box-shadow:0 0 0 0 var(--accent-glow)}50%{opacity:1;box-shadow:0 0 0 4px transparent}}

/* ==================== IMAGE PLACEHOLDERS ==================== */
.img-placeholder{border:2px dashed var(--border);border-radius:0;background:var(--bg-alt);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;padding:3rem 2rem;min-height:320px;text-align:center;transition:background .35s ease,border-color .35s ease}
.img-placeholder .ph-label{font-family:var(--f-label);font-size:var(--fs-mono-xs);letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint)}
.img-placeholder .ph-desc{font-size:var(--fs-mid);color:var(--text-muted);line-height:var(--lh-body);max-width:400px}
.img-placeholder .ph-size{font-family:var(--f-label);font-size:var(--fs-mono-xs);color:var(--border-hover)}

/* ==================== TERMINAL FRAME ==================== */
.terminal{border:1px solid var(--border);border-radius:0;overflow:hidden}
.terminal-bar{background:#131313;padding:10px 16px;display:flex;align-items:center;gap:6px;border-bottom:1px solid rgba(255,255,255,.06)}
.td{width:9px;height:9px;border-radius:0}
/* WINDOW BUTTONS ARE NOT A SEMANTIC RAMP. The macOS red/amber/green is a
   borrowed convention, not a decision this system made, and it was quietly the
   largest surviving source of second and third hues — three of them, on every
   product mock. One accent then two neutrals: the mock still reads instantly
   as a window, and the red stays where the system puts red, on the first
   square. Same three values in .browser-dots and .d-dots. */
.td:nth-child(1){background:#1d4ed8}.td:nth-child(2){background:#7d7979}.td:nth-child(3){background:#444141}
.terminal-title{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:#555;margin-left:auto;letter-spacing:1px}
.terminal-body{background:#201e1d;padding:2.5rem;min-height:320px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:2rem}

/* ==================== PRODUCT FRAMES ==================== */
.pf{border:1px solid var(--border);border-radius:0;overflow:hidden;transition:border-color .3s,box-shadow .3s}
.pf:hover{border-color:var(--border-hover);box-shadow:var(--shadow-sm)}
.pf-bar{padding:10px 16px;display:flex;align-items:center;gap:6px;border-bottom:1px solid var(--border);background:var(--bg-alt)}
.pf-t{font-family:var(--f-label);font-size:var(--fs-mono-xs);color:var(--text-faint);margin-left:auto;letter-spacing:var(--tr-caps)}
.pf-body{padding:2.5rem;min-height:260px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:1.25rem}
.pf-note{font-size:var(--fs-small);color:var(--text-muted);text-align:center;font-style:italic}
.pf-sub{font-family:var(--f-label);font-size:var(--fs-mono-xs);color:var(--text-faint);letter-spacing:var(--tr-caps);text-transform:uppercase}
.pf-c1{border-color:var(--c1-border)}.pf-c1 .pf-bar{background:var(--c1-soft)}.pf-c1:hover{border-color:var(--c1)}
.pf-c2{border-color:var(--c2-border)}.pf-c2 .pf-bar{background:var(--c2-soft)}.pf-c2:hover{border-color:var(--c2)}
.pf-c3{border-color:var(--c3-border)}.pf-c3 .pf-bar{background:var(--c3-soft)}.pf-c3:hover{border-color:var(--c3)}
.pf-c4{border-color:var(--c4-border)}.pf-c4 .pf-bar{background:var(--c4-soft)}.pf-c4:hover{border-color:var(--c4)}
.pf-c5{border-color:var(--c5-border)}.pf-c5 .pf-bar{background:var(--c5-soft)}.pf-c5:hover{border-color:var(--c5)}

/* ==================== PLACEHOLDER ANIMATIONS ==================== */
.anim-bars{display:flex;gap:10px;align-items:flex-end;height:90px}
.anim-bar{width:28px;border-radius:0;animation:bp 2s ease-in-out infinite}
.anim-bar:nth-child(1){background:var(--c1);height:40%;animation-delay:0s}
.anim-bar:nth-child(2){background:var(--c2);height:70%;animation-delay:.2s}
.anim-bar:nth-child(3){background:var(--c3);height:50%;animation-delay:.4s}
.anim-bar:nth-child(4){background:var(--c4);height:85%;animation-delay:.6s}
.anim-bar:nth-child(5){background:var(--c5);height:60%;animation-delay:.8s}
@keyframes bp{0%,100%{opacity:.7;transform:scaleY(1)}50%{opacity:1;transform:scaleY(1.15)}}
.flow-dots{display:flex;gap:14px;align-items:center}
.fdot{width:12px;height:12px;border-radius:0;animation:fp 3s ease infinite}
.fdot:nth-child(1){background:var(--c1);animation-delay:0s}
.fdot:nth-child(3){background:var(--c2);animation-delay:.5s}
.fdot:nth-child(5){background:var(--c3);animation-delay:1s}
.fdot:nth-child(7){background:var(--c4);animation-delay:1.5s}
.fdot:nth-child(9){background:var(--c5);animation-delay:2s}
.fa{color:var(--text-muted);font-family:var(--f-mono);font-size:12px}
@keyframes fp{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.3)}}
.ph-scan{display:flex;gap:6px;align-items:center}
.ph-sd{width:8px;height:8px;border-radius:0;background:var(--c5);animation:sb 1.2s ease infinite}
.ph-sd:nth-child(2){animation-delay:.2s}.ph-sd:nth-child(3){animation-delay:.4s}
@keyframes sb{0%,100%{opacity:.2}50%{opacity:1}}

/* ==================== CARDS ====================
   Ring instead of border, so the card edge sits inside the box and
   grids stay optically aligned. Elevation is two stacked shadows at
   3% and 5%. No translate on hover — the shadow deepens instead. */
.card-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.25rem}
.card{background:var(--bg-card);border:none;border-radius:0;padding:1.75rem;box-shadow:var(--shadow-card);transition:box-shadow .25s ease;position:relative;overflow:hidden}
.card:hover{box-shadow:inset 0 0 0 1px var(--border-hover)}
.card h3{font-size:1.0625rem;font-weight:600;margin-bottom:.4rem;color:var(--ink);letter-spacing:-.015em}
.card p{font-size:var(--fs-mid);color:var(--text-body);line-height:var(--lh-body)}
/* Step numeral: mono, hairline-weight, sits quietly above the title. */
.card-num{font-family:var(--f-label);font-size:.6875rem;font-weight:400;letter-spacing:var(--tr-caps);color:var(--text-faint);margin-bottom:.875rem;line-height:1}
.card-num::before{content:'';display:inline-block;width:6px;height:6px;border-radius:0;background:var(--accent-bright);margin-right:.5rem;vertical-align:middle}
/* Small elevated key tile — the 36px rounded square from mai's
   how-it-works band. */
.key-tile{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:0;background:var(--bg-card);box-shadow:var(--shadow-key);flex:none}

/* ==================== CAPABILITY CARDS (STACKED ACCORDION) ==================== */
.agent-grid{display:flex;flex-direction:column;gap:0;position:relative}
.ac{border:1px solid var(--hairline);overflow:hidden;transition:background .25s ease,border-color .25s ease;cursor:pointer;background:var(--bg-card)}
.ac:first-child{border-radius:0}
.ac:last-child{border-radius:0}
.ac+.ac{border-top:0}
.ac-h{padding:1.0625rem 1.5rem;display:flex;align-items:center;gap:.75rem;position:relative;user-select:none}
.ac-step{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint);margin-right:.25rem}
.ac-icon{font-size:1.125rem}.ac-fn{font-weight:600;font-size:var(--fs-mid);letter-spacing:-.015em}
.ac-arrow{margin-left:auto;font-size:.75rem;color:var(--text-faint);transition:transform .3s ease}
.ac.open .ac-arrow{transform:rotate(180deg)}
.ac-flow{position:absolute;right:1.5rem;top:50%;transform:translateY(-50%);display:flex;align-items:center;gap:.5rem}
.ac-flow-line{width:24px;height:1px;background:var(--border)}
.ac-body{max-height:0;overflow:hidden;transition:max-height .4s cubic-bezier(.22,1,.36,1),padding .3s ease;padding:0 1.5rem;background:var(--bg-card)}
.ac.open .ac-body{max-height:200px;padding:1.25rem 1.5rem;border-top:1px solid var(--border)}
.ac-body p{font-size:var(--fs-mid);color:var(--text-body);line-height:var(--lh-body);margin:0}
.ac-tandem{display:flex;align-items:center;gap:.5rem;margin-top:.5rem;font-family:var(--f-label);font-size:.7rem;color:var(--text-faint);letter-spacing:.5px}
.ac-tandem span{color:var(--accent)}
.ac1 .ac-h{background:var(--c1-soft)}.ac1 .ac-fn{color:var(--c1)}.ac1.open{border-color:var(--c1)}
.ac2 .ac-h{background:var(--c2-soft)}.ac2 .ac-fn{color:var(--c2)}.ac2.open{border-color:var(--c2)}
.ac3 .ac-h{background:var(--c3-soft)}.ac3 .ac-fn{color:var(--c3)}.ac3.open{border-color:var(--c3)}
.ac4 .ac-h{background:var(--c4-soft)}.ac4 .ac-fn{color:var(--c4)}.ac4.open{border-color:var(--c4)}
.ac5 .ac-h{background:var(--c5-soft)}.ac5 .ac-fn{color:var(--c5)}.ac5.open{border-color:var(--c5)}

/* ==================== STATS ====================
   Numbers carry in ink, not accent — a wall of green numerals is the
   opposite of restrained. Set in the display sans with tabular figures
   so columns line up. */
.stat-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:1rem}
.sc{border:none;border-radius:0;padding:1.625rem 1.25rem;text-align:left;background:var(--bg-card);box-shadow:var(--shadow-ring);transition:box-shadow .25s ease}
.sc:hover{box-shadow:inset 0 0 0 1px var(--border-hover)}
.sc-l{display:flex;align-items:center;gap:.4rem;font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint);margin-bottom:.625rem}
.sc-l::before{content:'';flex:none;width:5px;height:5px;border-radius:0;background:var(--accent-bright)}
.sc-v{font-family:var(--f-sans);font-size:clamp(1.75rem,3vw,2.25rem);font-weight:800;line-height:1;letter-spacing:-.04em;color:var(--ink);font-variant-numeric:tabular-nums}
.sc-m{font-size:var(--fs-small);color:var(--text-muted);margin-top:.4rem;line-height:1.5}

/* Animated stat counter */
.sc-v[data-count]{transition:color .3s}

/* ==================== INTEGRATIONS ==================== */
.int-grid{display:flex;flex-wrap:wrap;gap:8px}
.ii{display:flex;align-items:center;gap:8px;font-size:.8125rem;font-weight:600;color:var(--text-muted);padding:7px 14px;border:1px solid var(--border);border-radius:0;transition:color .2s ease,border-color .2s ease,background .2s ease;background:var(--bg-card)}
.ii:hover{border-color:var(--border-hover);color:var(--text);background:var(--bg-alt)}
.id{width:6px;height:6px;border-radius:0;flex:none}.id.live{background:var(--accent-bright)}.id.coming{background:var(--warning)}
.is{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;margin-left:2px}
.is.live{color:var(--success)}.is.coming{color:var(--warning)}

/* ==================== ROLE CARDS ==================== */
.rg{display:grid;grid-template-columns:repeat(4,1fr);gap:1.25rem}
.rc{border:none;border-radius:0;padding:1.75rem;transition:box-shadow .25s ease;background:var(--bg-card);box-shadow:var(--shadow-card)}
.rc:hover{box-shadow:inset 0 0 0 1px var(--border-hover)}
.rc-t{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint);margin-bottom:.625rem}
.rc-n{font-size:1.125rem;font-weight:600;letter-spacing:-.02em;margin-bottom:.4rem;color:var(--ink)}
.rc-d{font-size:var(--fs-mid);color:var(--text-body);line-height:var(--lh-body);margin-bottom:1rem}
.rc-tags{display:flex;gap:6px;flex-wrap:wrap}
.rt{font-family:var(--f-sans);font-size:.71875rem;font-weight:600;padding:5px 11px;border-radius:0}
.rt1{color:var(--c1);border:1px solid var(--c1-border);background:var(--c1-soft)}
.rt2{color:var(--c2);border:1px solid var(--c2-border);background:var(--c2-soft)}
.rt3{color:var(--c3);border:1px solid var(--c3-border);background:var(--c3-soft)}
.rt4{color:var(--c4);border:1px solid var(--c4-border);background:var(--c4-soft)}
.rt5{color:var(--c5);border:1px solid var(--c5-border);background:var(--c5-soft)}

/* ==================== SERVICE DETAIL ==================== */
.svc-detail{border:none;border-radius:0;margin-bottom:1.25rem;transition:box-shadow .25s ease;overflow:hidden;background:var(--bg-card);box-shadow:var(--shadow-card)}
.svc-detail:hover{box-shadow:inset 0 0 0 1px var(--border-hover)}
.svc-head{padding:1.375rem 1.75rem;display:flex;align-items:center;gap:1.125rem;cursor:pointer;border-bottom:1px solid var(--hairline)}
.svc-head .svc-icon{font-family:var(--f-mono);font-size:1.25rem;color:var(--accent)}
.svc-head h3{flex:1;font-size:1.0625rem;margin-bottom:0}
.svc-head .svc-tag{font-family:var(--f-label);font-size:.6875rem;text-transform:uppercase;color:var(--text-faint);letter-spacing:var(--tr-caps)}
.svc-body{padding:2rem;display:grid;grid-template-columns:2fr 1fr;gap:2.5rem}
.svc-body p{font-size:var(--fs-mid);color:var(--text-body);line-height:var(--lh-body);margin-bottom:1rem}
.svc-list{list-style:none;padding:0}.svc-list li{font-size:var(--fs-mid);color:var(--text-body);padding:.35rem 0;display:flex;gap:.5rem}
.svc-list li::before{content:'✓';flex:none;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;margin-top:.28em;border-radius:0;background:var(--accent-soft);color:var(--success);font-size:.625rem;font-weight:600}
.svc-meta{display:flex;flex-direction:column;gap:1rem}
.svc-meta-item{border:none;box-shadow:var(--shadow-ring);border-radius:0;padding:1rem;display:flex;flex-direction:column;gap:.25rem;background:var(--bg-alt)}
.svc-meta-label{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint)}
.svc-meta-val{font-size:var(--fs-mid);color:var(--text-body)}

/* ==================== PRICE CARDS ==================== */
.price-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1.25rem}
.price-card{border:none;border-radius:0;padding:2rem;transition:box-shadow .25s ease;background:var(--bg-card);box-shadow:var(--shadow-card)}
.price-card:hover{box-shadow:inset 0 0 0 1px var(--border-hover)}
.price-card.featured{position:relative;box-shadow:inset 0 0 0 1.5px var(--ink)}
.price-card.featured::before{content:'POPULAR';position:absolute;top:1.25rem;right:1.25rem;font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);padding:4px 9px;background:var(--ink);color:var(--bg);border-radius:0}
.price-name{font-family:var(--f-label);font-size:.6875rem;text-transform:uppercase;letter-spacing:var(--tr-caps);color:var(--text-faint);margin-bottom:.875rem}
.price-val{font-family:var(--f-sans);font-size:clamp(2rem,3.2vw,2.5rem);font-weight:800;letter-spacing:-.045em;color:var(--ink);margin-bottom:.25rem;font-variant-numeric:tabular-nums}
.price-val span{font-family:var(--f-sans);font-size:var(--fs-mid);font-weight:400;letter-spacing:-.01em;color:var(--text-muted)}
.price-desc{font-size:var(--fs-mid);color:var(--text-muted);margin-bottom:1.5rem;line-height:var(--lh-body)}
.price-features{list-style:none;padding:0;margin-bottom:2rem}
.price-features li{font-size:var(--fs-mid);color:var(--text-body);padding:.4rem 0;display:flex;gap:.5rem}
.price-features li::before{content:'✓';flex:none;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;margin-top:.28em;border-radius:0;background:var(--accent-soft);color:var(--success);font-size:.625rem;font-weight:600}

/* ==================== CHAT SHOWCASE ==================== */
.chat-demo{border:1px solid var(--accent);border-radius:0;overflow:hidden;box-shadow:none}
.chat-bar{background:var(--accent-soft);padding:12px 20px;display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--accent)}
.chat-bar-dot{width:8px;height:8px;border-radius:0;background:var(--accent);animation:pulse-dot 2.5s ease infinite}
.chat-bar-title{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--accent);letter-spacing:1px}
.chat-body{padding:2rem;background:var(--bg-card);display:flex;flex-direction:column;gap:1.25rem}
.chat-msg{max-width:75%;padding:12px 18px;font-size:var(--fs-mid);line-height:var(--lh-body);border-radius:0}
.chat-user{align-self:flex-end;background:var(--accent);color:var(--bg);border-radius:0}
.chat-ai{align-self:flex-start;background:var(--bg-alt);border:1px solid var(--border);color:var(--text-body);border-radius:0}
.chat-ai .chat-source{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--text-faint);margin-top:.5rem;letter-spacing:1px}
.chat-input{display:flex;gap:0;border-top:1px solid var(--border);background:var(--bg-alt)}
.chat-input input{flex:1;padding:14px 20px;border:none;background:transparent;font-family:var(--f-sans);font-size:var(--fs-mid);outline:none;color:var(--text);border-radius:0}
.chat-input button{padding:14px 24px;background:var(--ink);color:var(--bg);border:none;font-family:var(--f-sans);font-size:var(--fs-small);font-weight:600;cursor:pointer;border-radius:0;transition:background .2s}
.chat-input button:hover{background:var(--accent-hover)}

/* ==================== PRODUCT WIDGETS ==================== */
/* Base widget (matches chat-demo style) */
.pw{border:1px solid var(--accent);border-radius:0;overflow:hidden;box-shadow:none}
.pw-bar{background:var(--accent-soft);padding:10px 16px;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--accent)}
.pw-dot{width:7px;height:7px;border-radius:0;background:var(--accent);flex-shrink:0}
.pw-dot.live{animation:pulse-dot 2.5s ease infinite}
.pw-title{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--accent);letter-spacing:1px}
.pw-bar-r{margin-left:auto;font-family:var(--f-mono);font-size:0.6875rem;color:var(--text-faint)}
.pw-body{background:var(--bg-card);padding:0}

/* Browser Chrome */
.browser-chrome{border:1px solid var(--border);border-radius:0;overflow:hidden;box-shadow:var(--shadow-lg)}
.browser-top{background:var(--bg-alt);padding:10px 16px;display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--border)}
.browser-dots{display:flex;gap:6px}
.browser-dots span{width:10px;height:10px;border-radius:0;border:1px solid var(--border)}
.browser-dots span:first-child{background:#1d4ed8;border-color:#1d4ed8}
.browser-dots span:nth-child(2){background:#7d7979;border-color:#7d7979}
.browser-dots span:last-child{background:#444141;border-color:#444141}
.browser-url{flex:1;background:var(--bg-card);border:1px solid var(--border);border-radius:0;padding:5px 12px;font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--text-muted);display:flex;align-items:center;gap:6px}
/* The padlock glyph itself was removed from the templates in the v10.1 emoji
   sweep — a mock URL bar reads as a browser without one. Rule kept, neutral,
   for any mark a future mock puts in this slot. */
.browser-url .lock{color:var(--text-faint);font-size:0.625rem}
.browser-viewport{background:var(--bg-card);padding:0;position:relative}

/* Browser with sidebar layout */
.bv-layout{display:grid;grid-template-columns:200px 1fr;min-height:420px}
@media(max-width:768px){.bv-layout{grid-template-columns:1fr;min-height:auto}}
.bv-sidebar{background:var(--bg-alt);border-right:1px solid var(--border);padding:16px 0;display:flex;flex-direction:column}
@media(max-width:768px){.bv-sidebar{display:none}}
.bv-logo{padding:0 16px 16px;font-family:var(--f-mono);font-size:var(--fs-mono-sm);font-weight:700;color:var(--text);border-bottom:1px solid var(--border);margin-bottom:8px}
.bv-nav-item{padding:7px 16px;font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--text-muted);display:flex;align-items:center;gap:8px;cursor:default;transition:all .15s}
.bv-nav-item:hover{background:var(--bg-card);color:var(--text)}
.bv-nav-item.active{background:var(--accent-soft);color:var(--accent);border-right:2px solid var(--accent)}
.bv-nav-icon{width:14px;text-align:center;font-size:0.75rem}
.bv-main{padding:20px;overflow:hidden;display:flex;flex-direction:column;gap:16px}
.bv-main-header{display:flex;align-items:center;justify-content:space-between;gap:12px}
.bv-main-title{font-family:var(--f-mono);font-size:var(--fs-mono-sm);font-weight:700;color:var(--text)}
/* v10.1 — see the STATUS CHIP note in the enforcement layer. Was a green fill
   and border under a --success label that v10 had already turned red. */
.bv-main-badge{font-family:var(--f-mono);font-size:0.625rem;padding:2px 8px;background:var(--bg-alt);color:var(--text);border:1px solid var(--n-300);border-radius:0;letter-spacing:.5px}

/* Feed items */
.pw-feed{display:flex;flex-direction:column}
.pw-row{padding:9px 16px;border-bottom:1px solid var(--border);display:flex;gap:10px;align-items:flex-start;font-size:var(--fs-small);transition:background .15s}
.pw-row:last-child{border-bottom:none}
.pw-row:hover{background:var(--bg-alt)}
.pw-sev{font-family:var(--f-mono);font-size:0.625rem;font-weight:700;letter-spacing:.5px;padding:2px 6px;border-radius:0;flex-shrink:0;margin-top:1px}
/* THE SEVERITY LADDER, IN ONE HUE. Four states that used to be four colours
   (red / amber / blue / green) are now four FORMS on the same red-and-ink
   ramp, strongest to quietest:
     crit    solid red fill, white label      — loudest thing in the panel
     warn    red tint, red label, red border  — same hue, no fill
     info    ink outline on the tint          — neutral, still marked
     ok      no fill, no border, faint label  — the absence of a problem
   Read down that list and the severity is legible without colour at all, which
   is the property a one-hue system has to buy back. Do not reintroduce a green
   "ok": ok is the resting state, and the resting state is unmarked. */
.pw-sev.crit{background:var(--brand);color:#fff;border:1px solid var(--brand)}
.pw-sev.warn{background:var(--acc-100);color:var(--brand-text);border:1px solid var(--acc-300)}
.pw-sev.info{background:var(--bg-alt);color:var(--text-body);border:1px solid var(--n-300)}
.pw-sev.ok{background:transparent;color:var(--text-faint);border:1px solid transparent}
.pw-txt{flex:1;color:var(--text-body);line-height:1.45}
.pw-txt strong{color:var(--text);font-weight:600}
.pw-time{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--text-faint);flex-shrink:0;margin-top:2px}
.pw-src{font-family:var(--f-mono);font-size:0.625rem;color:var(--text-faint);margin-top:2px;letter-spacing:.5px}

/* Metric grid */
.pw-metrics{display:grid;grid-template-columns:repeat(3,1fr);gap:0}
@media(max-width:600px){.pw-metrics{grid-template-columns:repeat(2,1fr)}}
.pw-metric{padding:14px 16px;border-right:1px solid var(--border);border-bottom:1px solid var(--border)}
.pw-metric:nth-child(3n){border-right:none}
.pw-metric:nth-last-child(-n+3){border-bottom:none}
@media(max-width:600px){.pw-metric:nth-child(3n){border-right:1px solid var(--border)}.pw-metric:nth-child(2n){border-right:none}}
.pw-m-label{font-family:var(--f-mono);font-size:0.625rem;color:var(--text-faint);letter-spacing:1px;text-transform:uppercase;margin-bottom:3px}
.pw-m-val{font-family:var(--f-mono);font-size:1.125rem;font-weight:700;color:var(--text)}
.pw-m-delta{font-family:var(--f-mono);font-size:var(--fs-mono-xs);margin-top:2px}
.pw-m-delta.up{color:var(--success)}
.pw-m-delta.down{color:#1d4ed8}
.pw-m-delta.flat{color:var(--text-faint)}

/* Sparkline */
.pw-spark{display:flex;align-items:flex-end;gap:2px;height:24px;margin-top:5px}
.pw-spark b{width:4px;border-radius:0;background:var(--accent);opacity:.35}
.pw-spark b:last-child{opacity:1}

/* Agent log */
.pw-log-row{padding:7px 16px;border-bottom:1px solid var(--border);display:flex;gap:8px;align-items:flex-start;font-size:var(--fs-small)}
.pw-log-row:last-child{border-bottom:none}
.pw-log-ts{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--text-faint);flex-shrink:0;min-width:48px;margin-top:1px}
.pw-log-agent{font-family:var(--f-mono);font-size:0.5625rem;padding:2px 5px;border-radius:0;flex-shrink:0;font-weight:700;letter-spacing:.5px;margin-top:1px}
.pw-log-agent.a-journey{background:var(--c1-soft);color:var(--c1);border:1px solid var(--c1-border)}
.pw-log-agent.a-change{background:var(--c2-soft);color:var(--c2);border:1px solid var(--c2-border)}
.pw-log-agent.a-forecast{background:var(--c3-soft);color:var(--c3);border:1px solid var(--c3-border)}
.pw-log-agent.a-root{background:var(--c4-soft);color:var(--c4);border:1px solid var(--c4-border)}
.pw-log-agent.a-strategy{background:var(--c5-soft);color:var(--c5);border:1px solid var(--c5-border)}
.pw-log-agent.a-strategy{color:var(--text-muted)}
.pw-log-msg{flex:1;color:var(--text-body);line-height:1.45}

/* Sync rows */
.pw-sync-row{padding:9px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;font-size:var(--fs-small)}
.pw-sync-row:last-child{border-bottom:none}
/* Holds a two-letter monogram since the v10.1 emoji sweep — see .df-ico. */
.pw-sync-icon{width:26px;height:26px;border-radius:0;background:var(--bg-alt);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;font-family:var(--f-display);font-size:10px;font-weight:800;letter-spacing:.01em;color:var(--text-body);line-height:1;flex-shrink:0}
.pw-sync-name{font-weight:600;color:var(--text);flex:1;font-size:var(--fs-small)}
.pw-sync-badge{font-family:var(--f-mono);font-size:0.5625rem;padding:2px 7px;border-radius:0;letter-spacing:.5px;font-weight:600}
/* SYNCED is the resting state of ten rows at once — it is the least
   interesting thing on the panel and used to be the greenest. Quiet ink.
   SYNCING is the one that is happening now, so it takes the accent and the
   pulse, matching .pulse-dot elsewhere. */
.pw-sync-badge.synced{background:transparent;color:var(--text-faint);border:1px solid var(--n-300)}
.pw-sync-badge.syncing{background:var(--acc-100);color:var(--brand-text);border:1px solid var(--acc-300);animation:pw-pulse 2s ease infinite}
.pw-sync-badge.queued{background:var(--bg-alt);color:var(--text-faint);border:1px solid var(--border)}
.pw-sync-ago{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--text-faint)}
@keyframes pw-pulse{0%,100%{opacity:1}50%{opacity:.5}}

/* Tab bar inside widgets */
.pw-tabs{display:flex;border-bottom:1px solid var(--border);background:var(--bg-alt)}
.pw-tab{padding:8px 14px;font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--text-muted);letter-spacing:.5px;border-bottom:2px solid transparent;cursor:default;transition:all .15s}
.pw-tab.active{color:var(--accent);border-bottom-color:var(--accent);background:var(--bg-card)}

/* ==================== DATA FLOW ANIMATION ==================== */
.df{position:relative;padding:2rem 0}
.df-row{display:flex;align-items:center;justify-content:center;gap:0}
@media(max-width:768px){.df-row{flex-direction:column;gap:2rem}}

/* Source / output nodes */
.df-nodes{display:flex;flex-direction:column;gap:8px;flex-shrink:0;width:200px}
@media(max-width:768px){.df-nodes{flex-direction:row;flex-wrap:wrap;justify-content:center;width:auto}}
.df-node{display:flex;align-items:center;gap:10px;padding:8px 14px;border:1px solid var(--border);border-radius:0;font-family:var(--f-mono);font-size:0.75rem;color:var(--text-muted);background:var(--bg-card);transition:all .3s;white-space:nowrap;overflow:visible}
.df-node:hover{border-color:var(--accent);color:var(--accent)}
.df-node .df-ico{width:32px;height:32px;min-width:32px;border-radius:0;border:1px solid var(--border);display:flex;align-items:center;justify-content:center;flex-shrink:0;background:var(--bg-alt);padding:6px;overflow:hidden;box-sizing:border-box}
.df-node .df-ico svg{width:18px;height:18px;display:block;flex-shrink:0}
/* v10.1: this 32px box used to hold an emoji. It now holds either a two-letter
   monogram (named products — Shopify, Stripe, GA4) or a typographic mark
   (concepts — Analytics, Payments, Chat), and both need type set on them: an
   emoji brought its own size and colour, a glyph inherits the node's 13px body
   text and lands small and grey inside a 32px frame. Marks run a little larger
   than monograms because a single geometric glyph reads lighter than two
   letters at the same size. */
.df-node .df-ico{font-family:var(--f-display);font-size:11px;font-weight:800;
  letter-spacing:.01em;color:var(--text);line-height:1}
.df-node.active .df-ico{color:var(--brand-text)}
.df-node .df-lbl{flex-shrink:0;overflow:hidden;text-overflow:ellipsis}
.df-node.active{border-color:var(--accent);background:var(--accent-soft)}
.df-node.active .df-ico{border-color:var(--accent);background:var(--accent-soft)}

/* Flow lines */
.df-line{flex:1;min-width:60px;height:2px;background:var(--border);position:relative;overflow:visible}
@media(max-width:768px){.df-line{min-width:2px;width:2px;height:40px;flex:none}}
.df-line::after{content:'';position:absolute;top:-2px;left:0;width:6px;height:6px;border-radius:0;background:var(--accent);animation:df-dot-lr 2.5s linear infinite;box-shadow:none}
.df-line.reverse::after{animation:df-dot-rl 2.5s linear infinite}
@media(max-width:768px){.df-line::after{animation:df-dot-tb 2.5s linear infinite}.df-line.reverse::after{animation:df-dot-bt 2.5s linear infinite}}
.df-line.d1::after{animation-delay:0s}
.df-line.d2::after{animation-delay:.4s}
.df-line.d3::after{animation-delay:.8s}
.df-line.d4::after{animation-delay:1.2s}
@keyframes df-dot-lr{0%{left:0;opacity:0}10%{opacity:1}90%{opacity:1}100%{left:100%;opacity:0}}
@keyframes df-dot-rl{0%{left:100%;opacity:0}10%{opacity:1}90%{opacity:1}100%{left:0;opacity:0}}
@keyframes df-dot-tb{0%{top:0;opacity:0}10%{opacity:1}90%{opacity:1}100%{top:100%;opacity:0}}
@keyframes df-dot-bt{0%{top:100%;opacity:0}10%{opacity:1}90%{opacity:1}100%{top:0;opacity:0}}

/* Multi-line fanout */
.df-fan{display:flex;flex-direction:column;gap:6px;flex:1;min-width:40px;max-width:100px;position:relative}
@media(max-width:768px){.df-fan{flex-direction:row;min-width:auto;max-width:none;min-height:30px;gap:4px}}
.df-fan .df-line{flex:none;height:2px;width:100%}
@media(max-width:768px){.df-fan .df-line{height:30px;width:2px}}

/* Hub node (data lake center) */
.df-hub{width:140px;height:140px;border-radius:0;border:2px solid var(--accent);background:var(--accent-soft);display:flex;flex-direction:column;align-items:center;justify-content:center;flex-shrink:0;position:relative;z-index:2;box-shadow:none;transition:transform .3s ease}
.df-hub::before{content:'';position:absolute;inset:-6px;border-radius:0;border:1px solid var(--accent);opacity:.3;animation:df-hub-pulse 3s ease infinite}
@keyframes df-hub-pulse{0%,100%{opacity:.3;transform:scale(1)}50%{opacity:.1;transform:scale(1.06)}}
.df-hub-icon{font-size:1.5rem;margin-bottom:2px}
.df-hub-label{font-family:var(--f-mono);font-size:0.625rem;letter-spacing:1.5px;color:var(--accent);font-weight:700}
.df-hub-sub{font-family:var(--f-mono);font-size:0.5625rem;color:var(--text-faint);margin-top:2px}

/* Output nodes (right side) */
.df-out{display:flex;flex-direction:column;gap:8px;flex-shrink:0;width:210px}
@media(max-width:768px){.df-out{flex-direction:row;flex-wrap:wrap;justify-content:center;width:auto}}
.df-out .df-node{border-left:3px solid var(--accent);border-radius:0}
.df-out .df-node.c1{border-left-color:var(--c1)}
.df-out .df-node.c2{border-left-color:var(--c2)}
.df-out .df-node.c3{border-left-color:var(--c3)}
.df-out .df-node.c4{border-left-color:var(--c4)}
.df-out .df-node.c5{border-left-color:var(--c5)}
.df-out .df-node.active{background:var(--accent-soft);border-color:var(--accent)}

/* ==================== ANIMATED DATA FLOW (SVG) ==================== */
.adf{border:1px solid var(--border);border-radius:0;background:var(--bg-card);padding:1.5rem}
.adf-svg{display:block}
.adf-hub-ring{transition:filter .3s}
.adf-hub-pulse{animation:adf-pulse 3s ease infinite}
.adf-hub-pulse2{animation:adf-pulse 3s ease infinite .5s}
/* `r` is an SVG geometry property here, not the presentation attribute — in CSS
   it needs a unit. Unitless it is invalid, the whole declaration is dropped, and
   the circle simply never changed radius; only the opacity half of this
   animation was ever running. */
@keyframes adf-pulse{0%,100%{r:68px;opacity:.15}50%{r:78px;opacity:.05}}

/* ── KPI Counter Strip ── */
/* Self-contained card: full border, radius on all four corners. It was written
   top-less and rounded only at the bottom, as if it hangs off a panel above it —
   but it has exactly one consumer (the hero on /agencies) and that consumer was
   overriding both properties back with an inline style. The class now describes
   how it is actually used, so the next reuse does not inherit a half-drawn box
   and have to undo it again. */
.kpi-strip{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--border);border:1px solid var(--border);border-radius:0;overflow:hidden}
.kpi{background:var(--bg-card);padding:14px 12px;text-align:center;transition:background .3s ease}
.kpi-val{font-family:var(--f-mono);font-size:1.125rem;font-weight:700;color:var(--text);display:block;transition:color .2s,text-shadow .2s;min-height:1.4em}
.kpi-label{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint);margin-top:4px;display:block}
.kpi.kpi-flash{background:var(--accent-soft)}
.kpi.kpi-flash .kpi-val{color:var(--accent);text-shadow:0 0 10px var(--accent-glow)}

@media(max-width:768px){
  .adf{padding:.75rem;min-height:auto!important}
  .adf-svg{min-height:280px}
  .adf-strip{flex-wrap:wrap;gap:8px!important}
  .adf-strip svg{width:40px!important}
  .kpi-strip{grid-template-columns:repeat(2,1fr)}
  .kpi-val{font-size:0.9rem}
}
@media(max-width:768px){.df-out .df-node{border-left:none;border-top:3px solid var(--accent);border-radius:0}}

/* ==================== ANIMATED CHAT ==================== */
.otter-chat-wrap{max-width:680px;margin:0 auto}
.split .otter-chat-wrap{max-width:100%}
.otter-chat-wrap .chat-body{min-height:320px;position:relative;overflow:hidden;padding:1.5rem}
.otter-chat-wrap .chat-msg{opacity:0;transform:translateY(12px);transition:opacity .4s ease,transform .4s ease;max-width:82%}
.otter-chat-wrap .chat-msg.msg-visible{opacity:1;transform:translateY(0)}
.otter-chat-wrap .chat-msg.msg-exit{opacity:0;transform:translateY(-10px);transition:opacity .3s ease,transform .3s ease}

/* Thinking dots */
.otter-thinking{align-self:flex-start;display:flex;gap:5px;padding:14px 20px;background:var(--bg-alt);border:1px solid var(--border);border-radius:0;opacity:0;transform:translateY(12px);transition:opacity .3s ease,transform .3s ease}
.otter-thinking.msg-visible{opacity:1;transform:translateY(0)}
.otter-thinking span{width:7px;height:7px;border-radius:0;background:var(--text-faint);animation:think-bounce 1.2s ease infinite}
.otter-thinking span:nth-child(2){animation-delay:.15s}
.otter-thinking span:nth-child(3){animation-delay:.3s}
@keyframes think-bounce{0%,80%,100%{transform:translateY(0);opacity:.4}40%{transform:translateY(-6px);opacity:1}}

/* Typing cursor in input field */
.chat-typing-field{flex:1;padding:14px 20px;font-family:var(--f-sans);font-size:var(--fs-mid);color:var(--text);min-height:20px;display:flex;align-items:center}
.chat-typing-field::after{content:'';display:inline-block;width:2px;height:1.1em;background:var(--accent);margin-left:1px;animation:blink-cursor .7s step-end infinite;vertical-align:text-bottom}
.chat-typing-field.idle::after{display:none}
@keyframes blink-cursor{50%{opacity:0}}

/* Role tag on AI messages */
.chat-role{font-family:var(--f-mono);font-size:10px;letter-spacing:1.5px;text-transform:uppercase;margin-bottom:6px;display:block}

/* ==================== FORM ==================== */
.fg{margin-bottom:1.25rem}
.fl{display:block;font-family:var(--f-label);font-size:var(--fs-mono-xs);letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-muted);margin-bottom:.5rem}
.fi{width:100%;padding:12px 16px;background:var(--bg-card);border:1px solid var(--border);border-radius:0;color:var(--text);font-family:var(--f-sans);font-size:var(--fs-mid);transition:all .25s;outline:none}
.fi:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}
.fi::placeholder{color:var(--text-faint)}
textarea.fi{resize:vertical}

/* ==================== PRODUCT CONSOLE (.wt) ====================
   The console has now been all three ways: v6 dark-on-paper, v7 dark-on-dark,
   v8 paper-on-paper. v8 removes the last dark surface on the site.

   The argument for keeping it dark was that a console "should" look like a
   console. The argument against — and the reason it is light here — is that a
   dark panel is a second theme by another name. Every widget inside .wt has to
   be authored and checked against a ramp nothing else on the site uses, which
   is exactly the drift that killed `[data-theme]`. One surface, checked once.

   What sells it as a panel changes with the surface. On dark it was the inset
   top highlight, because a drop shadow against #0B0B0A is invisible. On paper
   the shadow does the work: the console is the only WHITE thing on a warm-paper
   page, lifted by a real warm shadow, and its own cards step back DOWN to the
   page colour. The step therefore runs the opposite direction from v7 —
   page #f3f2f2 → console #FFFFFF → its cards #f3f2f2 → its wells #f8f4f4.

   THE CONSOLE NO LONGER KEEPS A COOLER ACCENT. That rule survived v6 and v7 —
   page accent coral (marketing), console accent blue (data), so a chart line
   never competed with a CTA for meaning — and v10 ended it: with ONE hue in the
   system there is no second accent to give the console, and --accent here is
   the neutral #444141. The rule was still described in this comment for a
   release after the value stopped implementing it, which is how the brown --c2
   and the violet --c3 tints below survived the sweep: the comment justified
   them. If a chart line has to be told apart from a CTA now, it is told apart
   by weight and by being inside a framed panel, not by hue.

   What this block is still FOR: the console runs denser and smaller type than
   the page, so its text rungs are set darker than the page equivalents. That is
   the only reason .wt keeps a token scope at all. */
.wt{
  --bg:#FFFFFF;--bg-alt:#F7F5F1;--bg-card:#f3f2f2;--bg-raised:#f8f4f4;
  /* Console copy runs denser and smaller than page prose — 11px telemetry, not
     17px paragraphs — so it sets DARKER than the page equivalents rather than
     lighter. Checked against #f8f4f4, the darkest surface inside the panel and
     the only one these ever have trouble on. All four clear 4.5:1 there:
       --text 16.47 · --text-body 11.40 · --text-muted 6.95 · --text-faint 5.50 */
  --text:#201e1d;--text-body:#33312C;--text-muted:#54514A;--text-faint:#63605A;
  --border:rgba(32, 30, 29,.13);--border-hover:rgba(32, 30, 29,.24);--border-focus:#201e1d;
  --accent:#444141;--accent-hover:#1e3a8a;--accent-soft:rgba(29,78,216,.07);
  --accent-glow:rgba(29,78,216,.16);
  --accent-bright:#444141;
  --success:#0e6b38;--warning:#8f4408;--danger:#b91c1c;
  --shadow-sm:0 1px 2px rgba(32, 30, 29,.05);
  --shadow-md:0 2px 4px rgba(32, 30, 29,.04),0 8px 20px rgba(32, 30, 29,.06);
  --shadow-lg:0 4px 8px rgba(32, 30, 29,.05),0 20px 44px rgba(32, 30, 29,.08);
  --shadow-ring:inset 0 0 0 1px var(--border);
  --shadow-card:inset 0 0 0 1px var(--border),0 2px 8px rgba(32, 30, 29,.04);
  --shadow-btn:0 1px 2px rgba(32, 30, 29,.18),inset 0 1px 0 rgba(255,255,255,.12);
  --nav-bg:rgba(255,255,255,.92);
  --grid-line:rgba(32, 30, 29,.018);
  --c1:#444141;--c1-soft:rgba(29,78,216,.06);--c1-border:rgba(29,78,216,.20);
  /* v10.1: these two were the last chromatic residue in the console scope —
     --c2 a brown (#8A5216) and --c3's tints a violet (rgba(91,75,196)). The
     brown RENDERED, on .df-node across /integrations and /solutions and on the
     .al-* rows on /platform and /agencies. Both now track the page's neutral
     ramp, which is what :root already does with the same two tokens. */
  --c2:#605d5d;--c2-soft:rgba(32,30,29,.05);--c2-border:rgba(32,30,29,.16);
  --c3:#605d5d;--c3-soft:rgba(32,30,29,.05);--c3-border:rgba(32,30,29,.16);
  --c4:#1e40af;--c4-soft:rgba(30, 64, 175,.06);--c4-border:rgba(30, 64, 175,.20);
  --c5:#201e1d;--c5-soft:rgba(32, 30, 29,.035);--c5-border:rgba(32, 30, 29,.13);
  --brand:#1d4ed8;--brand-text:#1e40af;--brand-vivid:#1d4ed8;
  --brand-soft:rgba(29, 78, 216,.07);--brand-border:rgba(29, 78, 216,.24);
  --btn-gradient:#201e1d;
  background:var(--bg);color:var(--text);border-radius:0;overflow:hidden;
  /* The panel is white on warm paper, so the edge alone is nearly invisible and
     the shadow is what makes it a physical object. The hairline ring stays to
     hold the boundary where the shadow is weakest (the top edge), and the inset
     highlight from v7 is dropped — a white line on a white fill draws nothing. */
  border:1px solid var(--border);
  box-shadow:
             0 4px 10px rgba(32, 30, 29,.04),
             0 28px 60px -18px rgba(32, 30, 29,.16);
  transition:border-color var(--hov-out) var(--hov-ease),box-shadow var(--hov-out) var(--hov-ease);
}

/* ==================== DASHBOARD CHROME (shared across pages) ==================== */
.dash-chrome{border:1px solid var(--border);border-radius:0;overflow:hidden;box-shadow:none;position:relative}
.dash-top{background:var(--bg-alt);padding:10px 16px;display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--border)}
.dash-top .d-dots{display:flex;gap:6px}
.dash-top .d-dots span{width:10px;height:10px;border-radius:0}
.dash-top .d-dots span:first-child{background:#1d4ed8}.dash-top .d-dots span:nth-child(2){background:#7d7979}.dash-top .d-dots span:last-child{background:#444141}
.dash-top .d-url{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:0;padding:5px 12px;font-family:var(--f-mono);font-size:11px;color:var(--text-muted);display:flex;align-items:center;gap:6px}
.dash-top .d-url .d-lock{color:var(--text-faint);font-size:10px}
.dash-vp{background:var(--bg);position:relative}
.dash-lay{display:grid;grid-template-columns:180px 1fr;min-height:480px}
@media(max-width:768px){.dash-lay{grid-template-columns:1fr}.dash-sb{display:none!important}}
.dash-sb{background:var(--bg-alt);border-right:1px solid var(--border);padding:16px 0;display:flex;flex-direction:column}
.dash-sb-logo{padding:4px 16px 14px;font-family:var(--f-mono);font-weight:700;font-size:13px;color:var(--text)}
.dash-sb-item{padding:7px 16px;font-family:var(--f-mono);font-size:11px;color:var(--text-muted);display:flex;align-items:center;gap:8px;cursor:default;transition:all .15s}
.dash-sb-item:hover{background:var(--bg-card);color:var(--text)}
.dash-sb-item.act{background:var(--accent-soft);color:var(--accent);border-right:2px solid var(--accent)}
.dash-sb-icon{width:14px;text-align:center;font-size:12px}
.dash-mn{padding:20px;overflow:hidden;display:flex;flex-direction:column;gap:14px}
.dash-mn-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
.dash-mn-title{font-family:var(--f-mono);font-size:13px;font-weight:700;color:var(--text)}
.dash-mn-badge{font-family:var(--f-mono);font-size:9px;padding:2px 8px;background:var(--bg-alt);color:var(--text);border:1px solid var(--n-300);border-radius:0;letter-spacing:.5px}
.dp-sev{font-family:var(--f-mono);font-size:8px;font-weight:700;letter-spacing:.5px;padding:2px 5px;border-radius:0;flex-shrink:0;margin-top:1px}
/* v8: severity chips are 8px bold caps — the smallest text on the site, and
   the one place a light-on-dark tint is least survivable. #5b8cff was 2.8:1
   and #fbbf24 was 1.7:1 on paper. Darkened to the signal rungs.
   v10.1: the LABELS were corrected to the signal rungs back then and the fills
   were not, so this block still carried an amber, a blue and a teal underneath
   ink and red text. Same four-form ladder as .pw-sev above. At 8px the fill and
   the border are doing more work than the label colour, so getting these right
   matters more here than anywhere else in the panel. */
.dp-sev.cr{background:var(--brand);color:#fff;border:1px solid var(--brand)}
.dp-sev.wr{background:var(--acc-100);color:var(--brand-text);border:1px solid var(--acc-300)}
.dp-sev.in{background:var(--bg-alt);color:var(--text-body);border:1px solid var(--n-300)}
.dp-sev.ok{background:transparent;color:var(--text-faint);border:1px solid transparent}

/* ==================== CTA ==================== */
.cta{background:var(--bg-alt);padding:var(--band-y) 0;text-align:center;border-top:1px solid var(--hairline);border-bottom:none;transition:background .35s ease}
.cta h2{margin-bottom:1rem;text-align:center;font-size:clamp(2rem,4.6vw,2.75rem);letter-spacing:-.045em;line-height:1.08}
.cta h3{text-align:center}
.cta .desc,.cta .ssub{margin:0 auto 2.25rem;text-align:center;max-width:56ch}
.cta .btn-row,.cta .hero-actions{justify-content:center}

/* CTA with real-SMB background footage */
.cta--video{position:relative;overflow:hidden;isolation:isolate;background:#201e1d;border-top:none;color:#f8f4f4}
.cta--video .cta-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;
  opacity:0;transform:scale(1.04);transition:opacity 1.1s ease,transform 9s ease-out;object-position:center}
.cta--video .cta-video.is-on{opacity:.55;transform:scale(1)}
.cta--video .cta-scrim{position:absolute;inset:0;z-index:1;pointer-events:none;
  background:radial-gradient(120% 120% at 50% 50%,rgba(32, 30, 29,.55) 0%,rgba(32, 30, 29,.78) 60%,rgba(32, 30, 29,.92) 100%)}
.cta--video .container{position:relative;z-index:2}
.cta--video h2{color:#f3f2f2;text-shadow:0 2px 20px rgba(0,0,0,.55)}
.cta--video h2 .hl{color:var(--brand-text)}
.cta--video .desc{color:#EAEBEE;text-shadow:0 1px 14px rgba(0,0,0,.6)}

/* ===================================================================
   CONSOLIDATED CLOSING BLOCK — "the shift" proof + closing pitch
   in a card floating over the real-SMB footage.

   v6 floated a WHITE card here and hardcoded every text colour inside it
   because "the card stays white in both themes". On v7 that card is the single
   brightest object on the site and it lands at the bottom of every long page —
   a full-width flashbang after eight dark bands. It is now a raised dark card
   on the same ramp, which also lets the footage behind it read instead of being
   punched out by a white rectangle. All the hardcoded values below are tokens
   again, so this block follows the ramp if it is ever retuned.
   =================================================================== */
.smb-cta{position:relative;overflow:hidden;isolation:isolate;padding:6rem 0;background:#201e1d;border-top:none}
.smb-cta-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;
  opacity:0;transform:scale(1.05);transition:opacity 1.1s ease,transform 14s ease-out;object-position:center}
.smb-cta-video.is-on{opacity:.78;transform:scale(1)}
.smb-cta-scrim{position:absolute;inset:0;z-index:1;pointer-events:none;
  background:radial-gradient(120% 120% at 50% 45%,rgba(32, 30, 29,.35) 0%,rgba(32, 30, 29,.6) 65%,rgba(32, 30, 29,.82) 100%)}
.smb-cta .container{position:relative;z-index:2}

/* Raised card. The 1px paper ring is what separates it from the footage —
   a shadow cannot, because the scrim behind it is already near-black. */
/* ── LIGHT ISLAND IN A DARK BAND ──────────────────────────────────────────
   The dark-band token scope near the bottom of this file (.vband, .ai-shift,
   .cta--video, .smb-cta) is written on the stated assumption that "nothing in
   them is a UI surface" — type laid straight onto footage, so it needs the
   inverted set. This card is the one place that assumption does not hold: it
   is opaque paper sitting ON the footage, and it was inheriting the inverted
   tokens onto it. Measured live: h3 painted #f8f4f4 on a #FFFFFF card, .desc
   #bab6b6, and the divider rgba(247,246,242,.16) — near-white on white, which
   is why the block read as a mostly-empty card with only the coral surviving.
   Restoring the :root set here scopes the fix to the surface that is actually
   light, and leaves the band's dark treatment intact for anything outside the
   card. Audited sitewide: this is the ONLY light surface inside those four
   bands, so it is the only reset needed. Keep these values in sync with :root. */
.smb-cta-card{max-width:1000px;margin:0 auto;background:var(--bg-card);border-radius:0;text-align:center;
  padding:clamp(2.5rem,5vw,4.25rem) clamp(1.5rem,4vw,4rem);
  box-shadow:inset 0 0 0 1px var(--border);
  --ink:#201e1d;
  --text:#201e1d;--text-body:#605d5d;--text-muted:#676363;--text-faint:#696565;
  --hairline:rgba(32, 30, 29,.10);
  --border:rgba(32, 30, 29,.14);--border-hover:rgba(32, 30, 29,.26);
  --brand:#1d4ed8;--brand-text:#1e40af;
  --accent:#1e40af;--accent-hover:#1e3a8a;
  --success:#0e6b38;--warning:#8f4408;--danger:#b91c1c}
.smb-cta-card .eyebrow{color:var(--text-muted);justify-content:center}
.smb-cta-card h2{color:var(--text);max-width:24ch;margin:0 auto 1.25rem;text-align:center}
/* v9: was coral. .hl is a width accent now; these two rules outranked it. */
.smb-cta-card h2 .hl{color:inherit;font-weight:var(--wt-accent)}
.smb-cta-card .desc{color:var(--text-body);max-width:62ch;margin:0 auto;text-align:center}

/* Stats grid — hairline gaps let the card show through as the divider. */
.smb-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;margin:3rem 0 0;
  background:var(--border);border:1px solid var(--border);border-radius:0;overflow:hidden}
.smb-stat{background:var(--bg-raised);padding:2.1rem 1.1rem;display:flex;flex-direction:column;align-items:center;gap:.55rem;
  transition:background var(--hov-out) var(--hov-ease)}
.smb-stat:hover{background:var(--brand-soft);transition:background var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.smb-stat .stat-num{font-family:var(--f-sans);font-size:clamp(2rem,4vw,3rem);
  letter-spacing:-.045em;line-height:1;color:var(--text);font-weight:800;font-variant-numeric:tabular-nums}
.smb-stat .stat-label{font-family:var(--f-sans);font-size:.88rem;font-weight:400;line-height:1.45;
  color:var(--text-body);max-width:24ch}
.smb-stat .stat-src{font-family:var(--f-label);font-size:11px;text-transform:uppercase;letter-spacing:var(--tr-caps);color:var(--text-faint)}

/* Closing pitch, separated inside the same card */
.smb-cta-close{margin-top:3rem;padding-top:2.75rem;border-top:1px solid var(--border)}
/* …unless it is the only thing in the card. The separator exists to divide this
   pitch from the stat grid that used to sit above it; that grid was removed from
   index.php and .smb-stats now has no consumer, so the rule divided nothing and
   the 3rem + 2.75rem stack became 161px of blank card above the first line of
   text. Keyed off :first-child rather than deleted, so it comes back by itself
   if anything is ever put above it again. */
.smb-cta-close:first-child{margin-top:0;padding-top:0;border-top:none}
.smb-cta-close h3{font-family:var(--f-sans);font-size:clamp(1.5rem,3vw,2.1rem);
  letter-spacing:-.04em;line-height:1.12;color:var(--text);margin-bottom:1rem;font-weight:800;text-align:center}
.smb-cta-close h3 .hl{color:inherit;font-weight:var(--wt-accent)}
.smb-cta-close .desc{color:var(--text-body);max-width:52ch;margin:0 auto 2rem;text-align:center}
/* Tagline used to live INSIDE .smb-cta-secondary, so the accessible link name
   read "Built to bring you business. See pricing →". It is a statement, not a
   destination — it sits outside the anchor and keeps the muted treatment. */
.smb-cta-tagline{margin:1.25rem 0 .25rem;text-align:center;font-family:var(--f-sans);
  font-size:.90625rem;font-weight:600;color:var(--text-muted)}
.smb-cta-secondary{display:inline-flex;align-items:center;gap:.5rem;margin-top:0;width:100%;
  justify-content:center;font-family:var(--f-sans);font-size:.90625rem;font-weight:600;color:var(--text-muted);
  text-decoration:none;transition:color var(--hov-out) var(--hov-ease)}
.smb-cta-secondary span{color:var(--brand);font-weight:600;transition:transform .25s ease}
.smb-cta-secondary:hover{color:var(--text);transition:color var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.smb-cta-secondary:hover span{transform:translateX(4px)}

@media(max-width:900px){
  .smb-stats{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:560px){
  .smb-cta{padding:3.5rem 0}
  .smb-cta-card{border-radius:0}
  .smb-stats{grid-template-columns:1fr 1fr}
  .smb-stat{padding:1.6rem .7rem}
}

/* ==================== FOOTER ==================== */
footer{background:var(--bg);padding:4rem 0 1.75rem;border-top:1px solid var(--hairline);transition:background .35s ease}
.f-grid{display:grid;grid-template-columns:1.7fr repeat(5,1fr);gap:2rem 1.75rem;margin-bottom:3rem}
.f-brandcol{max-width:300px}
/* The footer logotype. Same markup as .nav-logo (includes/logo.php), so the two
   ends of the page cannot drift apart again — but scoped separately because the
   nav's entrance animation is keyed off .nav-logo and must not follow it here.
   `footer a` two sections down paints every footer anchor --text-body at 13px;
   the wordmark is fill:currentColor, so it would inherit that grey and shrink.
   `footer a.f-brand` outranks it and puts the mark back on ink at nav size. */
footer a.f-brand{display:flex;align-items:center;gap:.44rem;width:max-content;
  margin-bottom:.75rem;color:var(--ink)}
footer a.f-brand:hover{color:var(--ink)}
.f-brand .wm{height:19px;width:auto;display:block;overflow:visible}
.f-brand .wm-lane{flex:none;display:block}
.f-tag{font-size:var(--fs-small);color:var(--text-muted);line-height:1.6;margin-bottom:1.25rem;max-width:34ch}
.f-cta{display:inline-flex;align-items:center;gap:5px;font-family:var(--f-sans);font-size:.875rem;font-weight:600;letter-spacing:-.005em;color:var(--accent);transition:color .2s ease}
.f-cta::after{content:'→';transition:transform .2s ease}
.f-cta:hover{text-decoration:underline;text-underline-offset:3px}
.f-cta:hover::after{transform:translateX(3px)}
.f-ct{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint);margin-bottom:.875rem}
.f-ct-sub{margin-top:1.5rem}
.f-link{display:block;font-size:var(--fs-small);color:var(--text-muted);text-decoration:none;margin-bottom:.5rem;line-height:1.45;transition:color var(--hov-out) var(--hov-ease)}
.f-link:hover{color:var(--text);transition:color var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.f-bot{display:flex;justify-content:space-between;align-items:center;gap:1rem;flex-wrap:wrap;padding-top:1.5rem;border-top:1px solid var(--hairline);font-family:var(--f-sans);font-size:.8125rem;color:var(--text-faint)}
.f-legal{display:flex;gap:1.25rem}
.f-legal .f-link{display:inline;margin-bottom:0;font-size:.8125rem;font-family:var(--f-sans)}
/* "Cookie settings" is a <button> — it opens a control rather than navigating —
   so it needs the browser's button chrome stripped to sit level with the anchors
   beside it. Inherit, not re-declare, so it tracks .f-link if that ever changes. */
.f-link-btn{background:none;border:0;padding:0;cursor:pointer;font:inherit;color:inherit;line-height:inherit}

/* ==================== LAYOUT ==================== */
.split{display:grid;grid-template-columns:1fr 1fr;gap:4rem;align-items:center}
.split>*{min-width:0}
.split-text h3{font-size:1.25rem;font-weight:600;margin-bottom:.625rem;color:var(--ink);letter-spacing:-.025em;line-height:1.25}
.split-text p{font-size:var(--fs-body);color:var(--text-body);line-height:var(--lh-body)}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem}
.grid-3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:1.5rem}
.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:1.5rem}

/* ==================== COMPARISON TABLE ==================== */
.comp-table{width:100%;border-collapse:collapse;margin-bottom:2rem}
.comp-table th{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint);padding:.875rem 1rem;text-align:left;border-bottom:1px solid var(--border)}
.comp-table td{padding:.9375rem 1rem;font-size:var(--fs-mid);color:var(--text-body);border-bottom:1px solid var(--hairline);vertical-align:top}
.comp-table tr:last-child td{border-bottom:none}
.comp-table .highlight-col{background:var(--accent-soft);color:var(--text);font-weight:600}

/* ==================== PSEO PAGES ==================== */
.breadcrumbs{font-family:var(--f-label);font-size:var(--fs-mono-xs);color:var(--text-faint);margin-bottom:1.5rem;letter-spacing:var(--tr-caps)}
.breadcrumbs a{color:var(--accent);text-decoration:none}.breadcrumbs a:hover{text-decoration:underline}
/* Reassurance strip under a price or a deliverable list. Lived inline in
   index.php until /answer-engine-optimization needed it too; two copies of a
   component in two files is how they drift. */
.check-strip{display:flex;align-items:center;gap:1.25rem;flex-wrap:wrap;margin-top:1.75rem}
.check-strip span{display:flex;align-items:center;gap:6px;font-size:.8125rem;color:var(--text-muted)}
.check-strip .ck{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex:none;border-radius:0;background:var(--accent-soft);color:var(--success);font-size:9px;font-weight:600}
.feature-list{list-style:none;padding:0;margin:0}
.feature-list li{font-size:var(--fs-mid);color:var(--text-body);padding:.5rem 0;display:flex;gap:.75rem;align-items:flex-start;line-height:var(--lh-body)}
.feature-list li::before{content:'✓';flex:none;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;margin-top:.28em;border-radius:0;background:var(--accent-soft);color:var(--success);font-size:.625rem;font-weight:600}

/* ==================== RESPONSIVE ==================== */
@media(max-width:1200px){
  /* Four plan cards get cramped below ~1200px — drop to a 2x2 before the 1-col break. */
  .grid-4:has(.plan-card),.grid-4:has(.price-card){grid-template-columns:repeat(2,1fr)}
}
@media(max-width:1024px){.stat-grid{grid-template-columns:repeat(3,1fr)}.rg{grid-template-columns:repeat(2,1fr)}.price-grid{grid-template-columns:repeat(2,1fr)}
  .f-grid{grid-template-columns:repeat(3,1fr);gap:2.25rem 1.75rem}.f-brandcol{grid-column:1/-1;max-width:520px}}
@media(max-width:768px){
  .hero-grid,.split,.card-grid,.grid-2,.grid-3,.grid-4{grid-template-columns:1fr}
  .stat-grid{grid-template-columns:repeat(2,1fr)}.rg{grid-template-columns:1fr}
  .f-grid{grid-template-columns:1fr 1fr;gap:2rem 1.25rem}.container{padding:0 1.5rem}
  .bv-main [style*="grid-template-columns: repeat(4"]{grid-template-columns:repeat(2,1fr)!important}
  .bv-main [style*="grid-template-columns:repeat(4"]{grid-template-columns:repeat(2,1fr)!important}
  .bv-main [style*="grid-template-columns: 1fr 1fr"]{grid-template-columns:1fr!important}
  .bv-main [style*="grid-template-columns:1fr 1fr"]{grid-template-columns:1fr!important}
  .browser-viewport [style*="grid-template-columns: 1fr 1fr"]{grid-template-columns:1fr!important}
  .browser-viewport [style*="grid-template-columns:1fr 1fr"]{grid-template-columns:1fr!important}
  .svc-body{grid-template-columns:1fr}.comp-table{font-size:.8rem}
  .f-bot{flex-direction:column;gap:.5rem}
  .checkbox-grid,.radio-grid{grid-template-columns:1fr!important}.checkbox-grid.compact{grid-template-columns:repeat(2,1fr)!important}
  .hero{padding:3.5rem 0 3rem}.section,.section-alt{padding:var(--band-y) 0}
  .btn-p,.btn-s{padding:12px 20px}
}

/* ==================== NAV COLLAPSE ====================
   The nav collapses to the hamburger at 1024px, not 768px. Five dropdowns plus
   two CTAs cannot carry comfortable type between those widths — that band is
   where "Book a demo" wrapped and spilled past the edge. --nav-h-c keeps the
   collapsed bar height and the dropdown panel's top offset from drifting apart,
   which they previously did as two hardcoded 60px values.
   Keep in sync with the matchMedia query in includes/header.php. */
@media(max-width:1024px){
  .nav-inner{--nav-h-c:64px;height:var(--nav-h-c);padding:0 1.25rem;gap:1rem}
  .nav-toggle{display:inline-flex}
  /* The panel used to cap itself at 60vh and scroll internally. Two problems.
     First, `overflow-y:auto` here carried no !important while the desktop
     .mega-menu rule LATER in this file sets `overflow:visible` at equal
     specificity — so the auto never applied, and an open Services panel
     (1239px of content in a 506px box) painted straight over Pricing, Platform
     and About below it. Second, even working it would have been a scroll region
     nested inside the nav sheet, which is awkward on a phone.
     So the cap moves to .nav-links below: ONE scrolling sheet, panel flows at
     its natural height inside it. */
  .mega-menu,.mega-menu.mm-sm{position:static!important;box-shadow:none!important;border:none!important;padding:0!important;background:transparent!important;border-radius:0;display:none!important;max-height:none!important;overflow:visible!important;transform:none!important;opacity:1!important;pointer-events:auto!important;min-width:0!important;width:100%!important}
  .mega-menu::before{display:none!important}
  .nav-dd.open>.mega-menu{display:block!important}
  .mm-inner{flex-direction:column!important;padding:0!important;gap:0!important;padding-left:1rem!important}
  /* Both overrides undo the desktop 300px column measure, which means two
     different things once mm-inner flips to flex-direction:column. As a width it
     would pin each section to 300px inside a sheet only ~264px wide at 320px and
     throw the nav into horizontal overflow; as a flex-basis it becomes a HEIGHT,
     which padded the three-link Products section out to 300px and left ~90px of
     dead space above the next label. Stacked sections size to their content and
     stretch to the sheet. */
  .mm-col{flex:0 0 auto!important;min-width:auto!important;width:auto!important;padding:0.75rem 0 0.5rem!important;border-right:none!important;border-bottom:1px solid var(--border)!important}.mm-col:last-child{border-bottom:none!important}
  /* Same bump as the desktop panel. 9px labels and 12.8px links were the
     smallest type anywhere on the site, on the one surface you navigate with a
     thumb — and 6px of vertical padding gave a ~25px row against the 44px
     minimum touch target. 10px padding on a 15px line clears it. */
  .mm-label{margin-bottom:0.5rem!important;font-size:0.6875rem!important;white-space:normal!important;border-bottom:none!important;padding-bottom:0!important}
  /* padding-left leaves room for the glyph added in section 9c. This rule
     carries !important, so without the left value here it would win over the
     desktop padding and the icon would sit on top of the label. */
  .mm-link{white-space:normal!important;padding:10px 8px 10px 30px!important;font-size:0.9375rem!important;text-align:left!important;margin:0!important;flex-wrap:wrap!important}
  /* Show nav item descriptions on mobile (full content, not just labels) */
  .mm-desc{display:block!important;flex:1 1 100%!important;margin-left:0!important;font-size:0.8125rem!important;opacity:.85!important;line-height:1.4!important}
  .mm-more{margin-top:0.375rem!important;padding-top:0.625rem!important}
  .nav-dd{width:100%}
  .nav-dd>a{display:flex;align-items:center;justify-content:space-between;width:100%;text-align:left}
  .nav-dd>a::after{content:'＋';font-size:12px;color:var(--text-muted);margin-left:auto;flex-shrink:0}
  .nav-dd.open>a::after{content:'－'}
  /* The open sheet is the single scroll region for the whole mobile nav — see
     the .mega-menu note above. Without a cap it is absolutely positioned with
     no height limit, so an expanded accordion simply ran off the bottom of the
     screen with nothing to scroll. dvh first so the iOS URL bar collapsing does
     not leave the last item unreachable; vh is the fallback for browsers
     without dvh. overscroll-behavior stops a flick at the end of the menu from
     scrolling the page underneath it. */
  .nav-links{display:none;position:absolute;top:var(--nav-h-c,64px);left:0;right:0;background:var(--bg);flex-direction:column;align-items:stretch;padding:1rem 1.25rem;border-bottom:1px solid var(--hairline);gap:0;backdrop-filter:blur(20px);text-align:left;z-index:999;
    max-height:calc(100vh - var(--nav-h-c,64px) - 34px);
    max-height:calc(100dvh - var(--nav-h-c,64px) - 34px);
    overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
  .nav-links.open{display:flex}
  /* The drawer turns nav links into flush, square, borderless rows. Applied to
     the CTA that stripped its fill (background:none!important), its radius and
     its padding, left-aligned the label and — via :last-of-type — removed its
     bottom border. A transparent 3-sided box with muted text reads as an empty
     input, not a button. Exclude the CTA from the row treatment. */
  .nav-links>a:not(.nav-cta),.nav-dd>a{display:block;width:100%;text-align:left;padding:13px 0!important;border-bottom:1px solid var(--hairline);border-radius:0;background:none!important}
  .nav-links>a:not(.nav-cta):last-of-type{border-bottom:none}
  .nav-links .nav-trial{display:block;width:100%;text-align:left;border:none!important;margin:0!important;padding:13px 0!important;border-bottom:1px solid var(--hairline)!important;border-radius:0}
  /* Restore the full pill: ink fill, page-colour label, centred, with real
     padding. Stated explicitly so it survives the drawer's !important rows. */
  .nav-links>a.nav-cta{display:flex;justify-content:center;width:100%;text-align:center;
    margin-top:1rem;padding:13px 18px;border:1px solid var(--ink);border-radius:0;
    background:var(--ink);color:var(--bg);font-weight:600}
}

/* ==================== MEGA MENUS ==================== */
.nav-dd{position:relative}.nav-dd>a{cursor:pointer}
/* width:max-content is load-bearing. The panel is absolutely positioned inside
   a ~100px-wide .nav-dd, so with only min-width set, shrink-to-fit pinned it at
   exactly 480px; the flex columns then shrank below their content and .mm-inner's
   overflow:hidden sliced the longest link ("Email Marketing" lost 37px). Sizing
   to content and capping with max-width removes the squeeze at the source.
   --mm-shift is set by the nav script to keep a right-edge panel on screen. */
.mega-menu{display:none;position:absolute;top:calc(100% + 8px);left:50%;
  transform:translateX(calc(-50% + var(--mm-shift,0px))) translateY(4px);
  background:var(--bg-card);border:1px solid var(--border);border-radius:0;
  box-shadow:none;z-index:1001;
/* The cap was min(88vw,700px), set when the widest menu was three columns.
   Services is four now, and 700px divided four ways is 175px a column — every
   description wrapped to three lines and the panel read as a wall. 1040px gives
   four columns ~245px each, which is where a two-line description fits on one
   or two lines instead of three. The vw term rises with it so the panel can
   still use the width available on a 1100px laptop; --mm-shift keeps the right
   edge on screen either way. Three-column menus are unaffected — they size to
   max-content and were never hitting the cap. */
  width:max-content;min-width:480px;max-width:min(94vw,1040px);
  padding:0;overflow:visible;opacity:0;pointer-events:none;transition:opacity .2s ease,transform .2s ease}
.mega-menu::before{content:'';position:absolute;top:-12px;left:0;right:0;height:14px;background:transparent}
.mega-menu.mm-sm{min-width:320px}
/* :focus-within is not decoration — without it the panel opened on hover ONLY,
   so on desktop a keyboard user could tab to "Services" and never reach the
   links inside it. The mobile branch already had a click handler; this is the
   equivalent for the pointer-free desktop path. */
@media(min-width:1025px){
  .nav-dd:hover .mega-menu,
  .nav-dd:focus-within .mega-menu{display:block;opacity:1;pointer-events:auto;transform:translateX(calc(-50% + var(--mm-shift,0px))) translateY(0)}
}
.mm-inner{display:flex;gap:0;padding:0;overflow:hidden;border-radius:0}
/* width:300px is what sets the column measure, and it has to be a real width
   rather than a flex-basis — the panel is width:max-content, and max-content
   sizing reads the columns' content, not their basis, so `flex:1 1 300px` alone
   changed nothing. Before this, basis was 0 and column width was an accident of
   copy: whatever the single longest string in the panel happened to be became
   the measure for every column in it. The Products panel was 302px a column only
   because an over-long section label was propping it open, so tightening that
   label collapsed it to 260 and wrapped "Waterfall sequences that send
   themselves" onto two lines — fixing the label had made the panel worse, which
   is the tell that the width was never being asked for on purpose.
   300px is 248px of text after padding: one line for every description we ship,
   with room for a slightly longer one later. min-width:0 keeps the columns
   shrinkable, which is what the four-column Services panel needs when it hits
   max-width — 1040px over four columns is 260px each, and on a 1025px laptop
   94vw over four is 240px. It shrinks; it never clips against mm-inner's
   overflow:hidden. max-width caps a column that would otherwise stretch. */
.mm-col{flex:1 1 300px;width:300px;min-width:0;max-width:340px;padding:1.5rem 1.625rem;border-right:1px solid var(--border)}.mm-col:last-child{border-right:none}
/* white-space:nowrap is the register guard. Every column label sits above a
   hairline, and those hairlines read as one continuous rule across the panel —
   but only while every label is the same one line tall. Two labels were long
   enough to wrap at their column width (Products' swim-lanes label, Platform's
   "Observability Platform"), which dropped that column's hairline 15-23px below
   its neighbours and staggered the link lists underneath. Because the panel is
   width:max-content, nowrap does not clip: the column grows to fit the label
   and the panel grows with it. Keep labels to ~2 words so the growth is small.
   Mobile stacks the columns and overrides this back to normal. */
.mm-label{font-family:var(--f-label);font-size:.75rem;font-weight:600;letter-spacing:1.6px;text-transform:uppercase;color:var(--text-faint);white-space:nowrap;margin-bottom:0.875rem;padding-bottom:0.625rem;border-bottom:1px solid var(--border)}
/* Label on its own line, description stacked beneath and free to wrap. The old
   layout pushed the description to the right of the label on one nowrap line,
   which is what overflowed. Stacking also matches the mobile treatment. */
/* 13px/11px was the density of a spec sheet, not a menu. 15px label over 13px
   description is the smallest pair that stays comfortable at arm's length, and
   the taller padding takes a bare link (no description) to a ~42px row, which is
   a real pointer target rather than a 27px sliver. Colour also moves up a rung:
   --text-muted on the label was 4.9:1, fine for a caption and thin for the main
   thing you are asked to click. */
.mm-link{display:block;padding:10px 10px;margin:0 -10px;font-family:var(--f-sans);font-size:0.9375rem;font-weight:400;line-height:1.45;color:var(--text-body);white-space:normal;border-radius:0;
  transition:color var(--hov-out) var(--hov-ease),background-color var(--hov-out) var(--hov-ease)}
.mm-link:hover{color:var(--text);background-color:var(--bg-alt);
  transition:color var(--hov-in) var(--hov-ease) var(--hov-in-delay),background-color var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
/* Keyboard users get the same affordance as the mouse, plus a visible ring.
   Pairs with the :focus-within rule above that opens the panel in the first place. */
.mm-link:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;color:var(--text);background-color:var(--bg-alt)}
.mm-desc{display:block;font-family:var(--f-sans);font-size:0.8125rem;font-weight:400;line-height:1.45;color:var(--text-faint);margin-left:0;margin-top:3px;opacity:.85;transition:opacity .15s}.mm-link:hover .mm-desc,.mm-link:focus-visible .mm-desc{opacity:1}
.mm-badge{font-family:var(--f-label);font-size:.6875rem;font-weight:600;letter-spacing:var(--tr-caps);padding:2px 6px;border-radius:0;margin-left:auto}
/* Mega-menu badges. LIVE takes the accent because it is the one that should
   pull the eye down the list; COMING SOON goes quiet rather than amber. */
.mm-badge.live{background:var(--acc-100);color:var(--brand-text)}.mm-badge.coming-soon{background:var(--n-100);color:var(--text-faint)}
.mm-dot{font-size:0.5rem;line-height:1}
.mm-more{color:var(--accent)!important;font-weight:600;margin-top:0.625rem;border-top:1px solid var(--border);padding-top:0.875rem}

/* ==================== MULTI-STEP FORM ==================== */
.form-step{display:none;overflow:hidden}.form-step.active{display:block;animation:fadeStep .35s ease}
@keyframes fadeStep{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.form-nav{display:flex;gap:1rem;margin-top:2rem;align-items:center}
.step-label{font-family:var(--f-label);font-size:var(--fs-mono-xs);color:var(--text-muted);letter-spacing:var(--tr-caps)}
.checkbox-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:0.75rem;min-width:0}
.checkbox-grid.compact{display:grid;grid-template-columns:repeat(3,1fr);gap:0.5rem;min-width:0}
.cb-card{display:flex;align-items:flex-start;gap:0.75rem;padding:1rem;border:1px solid var(--border);border-radius:0;cursor:pointer;transition:all .25s;user-select:none;background:var(--bg-card);min-width:0;overflow:hidden}
.cb-card:hover{border-color:var(--border-hover);transform:translateY(-1px);box-shadow:var(--shadow-sm)}
.cb-card input[type="checkbox"]{margin-top:3px;accent-color:var(--accent);flex-shrink:0}
.cb-card input[type="checkbox"]:checked + .cb-content .cb-title{color:var(--accent)}
.cb-card:has(input:checked){border-color:var(--accent);background:var(--accent-soft)}
.cb-card.compact{padding:0.75rem}
.cb-content{min-width:0}
.cb-title{font-size:var(--fs-small);font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis}
.cb-desc{font-size:0.75rem;color:var(--text-faint);margin-top:0.25rem;line-height:1.4}
.radio-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:0.5rem;margin-bottom:1rem}
.radio-card{display:flex;align-items:center;gap:0.5rem;padding:0.65rem 0.75rem;border:1px solid var(--border);border-radius:0;cursor:pointer;transition:all .25s;font-size:var(--fs-small);user-select:none;background:var(--bg-card);min-width:0}
.radio-card:hover{border-color:var(--border-hover);transform:translateY(-1px)}
.radio-card input[type="radio"]{accent-color:var(--accent);flex-shrink:0}
.radio-card:has(input:checked){border-color:var(--accent);background:var(--accent-soft)}
.radio-card span{white-space:normal;overflow:hidden;text-overflow:ellipsis;line-height:1.3}

/* ==================== INNER LINK GRIDS ==================== */
.inner-links{margin-top:3rem;padding-top:2rem;border-top:1px solid var(--border)}
/* The label above each chip row is not a heading in the reading sense — it is a
   key for the row under it, and it repeats three or four times in a column. At
   1rem sans it competed with real content headings while carrying none of the
   weight. Mono label, faint, tracked: the same treatment .eyebrow gets, which is
   what this actually is. */
.inner-links h3{font-family:var(--f-mono);font-size:.6875rem;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--text-faint);margin-bottom:.875rem}
.inner-links h3+.il-grid{margin-bottom:2rem}
.il-grid{display:flex;flex-wrap:wrap;gap:0.5rem}
.il-grid a{font-size:0.8125rem;padding:6px 14px;border:1px solid var(--border);border-radius:0;color:var(--text-muted);white-space:nowrap;
  transition:color var(--hov-out) var(--hov-ease),border-color var(--hov-out) var(--hov-ease),background-color var(--hov-out) var(--hov-ease)}
.il-grid a:hover{border-color:var(--accent);color:var(--accent);background-color:var(--accent-soft);
  transition:color var(--hov-in) var(--hov-ease) var(--hov-in-delay),border-color var(--hov-in) var(--hov-ease) var(--hov-in-delay),background-color var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
/* A coverage name with no page behind it. Same chip metrics as .il-grid a so the
   rows line up, but no border and no hover: it must not read as a dead link.
   Used on /locations for the wider UK list — see includes/uk-data.php. */
.il-name{font-size:0.8125rem;padding:6px 14px;color:var(--text-faint);white-space:nowrap;cursor:default}

/* ==================== LOCATIONS: COUNTRY TOGGLE ====================
   Same segmented control as .pricing-toggle, kept as its own class rather than
   reused: the pricing one is centred with `margin:1.5rem auto 0` for a centred
   band, and /locations is a left-aligned page, so sharing it would have meant
   overriding the margin at every call site. */
.geo-toggle{display:inline-flex;border:1px solid var(--border);border-radius:0;
  padding:4px;background:var(--bg-card);margin-top:2rem;gap:2px}
.geo-btn{font-family:var(--f-sans);font-size:var(--fs-mid);font-weight:600;letter-spacing:-.005em;
  color:var(--text-muted);background:none;border:none;cursor:pointer;padding:9px 22px;
  border-radius:0;transition:color var(--dur-2,220ms) ease,background-color var(--dur-2,220ms) ease}
.geo-btn:hover{color:var(--text)}
.geo-btn.active{background:var(--ink);color:var(--bg)}
.geo-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.geo-pane{display:none}
.geo-pane.active{display:block}
@media(max-width:420px){.geo-toggle{width:100%}.geo-btn{flex:1;padding:9px 12px}}

/* ==================== LAKE DEMO (Homepage Chat + Sources) ==================== */
.lake-demo{max-width:740px;margin:0 auto;position:relative}
.lake-sources{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin-bottom:1rem;padding:0 0.5rem}
.lake-node{display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border:1px solid var(--border);border-radius:0;font-family:var(--f-mono);font-size:11px;letter-spacing:0.5px;color:var(--text-faint);background:var(--bg-card);transition:all .4s ease;opacity:0.5}
.lake-node-dot{width:6px;height:6px;border-radius:0;background:var(--text-faint);transition:all .4s ease;flex-shrink:0}
.lake-node-name{white-space:nowrap}
.lake-node.pulling{opacity:1;border-color:var(--accent);color:var(--accent);background:var(--accent-soft);transform:scale(1.05)}
.lake-node.pulling .lake-node-dot{background:var(--accent);box-shadow:none;animation:node-pulse 0.8s ease infinite}
.lake-node.done{opacity:0.7;border-color:var(--accent);color:var(--accent)}
.lake-node.done .lake-node-dot{background:var(--accent)}
@keyframes node-pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:0.5;transform:scale(1.4)}}

.lake-status{display:flex;align-items:center;gap:8px;justify-content:center;margin-bottom:0.75rem;font-family:var(--f-mono);font-size:11px;letter-spacing:1px;color:var(--text-faint);transition:color .3s}
.lake-status-dot{width:6px;height:6px;border-radius:0;background:var(--text-faint);transition:all .3s}
.lake-status.active{color:var(--accent)}
.lake-status.active .lake-status-dot{background:var(--accent);box-shadow:none;animation:node-pulse 1s ease infinite}

.lake-demo .chat-demo{border:1px solid var(--accent);border-radius:0;overflow:hidden;box-shadow:none}
.lake-demo .chat-body{min-height:320px;position:relative;overflow:hidden;padding:1.5rem}
.lake-demo .chat-msg{opacity:0;transform:translateY(12px);transition:opacity .4s ease,transform .4s ease;max-width:82%}
.lake-demo .chat-msg.msg-visible{opacity:1;transform:translateY(0)}
.lake-demo .chat-msg.msg-exit{opacity:0;transform:translateY(-10px);transition:opacity .3s ease,transform .3s ease}

.chat-credits{margin-left:auto;font-family:var(--f-label);font-size:11px;letter-spacing:var(--tr-caps);color:var(--accent);opacity:0;transition:opacity .4s}
.chat-credits.show{opacity:1}

@media(max-width:600px){
  .lake-sources{gap:6px;padding:0}
  .lake-node{padding:4px 8px;font-size:10px}
  .lake-demo .chat-body{min-height:260px;padding:1rem}
}

/* ==================== REDUCED MOTION ==================== */
/* JS fallback: if observer never fires, reveal after 0.5s */
@keyframes reveal-fallback{to{opacity:1;transform:none}}
.reveal,.reveal-stagger>*,.reveal-scale,.reveal-left,.reveal-right{animation:reveal-fallback 0s .5s forwards}
.reveal.visible,.reveal-stagger.visible>*,.reveal-scale.visible,.reveal-left.visible,.reveal-right.visible{animation:none}

/* ==================== HERO ====================
   The hero ripple is gone, deliberately. It spawned an 800x800px radial glow at
   the pointer every 120ms of mousemove and expanded each one over a second, so
   several overlapping glows chased the cursor — the "pulsating flashes" in the
   hero. It was also still painting rgba(0,102,255) from the pre-v6 blue palette.
   Do not reintroduce a pointer-triggered glow here; the hero already has the
   self-heal animation carrying the motion. */
.hero,.page-hero{position:relative;overflow:hidden}

/* ==================== INTERACTIVE WIDGET CONTRAST ==================== */
/* Light mode: darken interactive controls so they stand out */
.theme-toggle{border-color:var(--text-muted);color:var(--text)}
.nav-toggle{border-color:var(--text-muted);color:var(--text)}
.btn-s{border-color:var(--text-muted);color:var(--text)}
.fi{border-color:var(--border-hover)}
.cb-card{border-color:var(--border-hover)}
.radio-card{border-color:var(--border-hover)}
.chat-input{border-top-color:var(--border-hover)}
.pw-tab{color:var(--text-body)}
/* Dark mode: lighten interactive controls so they stand out */
.theme-toggle{border-color:var(--text-muted);color:var(--text)}
.nav-toggle{border-color:var(--text-muted);color:var(--text)}
.btn-s{border-color:var(--text-muted);color:var(--text)}
.fi{border-color:var(--border-hover)}
.cb-card{border-color:var(--border-hover)}
.radio-card{border-color:var(--border-hover)}
.chat-input{border-top-color:var(--border-hover)}
.pw-tab{color:var(--text-body)}

/* ==================== CASE STUDIES / RESULTS ==================== */
.results-agg{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--border);border:1px solid var(--border);border-radius:0;overflow:hidden;margin-bottom:3rem}
.results-agg-item{background:var(--bg);padding:28px 20px;text-align:center}
.results-agg-value{font-family:var(--f-mono);font-size:clamp(1.25rem,3vw,2rem);font-weight:700;color:var(--accent);margin-bottom:6px}
.results-agg-label{font-family:var(--f-label);font-size:11px;color:var(--text-muted);letter-spacing:0.08em;text-transform:uppercase}
.results-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:1.5rem}
.results-card{border:1px solid var(--border);border-radius:0;padding:28px;background:var(--bg-card);position:relative;overflow:hidden;transition:border-color .3s ease,box-shadow .3s ease}
.results-card:hover{border-color:#4A4A46;box-shadow:var(--shadow-md)}
.results-card-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:20px}
.results-card-domain{font-family:var(--f-mono);font-size:var(--fs-mono-sm);font-weight:600;color:var(--text)}
.results-card-industry{font-family:var(--f-label);font-size:11px;color:var(--text-muted);margin-top:3px}
.results-card-status{font-family:var(--f-label);font-size:11px;color:var(--accent);border:1px solid var(--accent-soft);padding:2px 8px;border-radius:0;letter-spacing:0.05em;white-space:nowrap}
.results-card-metrics{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:20px}
.results-card-metric-value{font-family:var(--f-mono);font-size:1.2rem;font-weight:700;color:var(--text);margin-bottom:2px}
.results-card-metric-label{font-family:var(--f-label);font-size:11px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.08em}
.results-card-metric-change{font-family:var(--f-mono);font-size:11px;margin-top:2px}
.results-card-metric-change.up{color:var(--accent)}
.results-card-chart{height:60px;position:relative;margin-bottom:12px}
.results-card-chart svg{width:100%;height:100%}
.results-card-chart-line{fill:none;stroke:var(--accent);stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;filter:drop-shadow(0 1px 5px var(--accent-glow))}
.results-card-chart-area{fill:url(#resultsGreenGrad);opacity:0.3}
.results-card-chart-label{display:flex;justify-content:space-between;font-family:var(--f-label);font-size:9px;color:var(--text-faint);margin-top:4px}
.results-card-note{font-family:var(--f-label);font-size:11px;color:var(--text-muted);border-top:1px solid var(--border);padding-top:14px;line-height:1.5}
.results-card-note em{font-style:normal;color:var(--accent);font-weight:600}
.results-cost-strip{display:inline-flex;align-items:center;gap:14px;margin-top:1.5rem;padding:12px 20px;border:1px solid var(--border);border-radius:0;background:var(--bg-alt);font-family:var(--f-label);font-size:var(--fs-mono-sm)}
.results-cost-old{color:var(--text-muted);text-decoration:line-through;text-decoration-color:var(--c2)}
.results-cost-arrow{color:var(--text-faint)}
.results-cost-new{color:var(--accent);font-weight:700}
.results-cost-tag{color:var(--text-muted);font-size:10px;letter-spacing:0.05em}
.results-mini .results-grid{grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:1rem}
.results-mini .results-card{padding:20px}
.results-mini .results-card-metrics{gap:10px;margin-bottom:14px}
.results-mini .results-card-metric-value{font-size:1rem}
.results-mini .results-card-chart{height:48px;margin-bottom:8px}
@media(max-width:768px){.results-agg{grid-template-columns:repeat(2,1fr)}.results-grid{grid-template-columns:1fr}.results-cost-strip{flex-direction:column;gap:6px;align-items:flex-start}}
@media(max-width:480px){.results-agg{grid-template-columns:1fr}}

/* ========================================================================
   THE SHIFT — parallax SMB proof band (includes/ai-shift.php)
   Self-contained dark section; renders identically in light + dark themes.
   ===================================================================== */
.ai-shift{position:relative;overflow:hidden;padding:7rem 0;background:#201e1d;isolation:isolate}
/* Video background (was a 3x3 parallax tile grid — see includes/ai-shift.php).
   Slight over-scale so the gentle parallax translate never exposes an edge. */
.ai-shift-video{position:absolute;top:-6%;left:0;width:100%;height:112%;object-fit:cover;
  z-index:0;opacity:0;transform:scale(1.04);
  transition:opacity 1.2s ease;
  /* Graded so the footage is legible as a place of work but never competes with
     the copy. The veil below does the heavy darkening, so this only takes the
     colour down and holds contrast. */
  filter:grayscale(.3) contrast(1.06) brightness(.82);will-change:transform}
.ai-shift-video.is-on{opacity:.66}
/* Veil was cool slate (#16181D / rgba(22,24,29)) from v6 and read blue against
   the v7 warm ramp — the one band on the site that looked a different colour
   temperature. Now the page's own near-black. */
.ai-shift-veil{position:absolute;inset:0;z-index:1;pointer-events:none;
  background:radial-gradient(ellipse 80% 70% at 50% 45%, rgba(32, 30, 29,.6) 0%, rgba(32, 30, 29,.84) 55%, #201e1d 100%)}
.ai-shift-veil::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg, #201e1d 0%, transparent 18%, transparent 82%, #201e1d 100%)}
.ai-shift-inner{position:relative;z-index:2;text-align:center}
.ai-shift .eyebrow{color:var(--text-muted);justify-content:center}
.ai-shift .eyebrow::before{background:#f3f2f2;box-shadow:0 0 0 3px rgba(251,251,247,.14)}
.ai-shift h2{color:var(--text);max-width:20ch;margin-left:auto;margin-right:auto;text-align:center}
/* v8: was --accent (green). Green is semantic-only now — a green thing on this
   site means "live / verified / healthy", never "this is the brand". The one
   accented phrase in the closing headline is a brand moment, so it takes coral.
   Inside this band the readable coral rung is the vivid one, not --brand-text's
   page value; the dark-band token scope already swaps that. */
.ai-shift .hl{color:var(--brand-text)}
.ai-shift .desc{color:var(--text-body);max-width:60ch;margin:0 auto;text-align:center}

/* Animated accent rule under the headline */
.ai-shift h2::after{content:'';display:block;width:0;height:2px;margin:1.25rem auto 0;
  background:linear-gradient(90deg,transparent,var(--brand-text),transparent);transition:width 1.1s cubic-bezier(.22,1,.36,1) .2s}
.ai-shift-inner.visible h2::after{width:120px}

/* Stat grid. The tiles are translucent over moving footage, so the blur is what
   keeps 11px source lines readable while a frame changes underneath. */
.ai-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;margin-top:3.5rem;
  background:var(--border);border:1px solid var(--border);border-radius:0;overflow:hidden}
.ai-stat{background:rgba(11,11,10,.62);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  padding:2.4rem 1.25rem;display:flex;flex-direction:column;align-items:center;gap:.6rem;
  transition:background var(--hov-out) var(--hov-ease)}
.ai-stat:hover{background:rgba(11,11,10,.78);
  transition:background var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.ai-stat .stat-num{font-family:var(--f-sans);font-size:clamp(2.4rem,5vw,3.5rem);font-weight:800;
  letter-spacing:-.045em;line-height:1;color:var(--text);font-variant-numeric:tabular-nums}
.ai-stat .stat-label{font-family:var(--f-sans);font-size:.92rem;font-weight:400;line-height:1.45;
  color:var(--text-body);max-width:24ch}
/* Was #5D6472 — 2.93:1 on this tile, and it carries the attribution that makes
   the whole band credible. --text-faint clears AA at 11px. */
.ai-stat .stat-src{font-family:var(--f-label);font-size:11px;text-transform:uppercase;
  letter-spacing:var(--tr-caps);color:var(--text-faint)}

/* CTA — v8
   This band closes almost every page on the site and it ended in a text link,
   which is the weakest possible action in the strongest possible position. It
   is the taxonomy pair now: primary audit button, secondary pricing, price said
   once underneath as a plain line rather than smuggled into the link label. */
.ai-shift-cta{display:flex;align-items:center;justify-content:center;gap:.75rem;
  margin-top:2.75rem;flex-wrap:wrap}
.ai-shift-cta .btn-p{background:var(--brand-vivid);border-color:var(--brand-vivid);color:#201e1d}
.ai-shift-cta .btn-p:hover{background:#1d4ed8;border-color:#1d4ed8}
/* Secondary on a dark scrim: the page rule (ink border, ink label) is invisible
   here, so it inverts to a paper hairline and paper label. */
.ai-shift-cta .btn-s{border-color:rgba(247,246,242,.34);color:#f8f4f4}
.ai-shift-cta .btn-s:hover{border-color:#f8f4f4;background:rgba(247,246,242,.06)}
.ai-shift-price{margin-top:1rem;font-family:var(--f-sans);font-size:.875rem;
  color:var(--text-muted);text-align:center}

@media(max-width:900px){
  .ai-stats{grid-template-columns:repeat(2,1fr)}
  .ai-shift-bg{grid-template-columns:repeat(2,1fr)}
  .ai-shift-bg .ai-col:nth-child(3){display:none}
}
@media(max-width:560px){
  .ai-shift{padding:5rem 0}
  .ai-stats{grid-template-columns:1fr 1fr}
  .ai-stat{padding:1.75rem .75rem}
  .ai-shift-bg{grid-template-columns:1fr}
  .ai-shift-bg .ai-col:nth-child(2){display:none}
}

/* ========================================================================
   CASE-STUDY POP — chart draw-on-scroll + hover lift (homepage revamp)
   ===================================================================== */
.results-card-chart-line{transition:stroke-dashoffset 1.5s cubic-bezier(.22,1,.36,1)}
.results-card-chart-area{opacity:0;transition:opacity .9s ease .45s}
.results-card.chart-drawn .results-card-chart-area{opacity:.75}
.results-card{transition:border-color .3s ease,box-shadow .3s ease,transform .3s cubic-bezier(.22,1,.36,1)}
.results-card:hover{transform:translateY(-5px)}
/* count-up aggregate values use tabular figures so width doesn't jump while animating */
.results-agg-value{font-variant-numeric:tabular-nums}

/* The hero "latest result" ticker CSS lived here and is DELETED.
   Its markup went when the receipts band absorbed the claim; the rules
   outlived it and then actively broke the replacement — the new proof rail
   was first written as .hero-proof and silently inherited inline-flex, a
   30px radius and a --bg-alt fill from this corpse, which rendered it as a
   760px pill floating in the hero. The rail is .hero-receipts now (near
   .receipts below) and this block is gone so the name cannot be reused. */

/* ========================================================================
   BEFORE → AFTER case-study cards (replaces SpyFu line-chart cards)
   Reuses .results-card shell (grid, reveal, hover-lift, .chart-drawn hook).
   ===================================================================== */
.ba-card .results-card-header{align-items:center}
.ba-save{font-family:var(--f-mono);font-size:10px;color:var(--accent);border:1px solid var(--accent-soft);
  background:var(--accent-soft);padding:3px 9px;border-radius:0;letter-spacing:.04em;white-space:nowrap}

.ba-flip{display:flex;align-items:stretch;gap:10px;margin-bottom:18px}
.ba-was,.ba-now{flex:1;min-width:0;border:1px solid var(--border);border-radius:0;
  padding:10px 12px;background:var(--bg-alt);display:flex;flex-direction:column;gap:4px}
.ba-now{border-color:var(--accent-soft);background:var(--accent-soft)}
.ba-tag{font-family:var(--f-label);font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-faint)}
.ba-was-val{font-family:var(--f-mono);font-size:12.5px;color:var(--text-muted);text-decoration:line-through;
  text-decoration-color:var(--c2);text-decoration-thickness:1px}
.ba-now-val{font-family:var(--f-mono);font-size:12.5px;color:var(--accent);font-weight:600}
.ba-arrow{align-self:center;color:var(--text-faint);font-family:var(--f-mono);font-size:14px;flex-shrink:0}

.ba-result{margin-bottom:16px}
.ba-headline{font-family:var(--f-label);font-size:2.1rem;font-weight:700;color:var(--accent);line-height:1;
  display:flex;align-items:baseline;gap:5px;font-variant-numeric:tabular-nums}
.ba-headline .ba-per{font-size:.85rem;color:var(--text-muted);font-weight:400}
.ba-result-label{font-family:var(--f-label);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--text-muted);margin-top:5px}
.ba-bar{height:6px;border-radius:0;background:var(--bg-alt);border:1px solid var(--border);overflow:hidden;margin-top:13px}
.ba-bar-fill{display:block;height:100%;width:0;border-radius:0;
  background:linear-gradient(90deg,var(--accent-hover),var(--accent))}
.results-card.chart-drawn .ba-bar-fill{width:var(--bar-w);transition:width 1.5s cubic-bezier(.22,1,.36,1)}

.ba-foot{border-top:1px solid var(--border);padding-top:14px;display:flex;justify-content:space-between;
  align-items:center;gap:8px;flex-wrap:wrap}
.ba-growth{font-family:var(--f-label);font-size:11px;color:var(--accent);line-height:1.4}
.ba-mini{font-family:var(--f-label);font-size:11px;color:var(--text-muted);white-space:nowrap}
.ba-mini b{color:var(--text);font-weight:600}

/* mini variant (inline proof points) */
.results-mini .ba-headline{font-size:1.6rem}
.results-mini .ba-flip{margin-bottom:14px}
.results-mini .ba-was,.results-mini .ba-now{padding:8px 10px}

@media(max-width:380px){
  .ba-flip{flex-direction:column}
  .ba-arrow{transform:rotate(90deg)}
}

/* ========================================================================
   GROWTH cards — organic growth trajectory since starting with askotter
   Reuses .results-card shell + .results-card-chart draw-on-scroll.
   ===================================================================== */
.growth-card .results-card-header{align-items:center;flex-wrap:wrap;gap:6px 10px}
.growth-card .results-card-domain{overflow-wrap:anywhere}
.gc-since{font-family:var(--f-label);font-size:11px;letter-spacing:.04em;color:var(--accent);flex-shrink:0;
  border:1px solid var(--accent-soft);background:var(--accent-soft);padding:3px 9px;border-radius:0;white-space:nowrap}
.growth-card .results-card-chart{height:84px;margin:6px 0 18px}
/* Hero metric on its own full-width row; supporting metrics share a row below */
.gc-metrics{display:grid;grid-template-columns:1fr 1fr;gap:14px 16px;align-items:start;
  border-top:1px solid var(--border);padding-top:16px;margin-bottom:14px}
.gc-metric{min-width:0}
.gc-hero{grid-column:1 / -1;margin-bottom:2px}
.gc-val{font-family:var(--f-mono);font-weight:700;color:var(--text);font-size:1.05rem;line-height:1.05;font-variant-numeric:tabular-nums;overflow-wrap:break-word}
.gc-hero .gc-val{font-size:2rem;color:var(--accent)}
.gc-lbl{font-family:var(--f-label);font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin-top:5px;line-height:1.35}
.gc-note{font-family:var(--f-label);font-size:11px;color:var(--accent);line-height:1.45}

/* mini variant (inline proof) */
.results-mini .growth-card .results-card-chart{height:56px;margin-bottom:12px}
.results-mini .gc-hero .gc-val{font-size:1.5rem}
.results-mini .gc-metrics{gap:10px 12px;padding-top:12px}

/* ═══════════════════════════════════════════════════════════════
   SYSTEM VISUALIZATIONS  (.aoviz-*)
   Reusable, theme-aware diagrams: data intake, human-in-the-loop,
   iterative cycle, AI observability. Pure CSS animation.
   ═══════════════════════════════════════════════════════════════ */
.aoviz{max-width:1000px;margin:0 auto;font-family:var(--f-mono)}
.aoviz *{box-sizing:border-box}

/* shared node */
.aoviz-node{position:relative;display:flex;flex-direction:column;justify-content:center;gap:6px;
  padding:16px 18px;border:1.5px solid var(--border);border-radius:0;background:var(--bg-card);
  min-width:0;transition:border-color .25s ease,transform .25s ease}
.aoviz-node:hover{border-color:var(--border-hover)}
.aoviz-node.is-accent{border-color:var(--accent);background:var(--accent-soft)}
.aoviz-ntag{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-faint)}
.aoviz-ntitle{font-family:var(--f-display,'Space Grotesk',sans-serif);font-size:1.05rem;font-weight:600;color:var(--text);line-height:1.15}
.aoviz-nsub{font-family:var(--f-label);font-size:11px;line-height:1.5;color:var(--text-muted)}
.aoviz-node.is-accent .aoviz-ntag{color:var(--accent)}

/* animated connector wires */
.aoviz-wire{position:relative;flex:1 1 28px;min-width:24px;height:2px;background:var(--border);overflow:hidden;align-self:center}
.aoviz-wire::after{content:"";position:absolute;inset:0 auto 0 0;width:45%;background:linear-gradient(90deg,transparent,var(--accent),transparent);animation:aoviz-flow 2.2s linear infinite}
@keyframes aoviz-flow{0%{transform:translateX(-110%)}100%{transform:translateX(340%)}}
.aoviz-wire-v{position:relative;width:2px;height:26px;margin:0 auto;background:var(--border);overflow:hidden}
.aoviz-wire-v::after{content:"";position:absolute;inset:0 0 auto 0;height:55%;background:linear-gradient(180deg,transparent,var(--accent),transparent);animation:aoviz-flowv 2.2s linear infinite}
@keyframes aoviz-flowv{0%{transform:translateY(-110%)}100%{transform:translateY(260%)}}

/* ── 1 · DATA INTAKE ── */
.aoviz-intake{display:flex;align-items:stretch;gap:0}
.aoviz-intake>.aoviz-node{flex:0 0 auto}
.aoviz-sources{flex:1 1 320px;display:grid;grid-template-columns:repeat(2,1fr);gap:7px;align-content:center}
.aoviz-chip{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--border);border-radius:0;
  background:var(--bg);font-size:11px;color:var(--text-body);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.aoviz-chip .dot{width:6px;height:6px;border-radius:0;background:var(--accent);flex-shrink:0;animation:aoviz-pulse 2.4s ease-in-out infinite}
.aoviz-chip:nth-child(2n) .dot{animation-delay:.4s}
.aoviz-chip:nth-child(3n) .dot{animation-delay:.8s}
.aoviz-chip:nth-child(4n) .dot{animation-delay:1.2s}
@keyframes aoviz-pulse{0%,100%{opacity:.35;transform:scale(1)}50%{opacity:1;transform:scale(1.35)}}
.aoviz-cap{display:flex;flex-wrap:wrap;justify-content:center;gap:8px 22px;margin-top:1.5rem;
  font-size:11px;letter-spacing:.04em;color:var(--text-muted)}
.aoviz-cap span{display:inline-flex;align-items:center;gap:7px}
.aoviz-cap span::before{content:"";width:5px;height:5px;border-radius:0;background:var(--accent)}

/* ── 2 · HUMAN-IN-THE-LOOP ── */
.aoviz-hitl{display:flex;align-items:stretch;gap:0}
.aoviz-hitl>.aoviz-node{flex:1 1 0}
.aoviz-gate{flex:1.3 1 0;border-color:var(--accent);background:var(--accent-soft);align-items:flex-start}
.aoviz-gate .aoviz-ntag{color:var(--accent)}
.aoviz-gate-row{display:flex;gap:8px;margin-top:4px}
.aoviz-pill{font-size:10px;letter-spacing:.05em;padding:4px 9px;border-radius:0;border:1px solid var(--border)}
.aoviz-pill.ok{color:var(--accent);border-color:var(--accent);background:var(--bg)}
.aoviz-pill.hold{color:var(--c2);border-color:var(--c2-border,var(--c2));background:var(--bg)}
.aoviz-feedback{margin-top:1rem;display:flex;align-items:center;justify-content:center;gap:10px;
  font-size:11px;color:var(--text-muted)}
.aoviz-feedback .arc{flex:0 0 120px;height:1px;background:repeating-linear-gradient(90deg,var(--c2) 0 6px,transparent 6px 12px);opacity:.6}

/* ── 3 · ITERATIVE CYCLE ── */
.aoviz-cycle{position:relative;width:min(460px,86vw);aspect-ratio:1;margin:0 auto}
.aoviz-cycle svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.aoviz-cycle-center{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;
  width:42%;display:flex;flex-direction:column;gap:4px;align-items:center}
.aoviz-cycle-center .k{font-family:var(--f-display,'Space Grotesk',sans-serif);font-size:clamp(1.1rem,4vw,1.55rem);font-weight:600;color:var(--text);line-height:1.05}
.aoviz-cycle-center .s{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-faint)}
.aoviz-cnode{position:absolute;transform:translate(-50%,-50%);width:clamp(90px,24%,128px);text-align:center;
  padding:10px 6px;border:1.5px solid var(--border);border-radius:0;background:var(--bg-card)}
.aoviz-cnode .n{font-size:10px;color:var(--accent);letter-spacing:.1em}
.aoviz-cnode .t{font-family:var(--f-display,'Space Grotesk',sans-serif);font-size:clamp(.78rem,2.6vw,.95rem);font-weight:600;color:var(--text);line-height:1.1;margin-top:2px}
.aoviz-cnode .d{font-size:9.5px;color:var(--text-muted);line-height:1.35;margin-top:3px}

/* ── 4 · OBSERVABILITY ── */
.aoviz-obs{display:grid;grid-template-columns:0.85fr 1.15fr;gap:1.25rem;align-items:stretch}
.aoviz-guarantees{display:flex;flex-direction:column;gap:10px;justify-content:center}
.aoviz-guarantee{display:flex;gap:11px;padding:13px 15px;border:1px solid var(--border);border-radius:0;background:var(--bg-card)}
.aoviz-guarantee .gi{flex:0 0 auto;width:24px;height:24px;border:1.5px solid var(--accent);border-radius:0;
  display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:12px}
.aoviz-guarantee .gt{font-family:var(--f-display,'Space Grotesk',sans-serif);font-weight:600;font-size:.92rem;color:var(--text)}
.aoviz-guarantee .gd{font-size:10.5px;color:var(--text-muted);line-height:1.45;margin-top:2px}
.aoviz-ledger{border:1.5px solid var(--border);border-radius:0;background:var(--bg-card);overflow:hidden}
.aoviz-ledger-bar{display:flex;align-items:center;gap:8px;padding:9px 13px;border-bottom:1px solid var(--border);
  font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--text-faint)}
.aoviz-ledger-bar .live{margin-left:auto;display:inline-flex;align-items:center;gap:6px;color:var(--accent)}
.aoviz-ledger-bar .live::before{content:"";width:6px;height:6px;border-radius:0;background:var(--accent);animation:aoviz-pulse 1.6s ease-in-out infinite}
.aoviz-feed{padding:6px 0}
.aoviz-row{display:flex;align-items:center;gap:10px;padding:8px 13px;font-size:11px;color:var(--text-body);
  border-bottom:1px solid var(--border);opacity:0;animation:aoviz-rowin .6s ease forwards}
.aoviz-row:last-child{border-bottom:0}
.aoviz-row .ts{color:var(--text-faint);flex:0 0 auto}
.aoviz-row .msg{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aoviz-row .bdg{flex:0 0 auto;font-size:9px;letter-spacing:.06em;padding:2px 7px;border-radius:0;text-transform:uppercase}
.aoviz-row .bdg.ai{color:var(--c3);border:1px solid var(--c3-border,var(--c3))}
.aoviz-row .bdg.human{color:var(--accent);border:1px solid var(--accent)}
.aoviz-row .bdg.live{color:var(--text-body);border:1px solid var(--border)}
.aoviz-row:nth-child(1){animation-delay:.1s}.aoviz-row:nth-child(2){animation-delay:.5s}
.aoviz-row:nth-child(3){animation-delay:.9s}.aoviz-row:nth-child(4){animation-delay:1.3s}
.aoviz-row:nth-child(5){animation-delay:1.7s}.aoviz-row:nth-child(6){animation-delay:2.1s}
@keyframes aoviz-rowin{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}

/* ── responsive ── */
@media(max-width:820px){
  .aoviz-intake,.aoviz-hitl{flex-direction:column;align-items:stretch}
  .aoviz-intake>.aoviz-wire,.aoviz-hitl>.aoviz-wire{flex:0 0 26px;width:2px;height:26px;align-self:center}
  .aoviz-intake>.aoviz-wire::after,.aoviz-hitl>.aoviz-wire::after{inset:0 0 auto 0;width:100%;height:55%;
    background:linear-gradient(180deg,transparent,var(--accent),transparent);animation:aoviz-flowv 2.2s linear infinite}
  .aoviz-sources{grid-template-columns:repeat(3,1fr)}
  .aoviz-obs{grid-template-columns:1fr}
}
@media(max-width:520px){.aoviz-sources{grid-template-columns:repeat(2,1fr)}}

/* respect reduced motion */

/* ==================== RECEIPTS BAND (trust proof) ==================== */
/* ── v9: the proof rail is the base of the hero, not a band under it ────────
   .receipts was a separate section on --bg-alt with its own border. It is
   now inside <section class="hero">, so it must NOT paint a background or a
   top border: either one re-draws the seam this change exists to remove.
   The one rule it keeps is the hairline ABOVE the figures, which reads as
   the hero's own baseline rather than as the start of a new band. */
.hero-receipts{margin-top:clamp(2.5rem,5vw,4rem);padding-top:clamp(1.75rem,3vw,2.5rem);
  border-top:1px solid var(--hairline)}
/* Kept so any other page still rendering the old band is not broken. */
.receipts{padding:2.75rem 0;border-bottom:1px solid var(--border);background:var(--bg-alt)}
/* v9: open rail, not a boxed table. Inside the hero the 1px-gap-on-border
   trick drew a second framed object directly under the headline, which is
   what made the old band read as a separate page element. Hairline columns
   carry the same separation with none of the weight. */
.receipts-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:0}
.hero-receipts .receipt{background:transparent;padding:0 1.75rem 0 0;border-left:1px solid var(--hairline);
  padding-left:1.75rem}
.hero-receipts .receipt:first-child{border-left:0;padding-left:0}
.hero-receipts .receipt:hover{background:transparent}
.hero-receipts .receipts-src{text-align:left;margin-top:1.5rem}
@media(max-width:760px){
  .hero-receipts .receipt{border-left:0;padding:0 0 1.1rem}
  .hero-receipts .receipt+.receipt{border-top:1px solid var(--hairline);padding-top:1.1rem}
}
.receipt{background:var(--bg);padding:1.75rem 1.5rem;display:flex;flex-direction:column;gap:.4rem;transition:background .25s ease}
.receipt:hover{background:var(--bg-alt)}
.receipt-num{font-family:var(--f-mono);font-size:clamp(1.75rem,3vw,2.5rem);font-weight:800;color:var(--text);line-height:1;letter-spacing:-.02em}
.receipt-num.accent{color:var(--accent)}
.receipt-label{font-family:var(--f-label);font-size:var(--fs-mono-xs);letter-spacing:.5px;color:var(--text-muted);text-transform:lowercase}
.receipts-src{margin-top:1.25rem;text-align:center;font-family:var(--f-label);font-size:var(--fs-mono-xs);letter-spacing:.5px;color:var(--text-faint)}
.receipts-src a{color:var(--text-muted);border-bottom:1px solid var(--border-hover);transition:color .2s,border-color .2s}
.receipts-src a:hover{color:var(--accent);border-color:var(--accent)}
@media(max-width:720px){
  .receipt{padding:1.25rem 1rem}
}

/* ==================== 3D DEPTH SYSTEM ==================== */
/* Hero wash. These were paper at low alpha, which is invisible on paper —
   the whole hero scene silently rendered as nothing. Ink at low alpha instead,
   and slightly weaker, because a dark wash on a light page reads much more
   strongly than a light wash on a dark one at the same alpha. */
:root{--aurora-1:rgba(32, 30, 29,.022);--aurora-2:rgba(32, 30, 29,.014);--grid-3d:rgba(32, 30, 29,.030)}

/* --- Signature hero scene --- */
.hero,.page-hero{perspective:1400px;perspective-origin:50% 35%}
.page-hero{position:relative;overflow:hidden}
.page-hero>.container{position:relative;z-index:2}
.hero-scene{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;transition:transform .25s ease-out;will-change:transform}
.hero-aurora{position:absolute;inset:-25%;
  background:
    radial-gradient(38% 44% at 28% 32%, var(--aurora-1), transparent 70%),
    radial-gradient(34% 40% at 74% 38%, var(--aurora-2), transparent 70%),
    radial-gradient(46% 46% at 52% 82%, var(--aurora-1), transparent 72%);
  filter:blur(46px);opacity:.95;
  animation:ao-aurora 22s ease-in-out infinite alternate}
@keyframes ao-aurora{
  0%{transform:translate3d(0,0,0) scale(1)}
  50%{transform:translate3d(4%,-3%,0) scale(1.1)}
  100%{transform:translate3d(-4%,3%,0) scale(1.05)}}
.hero-grid3d{position:absolute;left:-50%;right:-50%;bottom:-32%;height:150%;margin:auto;
  transform:rotateX(76deg);transform-origin:50% 100%;
  background-image:linear-gradient(var(--grid-3d) 1px,transparent 1px),linear-gradient(90deg,var(--grid-3d) 1px,transparent 1px);
  background-size:56px 56px;
  -webkit-mask-image:radial-gradient(62% 72% at 50% 28%, #000 8%, transparent 82%);
  mask-image:radial-gradient(62% 72% at 50% 28%, #000 8%, transparent 82%);
  animation:ao-grid 4.5s linear infinite}
@keyframes ao-grid{from{background-position:0 0}to{background-position:0 56px}}
.hero-orb{position:absolute;width:440px;height:440px;border-radius:0;top:4%;left:50%;
  background:radial-gradient(circle at 50% 38%, var(--aurora-2), transparent 64%);
  filter:blur(12px);opacity:.6;transform:translateX(-50%);
  animation:ao-float 10s ease-in-out infinite}
@keyframes ao-float{0%,100%{transform:translate(-50%,0)}50%{transform:translate(-50%,-22px)}}

/* --- Cursor-driven 3D tilt cards --- */
.tilt3d{position:relative;transform-style:preserve-3d;
  transition:transform .35s cubic-bezier(.22,1,.36,1),
             box-shadow var(--hov-out) var(--hov-ease);
  will-change:transform}
/* No cursor-tracking glow and no accent border on hover. A 340px radial glow
   fading in and out per card meant crossing the row flashed each one in turn;
   the green border jump did the same in colour. v6's rule applies instead — no
   lift, the shadow simply deepens — on the asymmetric hover timing so a passing
   cursor never triggers it and what you leave behind decays. */
.tilt3d:hover{box-shadow:var(--shadow-lg);
  transition:transform .35s cubic-bezier(.22,1,.36,1),
             box-shadow var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.receipts-grid.tilt3d:hover{box-shadow:inset 0 0 0 1px var(--border-hover)}

/* --- Scroll reveal with depth --- */
.reveal-depth{opacity:0;transform:perspective(1000px) rotateX(8deg) translateY(40px);
  transition:opacity .9s cubic-bezier(.22,1,.36,1),transform .9s cubic-bezier(.22,1,.36,1)}
.reveal-depth.visible{opacity:1;transform:perspective(1000px) rotateX(0) translateY(0)}


/* ============================================================
   LAYERED FLY-IN SYSTEM (v5 — twitch-rivals style)
   Content flies in from the sides and stacks in depth layers,
   parallaxing to cursor + scroll with a gentle idle float.
   Used on the homepage hero + prioritized on important content.
   ============================================================ */

/* --- Hero: give the stage depth + room for the layers --- */
.hero-fly{position:relative;overflow:hidden;perspective:1600px;perspective-origin:50% 40%}
.hero-fly .container{position:relative;z-index:3}
/* Page heroes are short — give them vertical room so the side layers have space */
.page-hero.hero-fly{min-height:clamp(440px,54vh,580px);display:flex;align-items:center}
.page-hero.hero-fly>.container{width:100%}

/* --- Floating content tiles, stacked in real Z-depth layers around the hero --- */
.hero-layers{position:absolute;inset:0;z-index:2;pointer-events:none;
  transform-style:preserve-3d;will-change:transform}
.hl-tile{position:absolute;width:clamp(170px,15vw,212px);
  /* Never let a gutter tile cross into the text column. The gutter is
     (100vw - content-width)/2, so clamp the tile to what actually fits
     beside the container and leave 1rem of air. */
  max-width:calc((100vw - var(--content-width)) / 2 - 1rem);
  transform-style:preserve-3d;
  will-change:transform;transition:opacity .4s ease}

/* Depth-of-field bands — perspective auto-scales by translateZ; we add the
   blur/dim/shadow cues so the layers separate like twitch-rivals' stage. */
.hl-tile[data-band="back"]{width:clamp(146px,11.5vw,178px);
  filter:blur(2.4px) saturate(.85);opacity:.58}
.hl-tile[data-band="back"] .hl-in{box-shadow:none}
.hl-tile[data-band="front"]{width:clamp(192px,16.5vw,242px)}
.hl-tile[data-band="front"] .hl-in{
  box-shadow:none}
.hl-tile[data-band="front"] .hl-val{font-size:1.7rem}
.hl-tile[data-band="front"] .hl-in{
  box-shadow:0 0 0 1px rgba(32, 30, 29,.06)}
.hl-tile .hl-in{
  display:block;
  background:var(--bg-card);
  border:none;
  border-radius:0;
  padding:.8rem .95rem;
  box-shadow:inset 0 0 0 1px var(--border);
  opacity:0;
  /* bubble-up start: faint + blurred, sitting low, drifts up into place */
  transform:translate3d(0,clamp(90px,16vh,190px),0) scale(.8);
  filter:blur(5px);
  will-change:transform,opacity,filter}
.hl-tile .hl-in{box-shadow:inset 0 0 0 1px var(--border)}
/* Accented tile is marked with an ink ring, not an accent fill. */
.hl-tile.accent .hl-in{box-shadow:inset 0 0 0 1.5px var(--ink)}
.hl-tile .hl-cap{font-family:var(--f-label);font-size:11px;letter-spacing:var(--tr-caps);
  text-transform:uppercase;color:var(--text-faint);display:block;margin-bottom:.3rem}
.hl-tile .hl-val{font-family:var(--f-sans);font-weight:800;font-size:1.375rem;
  line-height:1.05;color:var(--ink);letter-spacing:-.035em;display:block;font-variant-numeric:tabular-nums}
.hl-tile .hl-val.accent{color:var(--accent)}
.hl-tile .hl-sub{font-family:var(--f-label);font-size:11px;color:var(--text-muted);
  display:block;margin-top:.3rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hl-tile .hl-spark{display:flex;align-items:flex-end;gap:3px;height:22px;margin-top:.45rem}
.hl-tile .hl-spark i{flex:1;background:var(--accent-bright);border-radius:0;opacity:.7;
  transform-origin:bottom;animation:hl-bar 2.6s ease-in-out infinite}
@keyframes hl-bar{0%,100%{transform:scaleY(.7)}50%{transform:scaleY(1)}}

/* Trigger: parent gets .hero-fly-go on load → tiles bubble up from the bottom,
   fading from faint+blurred to clear as they float to the top, staggered. */
.hero-fly-go .hl-tile .hl-in{
  animation:hlBubble 1.7s cubic-bezier(.22,1,.28,1) var(--d,0s) forwards}
@keyframes hlBubble{
  0%{opacity:0;filter:blur(5px);transform:translate3d(0,clamp(90px,16vh,190px),0) scale(.8)}
  35%{opacity:.55}
  70%{opacity:1;filter:blur(.6px)}
  to{opacity:1;filter:blur(0);transform:translate3d(0,0,0) scale(1)}}

/* --- Reusable directional fly-in (load + scroll-triggered) --- */
.fly{opacity:0;will-change:transform,opacity}
.fly-up{transform:translate3d(0,48px,0) scale(.97)}
.fly-left{transform:translate3d(-72px,18px,0) rotate(-2.5deg)}
.fly-right{transform:translate3d(72px,18px,0) rotate(2.5deg)}
.fly-pop{transform:scale(.78)}
.fly.in{opacity:1;transform:none;
  transition:opacity .7s cubic-bezier(.16,1,.3,1),transform .85s cubic-bezier(.16,1,.3,1);
  transition-delay:var(--fd,0s)}

/* Staggered fly-in across a row of children (cards flying into a stack) */
.fly-row>*{opacity:0;transform:translate3d(0,52px,0) scale(.95)}
.fly-row.in>*{opacity:1;transform:none;
  transition:opacity .65s cubic-bezier(.16,1,.3,1),transform .8s cubic-bezier(.16,1,.3,1)}
.fly-row.in>*:nth-child(1){transition-delay:.04s}
.fly-row.in>*:nth-child(2){transition-delay:.13s}
.fly-row.in>*:nth-child(3){transition-delay:.22s}
.fly-row.in>*:nth-child(4){transition-delay:.31s}
.fly-row.in>*:nth-child(5){transition-delay:.40s}
.fly-row.in>*:nth-child(6){transition-delay:.49s}

/* Alternate edges so cards visibly fly in from both sides into the stack */
.fly-row.fly-alt>*:nth-child(odd){transform:translate3d(-64px,32px,0) rotate(-2deg) scale(.95)}
.fly-row.fly-alt>*:nth-child(even){transform:translate3d(64px,32px,0) rotate(2deg) scale(.95)}
.fly-row.fly-alt.in>*{transform:none}

/* Below the desktop nav breakpoint there is no side room for a lateral
   entrance. The pre-reveal state translated cards up to 64px past the viewport,
   which widened the document and shoved the sticky nav's centred inner — and
   its hamburger — off screen. Vertical-only entrance there instead. */
@media(max-width:1024px){
  .fly-left,.fly-right,
  .fly-row.fly-alt>*:nth-child(odd),.fly-row.fly-alt>*:nth-child(even){
    transform:translate3d(0,44px,0) scale(.96)}
  .reveal-left,.reveal-right{transform:translateY(24px)}
}

/* Hide the floating layers where there's no side room */
@media(max-width:1540px){.hero-layers{display:none}}


/* ============================================================
   WEB BUILDER · EMAIL WATERFALL · 3D · SCALING PRICING (v4.1)
   ============================================================ */

/* --- WebGL canvas layered over the CSS hero scene --- */
.ao3d-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;
  pointer-events:none;z-index:1;opacity:.92}
[data-ao3d]{position:relative}
[data-ao3d]>.ao3d-canvas{z-index:0}

/* --- Nav: Start free trial button --- */
.nav-trial{font-family:var(--f-label);font-size:var(--fs-mono-xs);letter-spacing:.5px;
  color:var(--text-body);text-decoration:none;padding:9px 16px;border:1px solid var(--border);
  border-radius:0;transition:all .2s;white-space:nowrap;margin-right:.25rem}
.nav-trial:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-soft);
  box-shadow:0 0 0 3px var(--accent-glow)}

/* --- Product page: feature grid + steps + always-on --- */
.feat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.25rem;margin-top:2.5rem}
.feat-card{border:1px solid var(--border);border-radius:0;padding:1.75rem;
  background:var(--bg-card);transition:all .35s cubic-bezier(.22,1,.36,1)}
.feat-card:hover{border-color:var(--border-hover);box-shadow:var(--shadow-md)}
.feat-icon{font-family:var(--f-mono);font-size:1.4rem;color:var(--accent);margin-bottom:.75rem}
.feat-card h3{margin:0 0 .4rem;font-size:1.1rem}
.feat-card p{margin:0;font-size:var(--fs-mid);color:var(--text-body);line-height:var(--lh-body)}
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:1.25rem;margin-top:2.5rem;counter-reset:step}
.step{position:relative;border:1px solid var(--border);border-radius:0;
  padding:1.75rem;background:var(--bg-card)}
.step::before{counter-increment:step;content:'0' counter(step);font-family:var(--f-mono);
  font-size:var(--fs-mono-xs);color:var(--accent);letter-spacing:1px}
.step h3{margin:.5rem 0 .35rem;font-size:1.05rem}
.step p{margin:0;font-size:var(--fs-mid);color:var(--text-body);line-height:var(--lh-body)}
@media(max-width:820px){.feat-grid,.steps{grid-template-columns:1fr}}

/* --- Overnight / works-while-you-sleep timeline --- */
.ovn{display:grid;grid-template-columns:1.1fr 1fr;gap:2rem;align-items:center;margin-top:2.5rem}
.ovn-clock{border:1px solid var(--border);border-radius:0;background:var(--bg-card);
  padding:2rem;text-align:center;position:relative;overflow:hidden}
.ovn-clock-time{font-family:var(--f-mono);font-size:3rem;font-weight:700;color:var(--accent);
  line-height:1;letter-spacing:1px}
.ovn-clock-label{font-family:var(--f-mono);font-size:var(--fs-mono-xs);letter-spacing:1.5px;
  text-transform:uppercase;color:var(--text-faint);margin-top:.5rem}
.ovn-clock-sky{position:absolute;inset:0;z-index:0;opacity:.5;
  background:radial-gradient(60% 50% at 50% 0%,var(--accent-soft),transparent 70%)}
.ovn-clock>*{position:relative;z-index:1}
.ovn-moon{width:54px;height:54px;border-radius:0;margin:0 auto 1rem;
  background:radial-gradient(circle at 38% 38%,var(--accent-glow),transparent 60%),var(--bg-alt);
  border:1px solid var(--border);box-shadow:none}
.ovn-events{border:1px solid var(--border);border-radius:0;background:var(--bg-card);
  padding:1rem 1.25rem;min-height:280px}
.ovn-event{display:flex;gap:.75rem;align-items:flex-start;padding:.7rem 0;
  border-bottom:1px solid var(--border);opacity:0;transform:translateY(8px);
  transition:opacity .45s ease,transform .45s ease}
.ovn-event:last-child{border-bottom:none}
.ovn-event.on{opacity:1;transform:translateY(0)}
.ovn-event-t{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--accent);
  min-width:62px;padding-top:2px}
.ovn-event-x{font-size:var(--fs-mid);color:var(--text-body);line-height:1.5}
.ovn-event-x b{color:var(--text);font-weight:600}
@media(max-width:820px){.ovn{grid-template-columns:1fr}}

/* --- Web builder viz: blocks assembling into a live site --- */
.bld{border:1px solid var(--border);border-radius:0;background:var(--bg-card);
  padding:1.25rem;margin-top:2.5rem;overflow:hidden}
.bld-bar{display:flex;align-items:center;gap:.5rem;padding-bottom:.85rem;margin-bottom:1rem;
  border-bottom:1px solid var(--border)}
.bld-dot{width:10px;height:10px;border-radius:0;background:var(--border)}
.bld-url{flex:1;margin-left:.5rem;font-family:var(--f-mono);font-size:var(--fs-mono-xs);
  color:var(--text-faint);background:var(--bg-alt);border-radius:0;padding:5px 10px}
.bld-live{font-family:var(--f-mono);font-size:9px;letter-spacing:1px;color:var(--accent);
  border:1px solid var(--accent-glow);border-radius:0;padding:3px 7px}
.bld-block{border-radius:0;background:var(--bg-alt);margin-bottom:.6rem;
  opacity:0;transform:translateY(14px) scale(.98);
  transition:opacity .5s cubic-bezier(.22,1,.36,1),transform .5s cubic-bezier(.22,1,.36,1)}
.bld-block.on{opacity:1;transform:none}
.bld-hero{height:64px;background:linear-gradient(110deg,var(--accent-soft),var(--bg-alt))}
.bld-row{display:flex;gap:.6rem}
.bld-row .bld-block{flex:1;height:46px}
/* This block is a drawn CTA button inside the build animation — it depicts the
   thing a visitor would click on the site being built. It was green, which made
   the one mock button on the page the one colour the real buttons never use.
   Coral, like every other action on the site. */
.bld-cta{height:30px;width:140px;background:var(--brand-vivid);opacity:0;transform:translateY(14px);
  border-radius:0;transition:opacity .5s ease,transform .5s ease}
.bld-cta.on{opacity:1;transform:none}

/* --- Email waterfall viz --- */
.wf{margin-top:2.5rem;display:flex;flex-direction:column;gap:0;position:relative}
.wf-stage{display:flex;justify-content:center;gap:1rem;position:relative;padding:.5rem 0}
.wf-node{border:1px solid var(--border);border-radius:0;background:var(--bg-card);
  padding:.85rem 1.1rem;min-width:170px;text-align:left;position:relative;
  opacity:.35;transform:translateY(6px);transition:all .5s cubic-bezier(.22,1,.36,1)}
.wf-node.on{opacity:1;transform:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}
.wf-node .wf-k{font-family:var(--f-mono);font-size:9px;letter-spacing:1px;text-transform:uppercase;
  color:var(--text-faint)}
.wf-node .wf-t{font-size:var(--fs-mid);color:var(--text);font-weight:600;margin-top:2px}
.wf-node .wf-m{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--accent);margin-top:4px}
.wf-pipe{width:2px;height:26px;margin:0 auto;background:var(--border);position:relative;overflow:hidden}
.wf-pipe::after{content:'';position:absolute;inset:0;background:var(--accent);transform:translateY(-100%);
  transition:transform .6s ease}
.wf-pipe.on::after{transform:translateY(0)}
.wf-branch{font-family:var(--f-mono);font-size:9px;letter-spacing:1px;color:var(--text-faint);
  position:absolute;top:50%;transform:translateY(-50%)}
@media(max-width:680px){.wf-stage{flex-direction:column;align-items:center}.wf-node{min-width:220px}}

/* --- Scaling pricing: toggle + configurator + tiers --- */
.pricing-toggle{display:inline-flex;border:1px solid var(--border);border-radius:0;
  padding:4px;background:var(--bg-card);margin:1.5rem auto 0;gap:2px}
/* min-height, not more padding: this is the control that switches the whole
   pricing table between "you run it" and "we run it", so it is the most-tapped
   thing on the page, and it was rendering 37px tall — under the 44px floor for
   a touch target. The pill only needed 7px it was not being given. */
.pt-btn{font-family:var(--f-sans);font-size:var(--fs-mid);font-weight:600;letter-spacing:-.005em;color:var(--text-muted);
  background:none;border:none;cursor:pointer;padding:9px 22px;border-radius:0;transition:all .25s;
  min-height:44px;display:inline-flex;align-items:center;justify-content:center}
.pt-btn.active{background:var(--ink);color:var(--bg)}
.pt-btn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.pricing-pane{display:none}
.pricing-pane.active{display:block}

/* Configurator (self-serve scaling) */
.cfg{display:grid;grid-template-columns:1.4fr .9fr;gap:1.5rem;margin-top:2.5rem;align-items:start}
.cfg-build{border:1px solid var(--border);border-radius:0;background:var(--bg-card);padding:2rem}
.cfg-base{display:flex;justify-content:space-between;align-items:center;padding-bottom:1rem;
  border-bottom:1px solid var(--border);margin-bottom:1.25rem}
.cfg-base h3{margin:0;font-size:1.15rem}
.cfg-base .cfg-baseprice{font-family:var(--f-mono);font-weight:700;color:var(--text)}
.cfg-addons{display:flex;flex-direction:column;gap:.75rem}
.addon-chip{display:flex;align-items:center;gap:.85rem;border:1px solid var(--border);
  border-radius:0;padding:1rem 1.1rem;cursor:pointer;background:var(--bg-alt);
  transition:all .25s;text-align:left}
.addon-chip:hover{border-color:var(--accent)}
.addon-chip.on{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 2px var(--accent-glow)}
.addon-check{width:20px;height:20px;border-radius:0;border:1.5px solid var(--border);flex-shrink:0;
  display:flex;align-items:center;justify-content:center;color:transparent;font-size:13px;transition:all .2s}
.addon-chip.on .addon-check{background:var(--ink);border-color:var(--ink);color:var(--bg)}
.addon-meta{flex:1}
.addon-meta .addon-name{font-weight:600;font-size:var(--fs-body)}
.addon-meta .addon-blurb{font-size:var(--fs-small);color:var(--text-muted);line-height:1.5}
.addon-price{font-family:var(--f-mono);font-size:var(--fs-mono-sm);color:var(--accent);white-space:nowrap}
.cfg-total{border:1px solid var(--accent);border-radius:0;background:var(--accent-soft);
  padding:2rem;position:sticky;top:90px}
.cfg-total-label{font-family:var(--f-label);font-size:var(--fs-mono-xs);letter-spacing:var(--tr-caps);
  text-transform:uppercase;color:var(--text-faint)}
.cfg-total-val{font-family:var(--f-mono);font-size:2.75rem;font-weight:700;color:var(--text);
  line-height:1;margin:.35rem 0 .25rem}
.cfg-total-val span{font-size:1rem;font-weight:400;color:var(--text-muted)}
.cfg-total-list{list-style:none;padding:0;margin:1.25rem 0;font-size:var(--fs-small);
  color:var(--text-body)}
.cfg-total-list li{display:flex;justify-content:space-between;padding:.35rem 0;
  border-bottom:1px solid var(--border)}
.cfg-total-list li span:last-child{font-family:var(--f-mono);color:var(--text-muted)}
@media(max-width:820px){.cfg{grid-template-columns:1fr}.cfg-total{position:static}}

/* Done-for-you tiers + HITL badge */
.hitl-badge{display:inline-flex;align-items:center;gap:6px;font-family:var(--f-mono);font-size:10px;
  letter-spacing:1px;text-transform:uppercase;color:var(--accent);border:1px solid var(--accent-glow);
  background:var(--accent-soft);border-radius:0;padding:4px 9px;margin-top:.85rem}
.hitl-badge::before{content:'';width:6px;height:6px;border-radius:0;background:var(--accent);
  animation:pulse-dot 2.5s ease infinite}

/* ============================================================
   MOBILE OVERFLOW HARDENING  (mobile-friendliness / SEO)
   The programmatic SEO templates (/{city}/{industry}, /seo-services-in/*,
   etc.) render grids with INLINE grid-template-columns:repeat(N,1fr),
   which override the class-based responsive rules above and cause
   horizontal scroll on phones. These !important rules collapse those
   inline grids, stop grid/flex items from blowing out their track, and
   add a clip backstop so no page can scroll sideways on mobile.
   ============================================================ */
@media(max-width:768px){
  /* Collapse inline 3-6 column grids to 2-up on tablets / large phones */
  [style*="grid-template-columns:repeat(3"],[style*="grid-template-columns: repeat(3"],
  [style*="grid-template-columns:repeat(4"],[style*="grid-template-columns: repeat(4"],
  [style*="grid-template-columns:repeat(5"],[style*="grid-template-columns: repeat(5"],
  [style*="grid-template-columns:repeat(6"],[style*="grid-template-columns: repeat(6"]{
    grid-template-columns:repeat(2,1fr)!important}
  /* Let grid/flex children shrink below their content size (prevents
     track blowout from long words, fixed inner widths, JS-built viz) */
  .grid-2>*,.grid-3>*,.grid-4>*,.card-grid>*,.stat-grid>*,.results-grid>*,
  .ifx-grid>*,.ifx-cards>*,.split>*,[style*="grid-template-columns"]>*{min-width:0}
  /* Inline "instant audit" form: stack input + button full width */
  .audit-inline-form{flex-wrap:wrap}
  .audit-inline-form input,.audit-inline-form button{flex:1 1 100%;width:100%;max-width:none}
}
@media(max-width:600px){
  /* Phones: any inline multi-column grid → single column (always fits) */
  [style*="grid-template-columns:repeat(2"],[style*="grid-template-columns: repeat(2"],
  [style*="grid-template-columns:repeat(3"],[style*="grid-template-columns: repeat(3"],
  [style*="grid-template-columns:repeat(4"],[style*="grid-template-columns: repeat(4"],
  [style*="grid-template-columns:repeat(5"],[style*="grid-template-columns: repeat(5"],
  [style*="grid-template-columns:repeat(6"],[style*="grid-template-columns: repeat(6"]{
    grid-template-columns:1fr!important}
}
/* Backstop: never allow sideways scroll on phones. `clip` (not `hidden`)
   does NOT create a scroll container, so sticky/fixed positioning still
   works. Root causes are fixed above; this just guarantees the result. */
@media(max-width:768px){
  html,body{overflow-x:clip;max-width:100%}
}

/* ========================================================================
   v6 PATTERN LAYER
   The components the hairline-band system needs that the old system had
   no equivalent for. Everything here is additive — nothing above depends
   on it, so a page opts in by using the class.
   ===================================================================== */

/* ---- Band: a full-bleed hairline-separated row. Use in place of
   .section when a page wants the band to own its own vertical rhythm. */
.band{padding:var(--band-y) 0;border-bottom:1px solid var(--hairline)}
.band--tight{padding:3rem 0}
.band--flush{padding-bottom:0}
.band--muted{background:var(--bg-alt)}
/* .band--ink is "the maximum-contrast band". On v6 that meant a dark fill with
   light text on a paper page. v7 made --ink paper, so the band had to invert to
   dark-on-paper or it would have painted light text on a light fill. v8 puts
   --ink back to near-black, so this inverts a second time and lands back on the
   v6 arrangement: a dark fill carrying light type, the one dark band on an
   otherwise paper site. Currently unused in markup, kept correct so reaching for
   it does not ship an unreadable band.

   The accent flips with it. On paper the AA-safe coral is the darkened
   --brand-text rung; on THIS fill that rung is far too dark, and full-saturation
   --brand-vivid is the one that works (5.9:1 on #201e1d). Same for the link
   colour: deep green #1e40af is unreadable here, so links take the bright rung. */
.band--ink{background:var(--ink);color:#f8f4f4}
.band--ink h2,.band--ink h3{color:#f3f2f2}
.band--ink .desc,.band--ink .ssub{color:#bab6b6}
.band--ink .eyebrow{color:#bab6b6}
.band--ink .eyebrow::before{background:#f3f2f2;box-shadow:0 0 0 3px rgba(251,251,247,.14)}
.band--ink a{color:#1d4ed8}
.band:last-child{border-bottom:none}

/* ---- Band header: eyebrow + h2 + one-line sub, left aligned, capped
   so the measure never runs past ~66 characters. */
.band-head{max-width:60ch;margin-bottom:2.75rem}
.band-head .desc,.band-head .ssub{margin-bottom:0}
.band-head--wide{max-width:74ch}
/* Header with a trailing action on the same baseline as the h2. */
.band-head-row{display:flex;align-items:flex-end;justify-content:space-between;gap:2rem;flex-wrap:wrap;margin-bottom:2.75rem}
.band-head-row .band-head{margin-bottom:0}

/* ---- Live pulse dot. The telemetry tell: a small dot that breathes.
   Paired with an uppercase label it reads as "this is running now". */
.live-dot{display:inline-block;width:7px;height:7px;border-radius:0;background:var(--accent-bright);flex:none;animation:ao-pulse 2.4s cubic-bezier(.4,0,.6,1) infinite}
.live-dot--muted{background:var(--text-faint);animation:none}
@keyframes ao-pulse{0%,100%{opacity:1;box-shadow:0 0 0 0 var(--accent-glow)}50%{opacity:.55;box-shadow:0 0 0 5px transparent}}
.live-label{display:inline-flex;align-items:center;gap:6px;font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint)}

/* ---- Status chip: tiny floating badge, white on a soft double shadow.
   Used to annotate a visualisation ("+38%", "flagged", "live"). */
.chip{display:inline-flex;align-items:center;gap:4px;border-radius:0;background:var(--bg-card);padding:3px 9px;font-size:.625rem;font-weight:600;color:var(--ink);box-shadow:none;white-space:nowrap}
.chip--up{color:var(--success)}
.chip--warn{color:var(--warning)}

/* ---- Case-study card. Two variants alternate across a row: one white
   with elevation, one flat on the muted surface. */
.cs-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.25rem}
.cs-card{display:flex;flex-direction:column;justify-content:space-between;gap:2rem;min-width:0;border-radius:0;padding:1.75rem;color:var(--text);background:var(--bg-card);box-shadow:var(--shadow-card);transition:box-shadow .25s ease}
.cs-card--flat{background:var(--bg-alt);box-shadow:var(--shadow-ring)}
.cs-card:hover{box-shadow:inset 0 0 0 1px var(--border-hover)}
.cs-quote{font-size:1.0625rem;line-height:1.5;letter-spacing:-.015em;color:var(--ink)}
.cs-stat{font-family:var(--f-sans);font-size:clamp(2.25rem,4vw,3rem);font-weight:800;letter-spacing:-.05em;line-height:1;color:var(--ink);font-variant-numeric:tabular-nums}
.cs-meta{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap}
.cs-who{font-size:var(--fs-small);color:var(--text-muted)}
@media(min-width:769px){.cs-card{padding:2.5rem}}

/* ---- Logo / capability strip: a centred hairline row of muted marks
   under a single uppercase caption. */
.logo-strip{display:flex;align-items:center;justify-content:center;gap:2.5rem;flex-wrap:wrap}
.logo-strip img{height:24px;width:auto;opacity:.5;filter:grayscale(1);transition:opacity .25s ease}
.logo-strip img:hover{opacity:.85}
.strip-cap{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint);text-align:center;margin-bottom:1.5rem}

/* ---- Inline email capture: single hairline field with the ink button
   sitting inside it. mai's hero conversion unit. */
.inline-capture{display:flex;align-items:center;gap:6px;padding:6px;border-radius:0;background:var(--bg-card);box-shadow:inset 0 0 0 1px var(--border);max-width:460px;transition:box-shadow .2s ease}
.inline-capture:focus-within{box-shadow:inset 0 0 0 1px var(--border-hover), 0 0 0 3px var(--accent-glow)}
.inline-capture input{flex:1;min-width:0;border:none;background:transparent;padding:9px 12px;font-family:var(--f-sans);font-size:.9375rem;color:var(--text);letter-spacing:-.005em}
.inline-capture input::placeholder{color:var(--text-faint)}
.inline-capture input:focus{outline:none}
.inline-capture .btn-p{flex:none;padding:11px 18px}
@media(max-width:520px){
  .inline-capture{flex-wrap:wrap;border-radius:0}
  .inline-capture input,.inline-capture .btn-p{flex:1 1 100%;width:100%}
}

/* ---- Numbered flow: the four-beat "how it works" column. Each step is
   a hairline row with a mono index, not a card. */
.flow{display:grid;gap:0;border-radius:0;overflow:hidden;box-shadow:var(--shadow-ring)}
.flow-step{display:grid;grid-template-columns:auto 1fr;gap:1.125rem;padding:1.5rem 1.75rem;background:var(--bg-card);border-bottom:1px solid var(--hairline)}
.flow-step:last-child{border-bottom:none}
.flow-idx{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);color:var(--text-faint);padding-top:.3rem;min-width:2ch}
.flow-body h3{margin-bottom:.3rem}
.flow-body p{font-size:var(--fs-mid);color:var(--text-body);line-height:var(--lh-body);margin:0}

/* ---- Two-column band: copy left, visualisation right. */
.band-split{display:grid;grid-template-columns:1.02fr .98fr;gap:4rem;align-items:center}
.band-split>*{min-width:0}
.band-split--rev>:first-child{order:2}
@media(max-width:900px){
  .band-split,.band-split--rev>:first-child{grid-template-columns:1fr;order:0}
  .cs-grid{grid-template-columns:1fr}
}

/* ---- Hairline metric row: 4 cells divided by hairlines, no card. */
.metric-row{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border-radius:0;overflow:hidden;box-shadow:var(--shadow-ring)}
.metric-cell{padding:1.625rem 1.375rem;background:var(--bg-card);border-right:1px solid var(--hairline)}
.metric-cell:last-child{border-right:none}
.metric-val{font-family:var(--f-sans);font-size:clamp(1.625rem,2.8vw,2.125rem);font-weight:800;letter-spacing:-.045em;line-height:1;color:var(--ink);font-variant-numeric:tabular-nums}
.metric-lab{font-size:var(--fs-small);color:var(--text-muted);margin-top:.4rem;line-height:1.45}
.metric-src{font-family:var(--f-label);font-size:.6875rem;letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint);margin-top:.5rem}
@media(max-width:900px){.metric-row{grid-template-columns:1fr 1fr}
  .metric-cell:nth-child(2){border-right:none}
  .metric-cell:nth-child(1),.metric-cell:nth-child(2){border-bottom:1px solid var(--hairline)}}
@media(max-width:480px){.metric-row{grid-template-columns:1fr}
  .metric-cell{border-right:none;border-bottom:1px solid var(--hairline)}
  .metric-cell:last-child{border-bottom:none}}

/* ---- Mobile: bands breathe less, display type stays regular-weight. */
@media(max-width:768px){
  :root{--band-y:3.5rem}
  .band-head,.band-head-row{margin-bottom:2rem}
  /* Sizes are governed by the fluid scale's clamp minimums now, so the old
     per-breakpoint font-size overrides here would only fight it. */
  .hero-grid{gap:2.5rem}
  .band-split{gap:2.5rem}
  .logo-strip{gap:1.5rem}
  .logo-strip img{height:20px}
  .nav-cta{border-radius:0}
  .flow-step{padding:1.25rem 1.25rem;gap:.875rem}
  .cs-card{padding:1.5rem;gap:1.5rem}
}

/* ═══════════════════════════════════════════════════════════════════
   v7 LAYER — dark surface, motion, and the three showcase bands
   Appended 2026-08-07. Everything above is v6 structure retinted; this
   block is what v7 adds. Keep additions here rather than editing the
   blocks above, so the diff between systems stays readable.
   ═══════════════════════════════════════════════════════════════════ */

/* ---- Band rhythm -------------------------------------------------------
   On paper, a 3% fill was enough to separate two bands. On #0B0B0A the eye
   needs either more value or an edge, and more value turns the page grey.
   So separation is carried by the hairline, and .section-alt is a barely
   perceptible lift that only registers where two bands meet. */
.section-alt{background:var(--bg-alt)}
.band-deep{background:#070706}

/* A one-pixel top edge that catches the "light" of the band above. Applied to
   the first band after the hero so the fold has a real seam. */
.band-lit{position:relative}
.band-lit::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;
  background:linear-gradient(90deg,transparent,rgba(32, 30, 29,.16) 22%,rgba(32, 30, 29,.16) 78%,transparent)}

/* ---- Glow --------------------------------------------------------------
   Reserved for live product surfaces. Two rules only: the hero mockup and a
   focused/active state. Not decoration — if everything glows, nothing reads
   as live. */
.glow-brand{box-shadow:var(--glow-brand)}
.glow-accent{box-shadow:var(--glow-accent)}
.glow-wrap{position:relative;isolation:isolate}
.glow-wrap::after{content:'';position:absolute;inset:-14%;z-index:-1;pointer-events:none;
  background:radial-gradient(60% 50% at 50% 45%,var(--brand-glow) 0%,transparent 70%);
  opacity:.5;filter:blur(38px)}

/* ---- Motion ------------------------------------------------------------
   The v6 reveal (28px rise, 0.8s) stays as the base. v7 adds three things
   the dark surface makes possible and the brief asked for:
     .rise-in   a shorter, snappier entrance for dense grids
     .count-up  numbers that animate — see ao-motion.js
     .sheen     a single light pass across a surface, once, on reveal
   Everything respects prefers-reduced-motion at the bottom of this block. */
.rise-in{opacity:0;transform:translateY(14px);
  transition:opacity .55s var(--hov-ease),transform .55s var(--hov-ease)}
.rise-in.visible{opacity:1;transform:none}
.rise-in-stagger>*{opacity:0;transform:translateY(14px);
  transition:opacity .5s var(--hov-ease),transform .5s var(--hov-ease)}
.rise-in-stagger.visible>*{opacity:1;transform:none}
.rise-in-stagger.visible>*:nth-child(1){transition-delay:.04s}
.rise-in-stagger.visible>*:nth-child(2){transition-delay:.09s}
.rise-in-stagger.visible>*:nth-child(3){transition-delay:.14s}
.rise-in-stagger.visible>*:nth-child(4){transition-delay:.19s}
.rise-in-stagger.visible>*:nth-child(5){transition-delay:.24s}
.rise-in-stagger.visible>*:nth-child(6){transition-delay:.29s}
.rise-in-stagger.visible>*:nth-child(7){transition-delay:.34s}
.rise-in-stagger.visible>*:nth-child(8){transition-delay:.39s}

.sheen{position:relative;overflow:hidden}
.sheen::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(105deg,transparent 38%,rgba(32, 30, 29,.055) 50%,transparent 62%);
  transform:translateX(-120%)}
.sheen.visible::after{animation:sheenPass 1.5s var(--hov-ease) .25s 1 backwards}
@keyframes sheenPass{to{transform:translateX(120%)}}

/* Live pulse — the one continuously-animating element. Used on status dots
   only, where "still running" is the actual information. */
.pulse-dot{position:relative}
.pulse-dot::after{content:'';position:absolute;inset:0;border-radius:0;
  background:inherit;animation:pulseRing 2.4s var(--hov-ease) infinite}
@keyframes pulseRing{
  0%{transform:scale(1);opacity:.6}
  70%{transform:scale(2.6);opacity:0}
  100%{transform:scale(2.6);opacity:0}
}

/* ---- Metric figures (shared by the stats and showcase bands) ---------- */
.mnum{font-family:var(--f-sans);font-weight:400;font-variant-numeric:tabular-nums;
  letter-spacing:-.04em;line-height:1;color:var(--text)}
/* v9: a client's measured growth figure is the textbook case of VERIFIED, and
   it was rendering in the colour that now means "not decided yet". Green. */
.mnum--brand{color:var(--accent)}
.mnum--up{color:var(--accent)}
/* A declining client is shown, not hidden — the portfolio band averages every
   client including the ones that fell. Muted ink rather than red: this is a
   number being reported honestly, not an error state. */
.mnum--down{color:var(--text-muted)}

/* ---- BAND: industry stats --------------------------------------------- */
/* auto-fit, not a fixed 4, so the band does not leave a dead cell when the
   stat count changes. The portfolio band dropped from four stats to three when
   the two totals were replaced by averages, and a hard repeat(4,1fr) left an
   empty lit panel hanging off the right end. */
.istats{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:1px;
  background:var(--border);border:1px solid var(--border);border-radius:0;overflow:hidden}
.istat{background:var(--bg-card);padding:2.25rem 1.5rem;display:flex;flex-direction:column;gap:.6rem;
  transition:background var(--hov-out) var(--hov-ease)}
.istat:hover{background:var(--bg-raised);
  transition:background var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.istat .mnum{font-size:clamp(2.1rem,3.6vw,2.9rem)}
.istat-label{font-family:var(--f-sans);font-size:.9375rem;line-height:1.45;color:var(--text-body);max-width:26ch}
.istat-src{font-family:var(--f-label);font-size:11px;text-transform:uppercase;
  letter-spacing:var(--tr-caps);color:var(--text-faint);margin-top:auto;padding-top:.4rem}
/* Method note under the portfolio stats. Small and quiet, but full body measure
   and body colour rather than caption grey — it is load-bearing (it states n,
   the window and the exclusions), so it has to be readable, not decorative. */
.istats-method{margin-top:1.75rem;max-width:74ch;font-size:.875rem;
  line-height:1.6;color:var(--text-muted)}
/* The note is the same muted grey throughout, so a link inside it needs the
   accent AND the underline to read as clickable rather than as more footnote.
   The homepage AEO band relies on this: its note is the only route from a
   large percentage to the page that qualifies it. */
.istats-method a{color:var(--accent);text-decoration:underline;text-underline-offset:2px}
.istats-method a:hover{color:var(--accent-hover)}
@media(max-width:900px){.istats{grid-template-columns:repeat(2,1fr)}}
@media(max-width:520px){.istats{grid-template-columns:1fr}}

/* ---- BAND: client showcase (who we've built for) ---------------------- */
.cshow{display:grid;grid-template-columns:repeat(3,1fr);gap:1.25rem}
@media(max-width:1000px){.cshow{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.cshow{grid-template-columns:1fr}}
.ccard{background:var(--bg-card);border-radius:0;padding:1.5rem;
  box-shadow:var(--shadow-card);display:flex;flex-direction:column;gap:1rem;
  transition:box-shadow var(--hov-out) var(--hov-ease),background var(--hov-out) var(--hov-ease);
  text-decoration:none;color:inherit}
.ccard:hover{background:var(--bg-raised);
  box-shadow:inset 0 0 0 1px var(--border-hover);
  transition:box-shadow var(--hov-in) var(--hov-ease) var(--hov-in-delay),
             background var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.ccard-top{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem}
.ccard-domain{font-family:var(--f-mono);font-size:var(--fs-mono-sm);color:var(--text);word-break:break-all}
.ccard-sector{font-family:var(--f-label);font-size:10px;text-transform:uppercase;
  letter-spacing:var(--tr-caps);color:var(--text-faint);white-space:nowrap}
.ccard-head{font-family:var(--f-sans);font-size:1.375rem;font-weight:800;letter-spacing:-.03em;
  line-height:1.15;color:var(--text)}
.ccard-head em{font-family:var(--f-accent);font-style:italic;font-size:.94em;color:inherit}
/* The chart is the proof; it gets the accent, the copy does not. */
.ccard-chart{height:64px;width:100%;display:block}
.ccard-chart .cl{fill:none;stroke:var(--accent);stroke-width:1.75;
  stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:var(--len,400);stroke-dashoffset:var(--len,400)}
.ccard.visible .ccard-chart .cl{animation:drawLine 1.5s var(--hov-ease) .15s forwards}
@keyframes drawLine{to{stroke-dashoffset:0}}
.ccard-chart .ca{fill:url(#ccGrad);opacity:0;transition:opacity .6s ease .9s}
.ccard.visible .ccard-chart .ca{opacity:1}
.ccard-metrics{display:flex;gap:1.5rem;border-top:1px solid var(--hairline);padding-top:.875rem;margin-top:auto}
.ccard-metric .mnum{font-size:1.25rem}
.ccard-metric span{display:block;font-family:var(--f-label);font-size:10px;text-transform:uppercase;
  letter-spacing:var(--tr-caps);color:var(--text-faint);margin-top:.25rem}
.ccard-window{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--text-faint)}

/* ---- BAND: industries we service --------------------------------------
   auto-FIT, not auto-fill: auto-fill keeps empty tracks, and because the grid
   paints --border behind a 1px gap those empties rendered as blank cells in a
   slightly different tone — a row of four items looked like a row of five with
   one missing. auto-fit collapses them so every row is full by construction.

   ── auto-fit only fixed HALF of this ────────────────────────────────────────
   auto-fit collapses a track only when that track is empty down the WHOLE
   grid. It does nothing for a partial LAST ROW, which is the common case here:
   the band renders three groups of 5, 4 and 7 verticals into a 6-column grid,
   so the 7-item group put one item in row 2 and left five cells with no item
   in them. Those cells are not "empty space" — the container's --border
   background paints straight through them, so the last row rendered as a bar
   of grey filler boxes after the final trade.

   The hairline grid is now drawn by an OUTLINE on each item instead of by a
   background bleeding through a gap. Outlines are painted outside the border
   box and take no layout space, so adjacent items' outlines land on top of one
   another in the 1px gutter and read as a single shared rule — the same look
   the gap produced. Where there is no item there is now no paint, so a partial
   row is simply blank page, which is what it should always have been.

   The container keeps its 1px border and overflow:hidden, which clips the
   outer edge of the outer items' outlines so the frame stays one line thick.
   :focus-visible below deliberately overrides this outline — a focus ring
   replacing the hairline on the focused item is the intended behaviour. */
/* ── FLEX, not grid, and this is the whole reason ───────────────────────────
   A grid has fixed column tracks, so a group whose item count is not a
   multiple of the column count leaves real, paintable cells with nothing in
   them. That is unavoidable in grid without knowing the column count, and the
   column count here is responsive. The band renders three groups of 5, 4 and 7
   into what resolves to 6 columns at desktop width, so the home-services group
   put "Salons & Spas" alone on row 2 and left five empty cells beside it.

   Flex has no columns. Items wrap, and `flex:1 1 190px` lets whatever lands on
   the final row stretch to fill it. There is no empty cell to paint at any
   width, for any number of items, so this cannot regress when a 17th vertical
   is added to vertical-data.php. 190px stays the wrap threshold, so the
   breakpoint behaviour is unchanged from the grid version.

   The hairlines are drawn by an OUTLINE on each item rather than by the
   container background showing through the 1px gap — outlines paint outside
   the border box without taking layout space, so neighbours' outlines overlap
   in the gutter and read as one shared rule. With the old background-bleed
   approach an empty cell showed --border directly, which is what made the
   filler cells grey rather than merely empty.

   :focus-visible below intentionally overrides this outline. */
.iserve{display:flex;flex-wrap:wrap;gap:1px;
  background:var(--bg-card);border:1px solid var(--border);border-radius:0;overflow:hidden}
.iserve a{flex:1 1 190px;background:var(--bg-card);padding:1.15rem 1.25rem;text-decoration:none;
  outline:1px solid var(--border);
  display:flex;align-items:center;justify-content:space-between;gap:.75rem;
  transition:background var(--hov-out) var(--hov-ease),color var(--hov-out) var(--hov-ease)}
.iserve a:hover{background:var(--bg-raised);
  transition:background var(--hov-in) var(--hov-ease) var(--hov-in-delay),
             color var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.iserve-name{font-family:var(--f-sans);font-size:var(--fs-mid);font-weight:400;
  letter-spacing:-.008em;color:var(--text)}
/* Arrow rather than a repeated figure. Every vertical covers the same 100
   cities, so printing "100 cities" fifteen times said nothing and read as
   noise; the count lives once, in the band intro. */
.iserve-go{font-family:var(--f-mono);font-size:var(--fs-mono-xs);color:var(--text-faint);
  opacity:0;transform:translateX(-4px);
  transition:opacity var(--hov-out) var(--hov-ease),transform var(--hov-out) var(--hov-ease),color var(--hov-out) var(--hov-ease)}
.iserve a:hover .iserve-go{opacity:1;transform:none;color:var(--brand);
  transition:opacity var(--hov-in) var(--hov-ease) var(--hov-in-delay),
             transform var(--hov-in) var(--hov-ease) var(--hov-in-delay),
             color var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
/* Keyboard users get the same affordance the cursor gets. */
.iserve a:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.iserve a:focus-visible .iserve-go{opacity:1;transform:none;color:var(--brand)}

/* ---- Reduced motion ----------------------------------------------------
   Everything above degrades to its resting state. The pulse and the sheen
   stop entirely; the reveals land visible rather than animating in. */

/* ============================================================
   v8 MOTION — six patterns, one vocabulary
   ------------------------------------------------------------
   The problem this replaces: 76 named keyframes, 44 of them running
   `infinite`, and 21 separate prefers-reduced-motion blocks. Every band had
   invented its own entrance (heroFade, fadeStep, aoviz-rowin, al-in, cb-in,
   ch-row-in, iqFade, quizFade, wrFade, gs-rise, cm-in-slide...) and its own
   glow (sheenPass, bguFlash, hbvPing, proofPulse, pulseRing, ao-pulse...).
   None of them disagreed about much, which is the point — 20 near-identical
   fades read as busywork, not craft. Modern motion is fewer gestures applied
   consistently, so this is the whole vocabulary and everything maps onto it.

   The six:
     1 rise    entrance. The universal one.
     2 settle  staggered entrance for a group.
     3 ripple  "something shipped". The brand gesture. Rare on purpose.
     4 ledger  the diff reveal. The one narrative animation.
     5 tick    count-up on a headline metric.
     6 sheen   hover sweep across a card.

   Things that are NOT in here, deliberately: no infinite ambient loops, no
   scroll-linked transforms on text, no bounce on anything a cursor touches.
   ============================================================ */
:root{
  --dur-1:120ms;   /* micro: hover, focus, tap                         */
  --dur-2:220ms;   /* state: toggle, tab, accordion, dropdown          */
  --dur-3:420ms;   /* entrance: card, section                         */
  --dur-4:700ms;   /* narrative: hero, diff, count-up                  */
  /* --ease-out already exists as --hov-ease; aliased rather than
     redeclared so the two can never drift to different curves. */
  --ease-out:var(--hov-ease);
  --ease-in-out:cubic-bezier(.65,0,.35,1);
  --ease-spring:cubic-bezier(.34,1.56,.64,1); /* apply/success only */
}

/* ---- 1 · RISE — the universal entrance --------------------------------
   Fires once, via the shared IntersectionObserver in footer.php at 15%
   visibility, and never re-fires on scroll-back. Replaces every bespoke
   fade-in on the site. */
.rise{opacity:0;transform:translateY(16px);
  transition:opacity var(--dur-3) var(--ease-out),transform var(--dur-3) var(--ease-out)}
.rise.visible,.rise.is-in{opacity:1;transform:none}

/* ---- 2 · SETTLE — stagger for groups ----------------------------------
   60ms increment, CAPPED AT SIX. Child seven onward shares child six's delay:
   an uncapped stagger on a 15-card grid means the last card lands 900ms after
   the first, which reads as the page being slow rather than as choreography. */
.settle>*{opacity:0;transform:translateY(16px);
  transition:opacity var(--dur-3) var(--ease-out),transform var(--dur-3) var(--ease-out)}
.settle.visible>*,.settle.is-in>*{opacity:1;transform:none}
.settle.visible>*:nth-child(1){transition-delay:0ms}
.settle.visible>*:nth-child(2){transition-delay:60ms}
.settle.visible>*:nth-child(3){transition-delay:120ms}
.settle.visible>*:nth-child(4){transition-delay:180ms}
.settle.visible>*:nth-child(5){transition-delay:240ms}
.settle.visible>*:nth-child(n+6){transition-delay:300ms}

/* ---- 3 · RIPPLE — the brand gesture -----------------------------------
   A soft coral ring expanding from the element. Reserved for exactly one
   meaning: SOMETHING SHIPPED. Apply clicked, change published, form
   submitted. It is rare, which is the only reason it lands. Do not reach for
   it as a hover or an idle pulse — that is what the old glow family was. */
.ripple{position:relative}
.ripple::after{content:'';position:absolute;inset:0;border-radius:0;
  pointer-events:none;opacity:0;
  box-shadow:0 0 0 0 var(--brand-glow)}
.ripple.is-shipped::after{animation:ao-ripple var(--dur-4) var(--ease-out) 1}
@keyframes ao-ripple{
  from{opacity:.4;box-shadow:0 0 0 0 var(--brand-glow)}
  to{opacity:0;box-shadow:0 0 0 14px rgba(29, 78, 216,0)}}

/* ---- 4 · LEDGER — the diff reveal -------------------------------------
   The clearest demonstration of what askotter sells: a line struck through,
   the replacement typed in beneath, a timestamp. It used to be one of
   nineteen competing animations on the page; it is now the only narrative
   one, so it carries the hero, /platform and /web-builder. */
.ledger-old{position:relative;color:var(--text-muted)}
.ledger-old::after{content:'';position:absolute;left:0;top:50%;height:1px;width:0;
  background:var(--danger);
  transition:width var(--dur-3) var(--ease-out)}
.ledger.visible .ledger-old::after,.ledger.is-in .ledger-old::after{width:100%}
.ledger-new{overflow:hidden;white-space:nowrap;max-width:0;color:var(--accent);
  transition:max-width var(--dur-4) steps(40,end) var(--dur-3)}
.ledger.visible .ledger-new,.ledger.is-in .ledger-new{max-width:100%}
.ledger-stamp{opacity:0;font-family:var(--f-mono);font-size:var(--fs-mono-xs);
  color:var(--text-faint);
  transition:opacity var(--dur-2) var(--ease-out) calc(var(--dur-3) + var(--dur-4))}
.ledger.visible .ledger-stamp,.ledger.is-in .ledger-stamp{opacity:1}

/* ---- 5 · TICK — count-up on headline metrics --------------------------
   The count itself is driven by the existing site-wide observer in
   footer.php (data-count / data-prefix / data-suffix / data-decimals /
   data-comma). This class only supplies the typography guarantee: tabular
   numerals, so the layout does not jitter as digits change width. Max three
   per page. NOTE: do not add a second observer over these elements — the
   footer one already claims them and two observers double-animate. */
.tick{font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}

/* ---- 6 · SHEEN — hover sweep -----------------------------------------
   Replaces sheenPass, bguFlash, hbvPing, proofPulse and the rest of the glow
   family. Ink at low alpha on paper, swept once on hover. Pointer-fine only:
   on touch there is no hover, and firing it on tap reads as a flash. */
@media(hover:hover) and (pointer:fine){
  .sheen{position:relative;overflow:hidden;isolation:isolate}
  .sheen::after{content:'';position:absolute;inset:0;pointer-events:none;z-index:1;
    background:linear-gradient(105deg,transparent 38%,rgba(32, 30, 29,.055) 50%,transparent 62%);
    transform:translateX(-120%);opacity:0;
    transition:transform var(--dur-2) var(--ease-out),opacity var(--dur-2) var(--ease-out)}
  .sheen:hover::after{transform:translateX(120%);opacity:1}
}

/* ---- Orchestration rules ---------------------------------------------
   Enforced in CSS where CSS can enforce them.

   Off-screen pause. Every looping animation that survives must stop when it
   leaves the viewport — a paused offscreen animation costs nothing, a running
   one costs a composite every frame forever. footer.php adds .is-off to any
   element with [data-loop] once it exits. */
[data-loop].is-off,[data-loop].is-off *{animation-play-state:paused!important}

/* Budget: transform and opacity only. `top` was being animated at .18s in the
   nav dropdown, which lays out and paints on every frame; it is a transform
   now. will-change is applied on hover intent and dropped again on leave
   rather than being left on permanently, which is what turns it from a hint
   into a permanent extra compositor layer. */
.will-lift{will-change:auto}
.will-lift:hover{will-change:transform}

/* ============================================================
   REDUCED MOTION — the single block
   ------------------------------------------------------------
   This replaces 21 scattered blocks. They had drifted: some killed
   animation but left transitions running, some killed transforms but left
   opacity fading, several covered a component that had since been renamed.
   One global stop, then the handful of resting states that need asserting
   because their at-rest value is "invisible" rather than "still".
   ============================================================ */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    transition-delay:0ms!important;
    scroll-behavior:auto!important;
  }
  /* At-rest for these classes is opacity:0 — a global animation stop alone
     would leave the content permanently invisible rather than merely still. */
  .rise,.settle>*,.reveal,.reveal-stagger>*,.reveal-depth,
  .reveal-scale,.reveal-left,.reveal-right,
  .rise-in,.rise-in-stagger>*,.hero-enter,.hl-tile .hl-in,
  .aoviz-row,.fly,.fly-row>*{
    opacity:1!important;transform:none!important;filter:none!important}
  /* The fly-in hero stage is decorative depth, not content — the copy beside it
     says everything it says. Hiding it outright beats landing eight tiles in a
     pile, which is what "stop the animation" alone produces here. */
  .hero-layers{display:none!important}
  /* Resting width for a rule that animates from 0 — without this the underline
     under the /about-shift heading simply never draws. */
  .ai-shift h2::after{width:120px!important}
  .ledger-new{max-width:100%!important}
  .ledger-old::after{width:100%!important}
  .ledger-stamp{opacity:1!important}
  /* Purely decorative motion is removed outright rather than sped up, so it
     does not flash once on load. */
  .sheen::after,.ripple::after,.pulse-dot::after{display:none!important}
  .hero-aurora,.hero-grid3d,.hero-orb{animation:none!important}
  .ccard-chart .cl{stroke-dashoffset:0!important}
  .ccard-chart .ca{opacity:1!important}
}

/* ============================================================
   v8 COMPONENTS — one set, not per-section variants
   ------------------------------------------------------------
   Scope note, because this layer is deliberately narrower than "rewrite every
   component": the site has ~40 templates and eight card treatments that have
   drifted apart. Replacing all of them blind is a large regression surface for
   little visible gain, so what this layer does instead is:
     · define the ONE canonical button / form / card / data geometry, so new
       work has something to reach for and old work can be migrated in place;
     · align the existing primitives (.btn-p, .btn-s, .fi, .fl) to it, which
       moves every page at once without touching a single template;
     · fix the defects that are real bugs rather than inconsistencies.
   Anything still using a bespoke variant keeps working; it is now the odd one
   out rather than one of eight equals.
   ============================================================ */

/* ---- Visually-hidden ---------------------------------------------------
   Referenced by markup (search-field labels, icon-only controls) but never
   defined, so every "hidden" label was rendering as visible text. The
   clip-path form rather than the old width:1px/clip:rect hack: it does not
   collapse the box, so a screen reader still gets the layout position. */
.sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

/* ---- Buttons: three tiers, no more ------------------------------------
   The brand finally fills something. Through v6 and v7 the rule was "solid
   fills are ink, never accent", which was correct while coral could not carry
   a label — at #1d4ed8 on paper, white text is 2.76:1. It still cannot carry
   white. It carries INK: #201e1d on #1d4ed8 measures 6.52:1, clearing AA.

   So the primary action is coral with a near-black label. That is the one
   place full-saturation --brand-vivid is allowed to appear as a large area,
   and it is what buys the 2%-of-the-page coral budget its meaning: if the
   only orange rectangle on screen is the thing to click, no one has to be
   taught what to click. */
.btn-p,.btn-s,.btn-t,.nav-cta{--btn-h:48px}
.btn-p{
  padding:0 24px;min-height:var(--btn-h);
  background:var(--brand-vivid);color:var(--ink);border:1px solid var(--brand-vivid);
  border-radius:0;
  box-shadow:var(--shadow-btn);
}
.btn-p:hover{background:var(--brand);border-color:var(--brand);box-shadow:var(--shadow-btn-hover)}
.btn-p:active{box-shadow:inset 0 1px 0 rgba(255,255,255,.06)}
/* The focus ring cannot be coral on a coral button — it would vanish. Ink,
   offset, so it reads against both the button and the page behind it. */
.btn-p:focus-visible{outline:2px solid var(--ink);outline-offset:2px}

.btn-s{
  padding:0 24px;min-height:var(--btn-h);
  background:transparent;color:var(--ink);
  border:1px solid var(--border);border-radius:0;
  box-shadow:none;
}
.btn-s:hover{border-color:var(--ink);background:transparent}
.btn-s:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

/* Tertiary is a text link with an arrow. It takes the DARKENED coral rung,
   never --brand: this is small text, and --brand is 3.29:1. */
.btn-t{color:var(--brand-text)}
.btn-t:focus-visible{outline:2px solid var(--brand);outline-offset:3px}

/* The nav CTA is not the page's primary action — the hero button is. It stays
   ink so the two do not compete, and so there is exactly one coral rectangle
   above the fold. */
.nav-cta{border-radius:0}

/* ---- Forms ------------------------------------------------------------
   52px controls, single column always, label above the field. The label was
   uppercase mono at 12px, which is a telemetry face doing a form's job; it is
   sentence-case now and a step larger, because a form label is read once,
   quickly, by someone deciding whether to bother. */
.fg{margin-bottom:1.25rem;max-width:100%}
.fl{display:block;font-family:var(--f-sans);font-size:var(--fs-small);
  letter-spacing:var(--tr-ui);text-transform:none;color:var(--text-muted);
  margin-bottom:.4rem;font-weight:400}
.fi{width:100%;min-height:52px;padding:14px 16px;
  background:var(--bg-card);border:1px solid var(--border);
  border-radius:0;color:var(--text);
  font-family:var(--f-sans);font-size:var(--fs-mid);
  transition:border-color var(--dur-2) var(--ease-out),box-shadow var(--dur-2) var(--ease-out);
  outline:none}
/* Focus is coral at 2px, which is the one indicator the whole site shares.
   --brand (not --brand-vivid) because 3.29:1 clears WCAG 1.4.11's 3:1 for a
   non-text indicator, and the vivid rung does not. */
.fi:focus,.fi:focus-visible{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.fi::placeholder{color:var(--text-faint)}
textarea.fi{resize:vertical;min-height:120px}
/* Inputs never sit side by side. Two-up fields are the single most common
   cause of a mis-tapped form on a phone, and none of these forms has a field
   pair that genuinely belongs on one line. */
.iq-row{display:grid;grid-template-columns:1fr;gap:0 1rem}

/* Multi-step: the first step renders even if the markup forgets .active.
   This is defence, not decoration — .form-step{display:none} plus markup that
   never set .active on step one is what shipped a form with no visible fields
   and no error, and the failure is silent because the submit button is outside
   the step. Now the worst case is that two steps show, which is obvious. */
.form-step:first-of-type{display:block}
.form-step{display:none;overflow:hidden}
.form-step.active{display:block}
.form-step:first-of-type:not(.active):has(~.form-step.active){display:none}

/* ---- Cards ------------------------------------------------------------
   One component, three variants. Resting elevation is a hairline plus the
   smallest shadow; hover deepens rather than lifts, which is the v6 rule that
   survived both surface inversions. Pair with .sheen for the sweep. */
.card{background:var(--bg-card);border:1px solid var(--hairline);
  border-radius:0;padding:1.5rem;
  box-shadow:var(--shadow-sm);
  transition:border-color var(--hov-out) var(--hov-ease),box-shadow var(--hov-out) var(--hov-ease)}
a.card:hover,.card--interactive:hover{border-color:var(--border);box-shadow:var(--shadow-md);
  transition:border-color var(--hov-in) var(--hov-ease) var(--hov-in-delay),
             box-shadow var(--hov-in) var(--hov-ease) var(--hov-in-delay)}
.card--flat{box-shadow:none;background:transparent}
.card--well{background:var(--bg-alt);border-color:transparent;box-shadow:none}

/* ---- Data display -----------------------------------------------------
   Where the mono face and the green live. Tabular numerals everywhere a
   figure can change, so nothing reflows as it counts. */
.data,.data-table{font-family:var(--f-mono);font-variant-numeric:tabular-nums}
.data-table{width:100%;border-collapse:collapse;font-size:var(--fs-mono-sm)}
.data-table th{text-align:left;font-weight:600;color:var(--text-muted);
  font-size:var(--fs-mono-xs);letter-spacing:var(--tr-caps);text-transform:uppercase;
  padding:.5rem .75rem;border-bottom:1px solid var(--border)}
.data-table td{padding:.6rem .75rem;border-bottom:1px solid var(--hairline);color:var(--text-body)}
.data-table tr:last-child td{border-bottom:none}
.delta-up{color:var(--signal-up,var(--success))}
.delta-down{color:var(--danger)}
.delta-hold{color:var(--warning)}

/* ---- Radius scale (brief §7), exposed as tokens ---------------------- */
:root{--r-sm:8px;--r-md:12px;--r-lg:20px;--r-xl:28px;--r-pill:999px}

/* ---- Measure cap ------------------------------------------------------
   Body copy was unconstrained on wide viewports, so a paragraph in a
   full-width band ran to ~130 characters at 1440px — roughly double the
   comfortable line. Capped where it is not already capped by a grid. */
.desc,.ssub,.hero-desc,.lead{max-width:68ch}

/* ============================================================
   v8 — DARK BANDS ON A LIGHT SITE
   ------------------------------------------------------------
   Four bands are still dark, and correctly so: .vband, .ai-shift, .cta--video
   and .smb-cta all lay type over real video of real businesses, and footage
   needs a legibility scrim regardless of what colour the page is. That is an
   imagery treatment, not a second theme — nothing in them is a UI surface.

   But they were inheriting the page's LIGHT text tokens, which meant near-black
   copy and #1e40af eyebrows painted onto a #201e1d scrim: the /web-builder
   eyebrow measured 3.86:1 and the band read as empty. These bands therefore
   carry their own token scope, the same way .wt does. It is the one place the
   inverted set still exists, it is four selectors long, and it is here rather
   than scattered through four includes so there is one place to check it.
   ============================================================ */
.vband,.ai-shift,.cta--video,.smb-cta,.section--ink{
  --text:#f8f4f4;--text-body:#bab6b6;--text-muted:#A29E94;--text-faint:#8E8A81;
  --ink:#f8f4f4;
  --hairline:rgba(247,246,242,.10);
  --border:rgba(247,246,242,.16);--border-hover:rgba(247,246,242,.30);
  /* Full-saturation coral and bright green are the readable rungs HERE, which
     is the exact inverse of the page. On #201e1d: coral 5.86:1, green 8.31:1. */
  --brand:#5b8cff;--brand-text:#8db4fc;
  --accent:#5b8cff;--accent-hover:#8db4fc;
  --success:#4ade80;--warning:#fbbf24;--danger:#fca5a5;
}
/* The eyebrow dot and any coral rule inside these bands follow the same flip. */
.vband .eyebrow::before,.ai-shift .eyebrow::before,
/* v9: dark-band eyebrow dots follow the light ones off coral. */
.cta--video .eyebrow::before,.smb-cta .eyebrow::before,.section--ink .eyebrow::before{background:#f3f2f2;box-shadow:0 0 0 3px rgba(251,251,247,.14)}

/* An 11px tab number is text, so it takes the darkened coral rung. --brand is
   3.29:1 — correct for the rule beneath the tab, not for the label on it. */
.cap-tab.is-active .cap-tab-n{color:var(--brand-text)}

/* ============================================================
   v8.1 EDITORIAL LAYER
   ------------------------------------------------------------
   The surface inversion and the typeface change what the site is made of.
   This changes how it is ARRANGED, which is the part that was still v6.

   Three moves, all structural, none requiring a markup change — which is the
   point: they land on ~40 templates at once instead of on whichever three
   pages someone got round to.
   ============================================================ */

/* ---- 1 · Section rhythm ------------------------------------------------
   --band-y was 5.5rem (88px) top and bottom on every band, at every width.
   Two problems: a fixed value means a 1440px screen gets the same air as a
   768px one, and 88px is a UI gap rather than an editorial one. Fluid, and
   roughly half again as large at the top end. */
:root{--band-y:clamp(4rem,7vw,8.5rem)}

/* ---- 2 · The band header becomes asymmetric ---------------------------
   This is the single most visible change in the layer. Every band on the site
   opened with a stacked eyebrow → h2 → one-line sub, all left-aligned in a
   60ch column: a shape that is tidy, symmetrical, and completely inert.

   It is a two-column split now — big headline left, supporting line right,
   bottoms aligned on the same baseline. That baseline alignment is what makes
   it read as typeset rather than merely floated, and it is the reason for
   `align-items:end` instead of the more obvious `start`.

   The headline is deliberately capped at 18ch so it WRAPS. A display face at
   48px doing three short lines is the artwork; the same face doing one long
   line is a banner. */
@media(min-width:900px){
  .band-head{
    max-width:none;
    display:grid;
    grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr);
    column-gap:clamp(2.5rem,6vw,6.5rem);
    align-items:end;
    margin-bottom:clamp(2.75rem,4.5vw,4.5rem);
  }
  .band-head .eyebrow{grid-column:1/-1}
  .band-head h2{grid-column:1;margin-bottom:0;max-width:18ch}
  .band-head .desc,.band-head .ssub{grid-column:2;max-width:46ch;padding-bottom:.4em}
  /* --wide opted out of the 60ch cap; under a grid that cap is gone anyway, so
     the modifier now means "let the headline run longer before wrapping". */
  .band-head--wide h2{max-width:24ch}
  /* A header with no supporting line has nothing for column two to hold. Let
     the headline take the full width rather than leaving a conspicuous hole. */
  .band-head:not(:has(.desc)):not(:has(.ssub)) h2{grid-column:1/-1;max-width:26ch}
}

/* ---- 3 · The hero gets its headline back ------------------------------
   The hero grid was 1.05fr / .95fr — near-even, which is what you choose when
   you do not want to decide. At 88px display the headline was wrapping to four
   lines inside a half-width column and losing every one of its line breaks.

   The copy column now leads and the console follows. The console does not need
   half the screen to read as a console; the headline does need room to be the
   first thing on the page. */
@media(min-width:1000px){
  .hero-grid{grid-template-columns:1.22fr .78fr;gap:clamp(2.5rem,4.5vw,5rem)}
  .hero h1{max-width:20ch}
}

/* ---- Photography slots ------------------------------------------------
   Stubbed at real dimensions so the layouts are BUILT to receive photography
   rather than retrofitted around it once the shoot happens. Drop an <img> in
   and the frame does not move. Until then it is an honest empty state, not a
   grey box pretending to be a picture. */
.photo{position:relative;overflow:hidden;border-radius:0;
  background:var(--bg-alt);border:1px solid var(--hairline)}
.photo::before{content:'';display:block;padding-top:var(--photo-ratio,66.6%)}
.photo>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.photo--portrait{--photo-ratio:125%}
.photo--wide{--photo-ratio:47%}
.photo:empty::after,.photo:not(:has(img))::after{
  content:attr(data-slot);position:absolute;inset:0;display:flex;
  align-items:center;justify-content:center;text-align:center;padding:1.5rem;
  font-family:var(--f-mono);font-size:var(--fs-mono-xs);letter-spacing:var(--tr-caps);
  text-transform:uppercase;color:var(--text-faint)}

/* ---- Homepage hero: the headline leads, full width ---------------------
   The .hb-grid split put the h1 inside a ~55% column and capped it at 49px, so
   the site's single most important line of type was the fourth-largest thing on
   its own page — smaller than the console mock beside it. At 88px it simply
   does not fit a half-width column, which is why the earlier attempt wrapped
   "We build" onto a line of its own.

   So the headline spans the full container and the split starts BELOW it.
   `display:contents` on .hb-head is what makes this a CSS-only change: it
   dissolves the wrapper so its eyebrow, h1 and tagline become direct grid
   items that can be placed individually. No markup moves, and every other page
   using .hb-grid is untouched because this is scoped to .hero-build.

   The console keeps its place beside the supporting copy, so nothing that was
   above the fold drops below it. */
@media(min-width:1000px){
  .hero-build .hb-grid{
    grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
    grid-template-rows:auto auto auto;
    align-items:start;
    row-gap:0}
  .hero-build .hb-head{display:contents}
  .hero-build .hb-head .eyebrow{grid-column:1/-1;grid-row:1}
  .hero-build .hb-head h1{
    grid-column:1/-1;grid-row:2;
    font-size:var(--fs-display);
    line-height:var(--lh-display);
    letter-spacing:var(--tr-display);
    max-width:22ch;
    margin-bottom:2.25rem}
  /* The forced break between the two sentences is what the size is FOR — at
     full width both halves fit their own line, which is the composition the
     copy was written for. */
  .hero-build .hb-head .hb-br{display:inline}
  /* v9: THE LEFT COLUMN IS ONE GRID ITEM, and the viz no longer spans.
     The tagline moved into .hb-rest in the markup (see the note there), so
     row 3 holds the entire left column against the viz. A spanning item hands
     its surplus height to every row it spans, which is what opened ~104px of
     dead air between the tagline and the CTA once the price block came out.
     One row, one item: the surplus now lands below the left column, where the
     proof rail closes the hero anyway and nobody can see it. */
  .hero-build .hb-rest .hero-tagline{max-width:52ch;margin-bottom:2rem}
  .hero-build .hb-viz{grid-column:2;grid-row:3;align-self:start}
  .hero-build .hb-rest{grid-column:1;grid-row:3;align-self:start}
}

/* ---- Band headers, retargeted -----------------------------------------
   The .band-head component exists and is used exactly ONCE in ~40 templates.
   The pattern actually in the markup, 239 times, is three bare siblings inside
   a .container: .eyebrow, then h2, then p.desc. Styling the component that
   nobody uses is how a redesign ends up invisible, so this targets the real
   shape via direct-child selectors, which keeps it off every h2 inside a card,
   a mockup or an accordion.

   The move is typographic rather than columnar. A two-column split needs a
   common grid parent, and the only common parent here is .container, which
   holds the entire band — grid-ing it would rearrange the content too. So the
   headline gets the weight instead: capped near 20ch so a display serif WRAPS
   into two or three lines and becomes the artwork of the band, with the
   supporting line stepped up in size and given real air beneath it. */
.section>.container>h2,
.section-alt>.container>h2,
.iq-section>.container>h2{
  max-width:20ch;
  margin-bottom:1.25rem}
/* The line under the headline is the second voice in the band, not a caption.
   It was --fs-body in a 60ch measure, i.e. indistinguishable from the body copy
   three inches below it. Bigger, lighter, shorter measure. */
.section>.container>h2+.desc,
.section-alt>.container>h2+.desc,
.iq-section>.container>h2+.desc,
.section>.container>h2+.ssub,
.section-alt>.container>h2+.ssub{
  font-size:var(--fs-lead);
  line-height:1.5;
  color:var(--text-muted);
  max-width:54ch;
  margin-bottom:clamp(2.5rem,4vw,4rem)}

/* ---- The headline accent is coral, everywhere --------------------------
   .hl marks the turn in a headline — the half that carries the idea. It was
   green in five separate places (index.php's own <style>, video-band.php,
   .cta--video) and coral in two others, which is the same drift the CTA labels
   had. Green is semantic on this site: a green thing means live, verified or
   healthy. A headline is none of those. One rule, coral, and the page-level
   definitions below it are overridden rather than hunted down one at a time. */
/* ---- v9: THE HEADLINE ACCENT IS WIDTH, NOT COLOUR ----------------------
   Kept the note above because the drift it describes was real and the fix
   (one rule, not seven) is still right. What changed is what the rule sets.

   Under the colour law, coral means "proposed — awaiting your decision". A
   headline is not awaiting a decision, so 19 coral headline phrases across the
   site were 19 counterexamples to the only rule the palette has. Deleting the
   accent entirely was the other option and it was worse: the turn in the
   sentence is real and worth marking.

   So the accent moves onto the axis the new display face brought with it. The
   phrase renders WIDER than the headline around it (128 against 116). It reads
   as emphasis at a glance, it costs no colour, and it is not a device any of
   the three comparables can copy without also buying a width axis.

   Colour is deliberately NOT set here. .hl inherits the headline's ink, which
   is what makes it legal. If a headline phrase ever genuinely is a pending
   change, it takes .is-proposed (below), not .hl. */
/* Width ALONE was not enough. Measured on the live band: 116% against 128% is
   a real difference the eye does not report as emphasis — the phrase just
   looks like more headline. Weight is the perceptible axis, so the accent
   takes both: widest the face goes (125) plus 700 against the headline's 500.
   Still no hue, which is the only thing the colour law actually requires. */
.hl{;font-weight:var(--wt-accent)}
/* The four dark video bands scrim white type over footage. Width still reads
   there, so they need no override — but they DO need the old coral rule gone,
   or the inherited declaration wins on specificity and the law breaks in the
   four loudest places on the site. */
.vband .hl,.ai-shift .hl,.cta--video .hl,.smb-cta .hl{color:inherit;font-weight:var(--wt-accent)}

/* The one legal coral phrase: something the reader is being asked to accept.
   Rare by construction — if it is not a pending change, it is not this. */
.is-proposed{color:var(--brand-text)}

/* ---- Band headers stay left-aligned, even inside .tc -------------------
   17 bands wrap their content in `.container tc`, which centres the eyebrow,
   the headline and the supporting line. Centring is the one thing the band
   system has said not to do since v6: content bands are left-aligned and only
   the closing CTA centres. It also fought the eyebrow, which is display:flex
   and therefore ignored text-align, leaving the dot hanging left of a centred
   heading. The cards and grids BELOW the header keep whatever alignment they
   had — this only reclaims the header. */
.section>.container.tc>.eyebrow,
.section-alt>.container.tc>.eyebrow{justify-content:flex-start}
.section>.container.tc>h2,
.section-alt>.container.tc>h2,
.section>.container.tc>.desc,
.section-alt>.container.tc>.desc,
.section>.container.tc>.ssub,
.section-alt>.container.tc>.ssub,
.section>.container.tc>.eyebrow{text-align:left;margin-left:0;margin-right:0}

/* ---- The second heading tier ------------------------------------------
   A band can carry more than one heading. The programmatic city pages run
   three inside a single band: the band head, then "Using {service} to fix
   {industry} problems", then "What we report on {service}". All three were
   direct children of .container, so all three got the 20ch display cap above —
   and the lower two were pinned to font-size:1.5rem inline to keep them from
   competing. A display serif at 24px squeezed into a 20ch measure wraps into
   three stubby lines and reads as a heading that failed to load, which is
   exactly what it looked like.

   The fix is to give the tier a real definition instead of shrinking the first
   one. It steps down in size but UP in measure — a sub-head is a signpost, so
   it should sit on one or two lines, not become artwork. The top margin is what
   actually does the grouping work: it separates this heading from the block
   above far more than the type size does. */
.section>.container>h2.sh,
.section-alt>.container>h2.sh,
.iq-section>.container>h2.sh{
  font-size:clamp(1.375rem,1.05rem + .95vw,1.75rem);
  max-width:34ch;
  letter-spacing:var(--tr-h3);
  margin-top:clamp(3rem,5vw,4.5rem);
  margin-bottom:1.5rem}
/* Some of these sub-heads are introduced by their own .eyebrow. The eyebrow is
   then the top of the group, so it takes the separating margin and the heading
   gives it up — otherwise the gap opens BETWEEN the label and the heading it
   labels, which reads as two unrelated things. */
.section>.container>.eyebrow:has(+h2.sh):not(:first-child),
.section-alt>.container>.eyebrow:has(+h2.sh):not(:first-child),
.iq-section>.container>.eyebrow:has(+h2.sh):not(:first-child){
  margin-top:clamp(3rem,5vw,4.5rem)}
.section>.container>.eyebrow+h2.sh,
.section-alt>.container>.eyebrow+h2.sh,
.iq-section>.container>.eyebrow+h2.sh,
.section>.container>h2.sh:first-child,
.section-alt>.container>h2.sh:first-child{margin-top:0}

/* ---- Market band furniture (programmatic city pages) ------------------
   These three were inline styles repeated across the /{city} templates, each
   with its own max-width and its own idea of the spacing above it. Named here
   so the measure and the rhythm are set once. */
/* Body copy that sits directly in a .container rather than in a split column or
   a card. Without a measure it ran the full 1176px container — about 110
   characters a line, roughly double a readable one — which is why these bands
   looked like documentation rather than a page. */
.band-copy{max-width:70ch;font-size:var(--fs-body);color:var(--text-body);
  line-height:var(--lh-body);margin-bottom:2rem}
.band-copy:last-child{margin-bottom:0}
/* Card furniture: the source line under a card title, and the link that closes
   one. Both were inline styles repeated per card, one of them referencing a
   --font-mono variable this stylesheet does not define. */
.card-meta{font-family:var(--f-mono);font-size:.6875rem;letter-spacing:.04em;
  color:var(--text-muted);margin-bottom:.75rem}
.card-more{font-size:var(--fs-small);color:var(--accent);font-weight:600;
  transition:color var(--hov-out) var(--hov-ease)}
.card-more:hover{color:var(--accent-hover)}
.stat-grid--4{grid-template-columns:repeat(4,1fr)}
@media(max-width:900px){.stat-grid--4{grid-template-columns:repeat(2,1fr)}}
.mkt-tags{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:1rem}
.mkt-tags span{font-family:var(--f-mono);font-size:.6875rem;letter-spacing:.04em;
  padding:5px 11px;border:1px solid var(--border);border-radius:0;color:var(--text-faint)}
.prose-block{max-width:70ch;margin-top:clamp(2rem,3vw,2.75rem)}
.prose-block p{font-size:var(--fs-body);color:var(--text-body);line-height:var(--lh-body);margin-bottom:1.25rem}
.prose-block p:last-child{margin-bottom:0}
.prose-block strong{color:var(--text);font-weight:600}
/* The local proof point. Was a 3px accent bar; green is semantic on this site
   (live, verified, healthy) and a consumer-behaviour statistic is none of
   those, so it takes the coral the rest of the page's emphasis uses. */
.pull-stat{border-left:2px solid var(--ink);padding-left:1.5rem;max-width:64ch;
  margin-top:clamp(2rem,3vw,2.75rem)}
.pull-stat .pull-stat-h{font-size:var(--fs-mid);color:var(--text);font-weight:600;
  line-height:var(--lh-head);margin-bottom:.5rem}
.pull-stat p:last-child{font-size:var(--fs-small);color:var(--text-body);line-height:var(--lh-body);margin:0}

/* ---- FAQ disclosure list ---------------------------------------------
   Was eight inline-styled <details> with a default triangle marker. The marker
   is the only affordance these have, and the UA triangle sits outside the
   padding box at a size nobody chose. Replaced with a +/- that lines up with
   the text and flips on open. */
.faq-list{display:flex;flex-direction:column;gap:.5rem;max-width:78ch}
.faq-item{border:1px solid var(--border);border-radius:0;background:var(--bg-card);
  transition:border-color var(--hov-out) var(--hov-ease)}
.faq-item:hover{border-color:var(--border-hover)}
.faq-item[open]{border-color:var(--border-hover)}
.faq-item>summary{display:flex;align-items:flex-start;gap:.75rem;padding:1rem 1.25rem;
  font-size:var(--fs-mid);font-weight:600;color:var(--text);line-height:var(--lh-head);
  cursor:pointer;list-style:none}
.faq-item>summary::-webkit-details-marker{display:none}
.faq-item>summary::before{content:'+';flex:none;font-family:var(--f-mono);font-size:1rem;
  line-height:1.35;color:var(--text-faint);transition:transform .2s var(--hov-ease)}
.faq-item[open]>summary::before{content:'\2212'}
.faq-a{padding:0 1.25rem 1.25rem 3rem;color:var(--text-body);line-height:var(--lh-body);
  font-size:var(--fs-small);max-width:70ch}
@media(max-width:640px){.faq-a{padding-left:1.25rem}}

/* ── AEO short-answer block (includes/answer-block.php) ─────────────────────
   The attribute grid under the answer. Two columns on desktop so the labels
   read as a spec sheet, which is the shape the block is imitating; stacked
   under 640px, where a fixed label column left the values about 20 characters
   wide and every one of them wrapped five or six times. */
/* The short-answer spec table. A row-per-fact grid with hairline separators,
   which is how every other spec-shaped thing in v10 is drawn, and a fixed
   label column so the values line up down the page.
   dt is --f-label, not --f-mono: monospace is machine output in this system,
   and inside a prose answer it read as terminal output rather than as a label.
   Row rules go on the dd so the last one can be dropped without a nth-child
   count that breaks when a fact is added. */
/* NO column gap. The row rule is drawn by each cell's own border-bottom, so a
   column gap leaves a notch of bare ground between the label's rule and the
   value's rule and every row reads as two broken segments. The gutter is
   padding-right on the label instead, which the border spans. */
/* max-width goes on the LIST, not on the dd. Capping the value cell instead
   stopped each row rule at the end of its text while the 2px top rule ran the
   full container width, so the table looked ragged down its right edge. One
   measure on the grid ends every rule in the same place. */
.ao-answer-dl{display:grid;grid-template-columns:minmax(130px,150px) 1fr;max-width:62rem;
  gap:0;margin:2rem 0 0;padding-top:0;border-top:2px solid var(--rule)}
.ao-answer-dl dt{font-family:var(--f-label);font-size:.6875rem;font-weight:700;
  letter-spacing:var(--tr-caps);text-transform:uppercase;color:var(--text-faint);
  line-height:1.5;margin:0;padding:.95rem 1.75rem .95rem 0;border-bottom:1px solid var(--hairline)}
.ao-answer-dl dd{font-size:var(--fs-mid);color:var(--text-body);line-height:1.6;
  margin:0;padding:.95rem 0;border-bottom:1px solid var(--hairline)}
.ao-answer-dl dt:last-of-type,.ao-answer-dl dd:last-of-type{border-bottom:none}
@media(max-width:640px){
  /* One column on a phone: a 130px label column leaves the value about twenty
     characters wide, and the rule between rows is what keeps them readable. */
  .ao-answer-dl{grid-template-columns:1fr}
  .ao-answer-dl dt{padding:.9rem 0 .1rem;border-bottom:none}
  .ao-answer-dl dd{padding:0 0 .9rem}
}
@media(max-width:640px){
  .ao-answer-dl{grid-template-columns:1fr;gap:.25rem}
  .ao-answer-dl dt{margin-top:.85rem}
  .ao-answer-dl dt:first-of-type{margin-top:0}
}

/* ── COOKIE CONSENT (includes/cookie-consent.php) ───────────────────────────
   Fixed to the bottom edge, so it costs zero CLS — the banner never occupies
   layout space and never pushes the page down after paint. That matters more
   here than anywhere else on the site: this element renders on the FIRST
   pageview of every new visitor, which is exactly the pageview Core Web Vitals
   is measuring.

   z-index sits above the sticky nav (1000) and the top bar (1001) so the banner
   is never half-covered while scrolled, and below the skip link (2000), which
   must stay reachable first on tab.

   Accept and Reject are the SAME class — .cc-btn-p — on purpose. Making refusal
   visually quieter than acceptance is the most commonly enforced consent
   failure there is, so the equality is enforced in the stylesheet rather than
   left to whoever edits the markup next. Do not give Reject a ghost variant. */
.cc{position:fixed;left:0;right:0;bottom:0;z-index:1500;padding:0 1rem 1rem;
  opacity:0;transform:translateY(12px);
  transition:opacity var(--dur-2,220ms) var(--hov-ease,cubic-bezier(.22,1,.36,1)),
             transform var(--dur-2,220ms) var(--hov-ease,cubic-bezier(.22,1,.36,1))}
.cc.cc-in{opacity:1;transform:none}
.cc[hidden]{display:none}
.cc-inner{max-width:var(--content-width);margin:0 auto;background:var(--bg-card);
  border:1px solid var(--border);border-radius:0;
  box-shadow:var(--shadow-lg);padding:1.5rem 1.75rem;
  display:flex;flex-wrap:wrap;align-items:center;gap:1.25rem 2rem}
.cc-main{flex:1 1 34ch;min-width:0}
.cc-title{font-family:var(--f-sans);font-size:var(--fs-mid);font-weight:600;
  color:var(--text);margin:0 0 .35rem}
.cc-body{font-size:var(--fs-small);color:var(--text-body);line-height:var(--lh-body);
  margin:0;max-width:78ch}
.cc-body a{color:var(--accent);text-decoration:underline;text-underline-offset:2px}

/* The category list. Full width so it drops below the copy and buttons rather
   than squeezing them, and it only exists once "Manage" is pressed. */
.cc-prefs{flex:1 1 100%;order:3;display:flex;flex-direction:column;gap:.85rem;
  padding-top:1.25rem;border-top:1px solid var(--hairline)}
.cc-prefs[hidden]{display:none}
.cc-row{display:flex;align-items:flex-start;gap:.75rem;cursor:pointer}
.cc-row input{margin-top:.2rem;width:16px;height:16px;flex:none;accent-color:var(--ink);cursor:pointer}
.cc-row input:disabled{cursor:not-allowed;opacity:.55}
.cc-row span{display:block;min-width:0}
.cc-row strong{display:block;font-size:var(--fs-small);font-weight:600;color:var(--text)}
.cc-row em{display:block;font-style:normal;font-size:var(--fs-small);
  color:var(--text-muted);line-height:var(--lh-body);max-width:82ch}

.cc-acts{display:flex;flex-wrap:wrap;gap:.6rem;flex:none;order:2}
.cc-btn{font-family:var(--f-sans);font-size:var(--fs-small);font-weight:600;
  padding:10px 20px;border-radius:0;cursor:pointer;white-space:nowrap;
  border:1px solid transparent;
  transition:background-color var(--hov-out) var(--hov-ease),
             color var(--hov-out) var(--hov-ease),
             border-color var(--hov-out) var(--hov-ease)}
.cc-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.cc-btn-p{background:var(--ink);color:var(--bg);border-color:var(--ink)}
/* Was --accent (green). Green is reserved for measured reality, never for a
   thing you click — see the colour division of labour at the top of this file.
   The intensify direction follows the fill, same as .btn-p and .nav-cta. */
.cc-btn-p:hover{background:#000000;border-color:#000000}
.cc-btn-s{background:transparent;color:var(--text-muted);border-color:var(--border)}
.cc-btn-s:hover{color:var(--text);border-color:var(--border-hover)}
.cc-btn[hidden]{display:none}

@media(max-width:820px){
  .cc-inner{padding:1.25rem;gap:1rem}
  .cc-acts{flex:1 1 100%}
  /* Equal-width buttons rather than shrink-to-fit: at this width "Accept all"
     and "Reject all" would otherwise differ in size by a few pixels of glyph
     width, and on a consent control that reads as a nudge. */
  .cc-btn{flex:1 1 auto;text-align:center}
}

/* ── GLOSSARY INLINE CTA ───────────────────────────────────────────────────
   Replaces the "Get a free audit" link that closed every glossary page with an
   in-place capture, so a reader converts without a page transition. Two
   columns on desktop, stacked on phones. See includes/glossary-cta.php. */
.gl-cta{display:grid;grid-template-columns:1.1fr .9fr;gap:2.5rem;align-items:start;
  border:1px solid var(--border);border-left:2px solid var(--accent);
  background:var(--bg-card);padding:2.5rem}
.gl-cta-h{font-size:var(--fs-h3,1.5rem);margin:.35rem 0 .75rem;max-width:22ch}
.gl-cta-p{font-size:var(--fs-body);color:var(--text-body);line-height:var(--lh-body);margin:0;max-width:52ch}
.gl-cta-form .fg{margin-bottom:1rem}
.gl-cta-btn{width:100%;justify-content:center}
.gl-cta-fine{font-size:var(--fs-mono-xs,11px);font-family:var(--f-label);
  letter-spacing:var(--tr-caps);color:var(--text-faint);margin:.75rem 0 0;text-align:center}
.gl-cta-msg{font-size:var(--fs-small);color:var(--accent);margin:1rem 0 0;line-height:1.5}
.gl-cta-msg.is-err{color:var(--danger)}
.gl-cta-msg[hidden]{display:none}
@media(max-width:860px){
  .gl-cta{grid-template-columns:1fr;gap:1.75rem;padding:1.75rem}
  .gl-cta-h{max-width:none}
}

/* ── SEMANTIC LEVEL, VISUAL WEIGHT ─────────────────────────────────────────
   Sidebar cards on /contact and /get-started ("What to expect", "Who we build
   for", "Reach us directly", "Already spoken with us?") were marked up as h3
   under an h1, with no h2 between them. Nothing nests under those cards — each
   is a top-level section of the page — so the level was simply wrong, and the
   h1→h3 jump is a real failure for anyone navigating by heading rather than by
   eye: a screen reader announces a missing level and the outline implies a
   parent section that does not exist.

   They cannot just become h2 elements, because h2 here is the display face at
   --fs-h2 and that would turn a 2rem sidebar card into a billboard. This class
   carries h3's typography on an element that carries h2's level. Use it
   anywhere the outline needs a level the layout cannot afford to show. */
h2.as-h3{font-family:var(--f-display);font-size:var(--fs-h3);font-weight:800;
  line-height:1.43;margin-bottom:.4rem;color:var(--ink);letter-spacing:var(--tr-h3)}

/* ── reCAPTCHA badge vs the Growth Score launcher ──────────────────────────
   Google's floating badge is position:fixed at bottom:14px right:-186px, with
   a ~70px tab left on screen. The Growth Score launcher is fixed at bottom:20px
   right:20px and is up to 160px wide. Measured on /contact at 1440x900 the two
   overlapped outright — badge x1370-1440 y826-886 against launcher x1260-1420
   y833-880 — so the reCAPTCHA tab sat under the pill, and on the pages where
   the key rejects the host its red error text bled out from behind it.

   Lifting the badge to bottom:84px clears the launcher (whose top edge is 67px
   up at desktop, 59px on a phone) on all four pages that load api.js:
   /contact, /get-started, /assessment, /onboarding.

   Moved rather than hidden on purpose. Hiding the badge is permitted only if
   the reCAPTCHA attribution is reproduced next to every submit button, and
   four forms that each have to remember to carry a legal line is a worse
   failure mode than a badge sitting 64px higher. */
.grecaptcha-badge{bottom:84px!important}

/* ═══════════════════════════════════════════════════════════════════════════
   v10 "MODERNIST" — ENFORCEMENT LAYER
   2026-08-16. Everything above this line is v6→v9 component CSS: ~3,300 lines
   describing a system with soft corners, drop shadows, a raised white card
   surface, three type families and a green. This block retrofits all of it to
   the flat architectural system rather than rewriting it rule by rule.

   WHY A LAYER AND NOT A REWRITE. The component CSS above encodes several
   hundred decisions that are still correct — the asymmetric hover timing, the
   focus baseline, the mega-menu geometry, the reduced-motion pairing with 21
   PHP files. A rewrite would have to re-derive all of it to change four
   things: corner, elevation, rule weight and hue. Those four are exactly what
   a cascade layer is good at. Read this block as "the v10 diff".

   RULES FOR EDITING THIS BLOCK:
   1. Nothing here may introduce a second chromatic hue.
   2. `!important` appears only where an inline style in a .php template is
      being overridden — 38 templates carry <style> blocks and inline
      attributes that outrank a stylesheet. That trap has bitten this codebase
      at every system change (v9 shipped three coral .hl overrides that
      survived the central change). Where you see !important here, the comment
      says which file it is fighting.
   3. If you add a component, add it to the SECTION MAP below.

   SECTION MAP
     1  Ground, type and the flush-left law
     2  Bands: the 2px rule and the alternating surface
     3  Frames: the bordered grid, cells, and corner flags
     4  Buttons, tags, pills
     5  Kickers, labels, fine print
     6  Surfaces that used to be raised: cards, panels, consoles
     7  Data: tables, stats, diffs
     8  The poster band (the one full-red section per page)
     9  Chrome: ticker, nav, footer
    10  Media
    11  Motion
    12  No gradients
    13  Trust marks
    14  Flush left, finished
    15  Contrast corrections
    16  Typography, made consistent
    17  v10.1 — the status-chip sweep
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · GROUND, TYPE, AND THE FLUSH-LEFT LAW ────────────────────────────── */

body{
  font-family:var(--f-sans);
  background:var(--bg);
  color:var(--text);
  font-size:15px;
  line-height:1.55;
  font-weight:400;
  letter-spacing:0;
}

/* Weight 800 is the display voice now, and font-stretch is removed rather than
   set to 100%: the width axis is no longer in the font request, and a declared
   font-stretch on a face without a wdth axis is at best ignored and at worst
   synthesised into a smear by the fallback. */
h1,h2,h3,h4,h5,h6{
  font-family:var(--f-display);
  font-weight:800;
  font-stretch:normal;
  font-optical-sizing:auto;
  color:var(--text);
}
h1{font-size:var(--fs-h1);line-height:1.0;letter-spacing:var(--tr-display)}
h2{font-size:var(--fs-h2);line-height:1.05;letter-spacing:var(--tr-h2)}
h3{font-size:var(--fs-h3);line-height:1.2;letter-spacing:var(--tr-h3);color:var(--text)}
h4{font-size:1.1875rem;line-height:1.25;letter-spacing:var(--tr-h3)}

/* The accent phrase inside a display headline. v9 made this width+weight so
   that colour could mean one thing; v10 has one colour and puts it back to
   red, which is the approved design's single most recognisable gesture (the
   hero reads "...Then it keeps GETTING BETTER." with the last phrase red).
   --brand rather than --brand-text on purpose: this only ever appears at
   display size, where 3.76 clears the 3:1 that size is held to.

   !important fights index.php, video-band.php and photo-band.php, which each
   carry their own inline `.hl{color:...}` — the exact three files that
   survived the v9 change and kept rendering the old hue for a week. */
.hl,.vband-h .hl,.pband-line .hl{
  color:var(--brand)!important;
  font-family:inherit;
  font-weight:800;
  font-stretch:normal;
  font-style:normal;
}
/* .ital was a serif-italic accent from v8.1. There is no second face to change
   voice into, so it collapses to the same red-and-heavy treatment rather than
   rendering as a synthesised oblique of the display face. */
.ital{font-family:inherit;font-style:normal;font-weight:800;color:var(--brand);letter-spacing:inherit}

/* FLUSH LEFT, EVERYWHERE. "Never center headings, copy, or button labels."
   Roughly 40 templates centre something — a band head, a CTA, a stat caption —
   and each was a local decision made under a system that allowed it. This is
   the one place the law is stated, so it is the one place to change it back.
   The exceptions are genuinely centred OBJECTS, not centred text: a cell in a
   flex row, an icon in its box, the modal. Those keep their alignment because
   they align a box, not a line of type. */
.section,.section-alt,.page-hero,.cta,.band,.band-head,.band-head-row,
.hero,.hero-sub,.hero-desc,.desc,.ssub,.eyebrow,.stat,.sc,.card{
  text-align:left;
}
.cta h2,.cta h3,.cta .desc,.cta .ssub,.cta p,
.section h2,.section-alt h2,.page-hero h1,.band-head h2{
  text-align:left;
  margin-left:0;
  margin-right:0;
}
.cta .btn-row,.cta .hero-actions,.btn-row{justify-content:flex-start}
.eyebrow.eyebrow--center,.eyebrow-center{justify-content:flex-start;text-align:left}

/* Body copy sits on a 520–720px measure, per the source. */
p{margin:0 0 .75rem;line-height:var(--lh-body)}
.desc,.hero-sub,.hero-desc,.ssub,.band-copy{
  font-size:1rem;line-height:1.65;color:var(--text-body);max-width:660px;
}
.page-hero .hero-sub,.page-hero .hero-desc,.page-hero .desc{max-width:520px}

a{color:inherit;text-decoration:none}
a:hover{color:var(--brand-text)}


/* ── 2 · BANDS: THE 2px RULE AND THE ALTERNATING SURFACE ─────────────────── */

/* The single most system-defining rule in this block. Sections were separated
   by a 1px 10%-ink hairline in v8/v9 — a line designed to sit INTO the page.
   v10 wants the opposite: the rule is structure, it is meant to be seen, and
   at 2px/40% it reads as a printed rule rather than a soft divider.
   Do not "harmonise" this with --hairline. They are different objects; see the
   note on --rule vs --hairline in :root. */
.section{padding:var(--band-y) 0;background:var(--bg);border-bottom:var(--rule-w) solid var(--rule)}
.section-alt{padding:var(--band-y) 0;background:var(--bg-alt);border-bottom:var(--rule-w) solid var(--rule);border-top:none}
.band{padding:var(--band-y) 0;border-bottom:var(--rule-w) solid var(--rule)}
.band--muted,.band-alt{background:var(--bg-alt)}
.section:last-of-type,.section-alt:last-of-type{border-bottom:var(--rule-w) solid var(--rule)}
.page-hero{padding:5.5rem 0 4.5rem;background:var(--bg);border-bottom:var(--rule-w) solid var(--rule)}
.band--tight{padding:3rem 0}

/* The ink-filled band inverts the whole palette rather than only its text, so
   a card that lands inside one does not paint a light tint on near-black. */
.band--ink,.band-deep{background:var(--ink);color:var(--bg);border-bottom:var(--rule-w) solid var(--ink)}
.band--ink h1,.band--ink h2,.band--ink h3,.band-deep h1,.band-deep h2,.band-deep h3{color:var(--bg)}
.band--ink p,.band-deep p{color:var(--n-300)}

.container{max-width:var(--content-width);margin:0 auto;padding:0 48px}
@media(max-width:768px){
  .container{padding:0 24px}
  .section,.section-alt,.band{padding:3.5rem 0}
  .page-hero{padding:3rem 0 2.5rem}
}

/* The background texture is off at the token layer (--grid-line:transparent).
   This also removes the element from the paint path entirely. */
.grid-bg{display:none}


/* ── 3 · FRAMES: THE BORDERED GRID, CELLS, AND CORNER FLAGS ──────────────── */

/* The signature container of this system. "Multi-column content lives in
   framed grids: 2px outer border, 1px neutral-300 borders between equal cells.
   Always repeat(n, minmax(0,1fr)) so numbers never force overflow."

   minmax(0,1fr) rather than 1fr is load-bearing and not a nicety: a grid track
   defaults to min-content sizing, so a single long unbroken figure or URL in
   one cell widens that column and pushes the frame past its container. Every
   framed grid on this site carries count-up figures and URLs. */
.frame{
  display:grid;
  grid-template-columns:repeat(var(--frame-cols,3),minmax(0,1fr));
  border:var(--rule-w) solid var(--rule);
  background:var(--bg);
}
.frame--2{--frame-cols:2}
.frame--3{--frame-cols:3}
.frame--4{--frame-cols:4}
.frame>*{padding:36px 40px;border-right:1px solid var(--n-300);border-bottom:1px solid var(--n-300)}
/* Only the borders BETWEEN cells are drawn; the frame's own 2px edge is the
   outside, so the last cell in each row must not draw a right border or the
   frame renders a doubled edge.
   These are written out per column count rather than as
   `nth-child(var(--frame-cols)n)` — An+B in :nth-child() is parsed by the
   selector engine before custom properties are ever substituted, so a var()
   there makes the whole selector invalid and silently drops the rule. */
.frame>*:nth-child(3n){border-right:none}
.frame--2>*:nth-child(3n){border-right:1px solid var(--n-300)}
.frame--2>*:nth-child(2n){border-right:none}
.frame--4>*:nth-child(3n){border-right:1px solid var(--n-300)}
.frame--4>*:nth-child(4n){border-right:none}
.frame--flat>*{border-bottom:none}
.cell--tint{background:var(--bg-alt)}
@media(max-width:900px){.frame{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:620px){
  .frame{grid-template-columns:minmax(0,1fr)}
  .frame>*{border-right:none}
  .frame>*{padding:28px 24px}
}

/* Corner flag — "Most popular", "The gate". A red-filled square tab pinned to
   a cell's top-right. The cell it sits in must be position:relative; .cell--
   tint and .frame>* are the usual hosts, so relative is set on the cell rather
   than asked for at each call site. */
.frame>*,.flag-host{position:relative}
.flag{
  position:absolute;top:0;right:0;
  background:var(--brand);color:#fff;
  font-family:var(--f-label);font-size:10px;font-weight:800;
  text-transform:uppercase;letter-spacing:.06em;
  padding:4px 10px;
}


/* ── 4 · BUTTONS, TAGS, PILLS ────────────────────────────────────────────── */

/* Square, Archivo 800, 14px. Primary is a solid red fill with a white label;
   secondary is a 1px ink border on transparent. No shadow on either — the v8
   button carried a three-part shadow with an inset top highlight, which is a
   physical-lighting device and there is no light in this system.

   The fill is --btn-fill (accent-600), not --brand. See the DEVIATION note in
   :root: white on the raw accent is 4.20 and a 14px/800 label is not "large
   text", so the raw accent fails AA on the site's most-clicked element. */
.btn-p,.nav-cta,.btn-primary{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:13px 22px;
  background:var(--btn-fill);color:#fff;
  border:1px solid var(--btn-fill);
  border-radius:0;
  font-family:var(--f-display);font-size:14px;font-weight:800;
  letter-spacing:-.005em;line-height:1.2;
  text-decoration:none;cursor:pointer;
  box-shadow:none;
  transition:background var(--dur-1) var(--ease-out),border-color var(--dur-1) var(--ease-out);
}
.btn-p:hover,.nav-cta:hover,.btn-primary:hover{background:var(--btn-fill-hover);border-color:var(--btn-fill-hover);color:#fff;box-shadow:none}
.btn-p:active,.nav-cta:active{background:var(--brand-deep);border-color:var(--brand-deep);box-shadow:none}

.btn-s,.btn-secondary{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:13px 22px;
  background:transparent;color:var(--text);
  border:1px solid var(--ink);
  border-radius:0;
  font-family:var(--f-display);font-size:14px;font-weight:800;
  letter-spacing:-.005em;line-height:1.2;
  text-decoration:none;cursor:pointer;
  box-shadow:none;
  transition:background var(--dur-1) var(--ease-out),color var(--dur-1) var(--ease-out);
}
.btn-s:hover,.btn-secondary:hover{background:var(--ink);color:var(--bg);border-color:var(--ink)}

/* Wide buttons put their label flush left, per the source — a centred label in
   a full-width button is the one place this system would otherwise centre type. */
.btn-block,.btn-p.btn-block,.btn-s.btn-block{width:100%;justify-content:flex-start;text-align:left}

.btn-t{font-family:var(--f-display);font-weight:800;font-size:14px;color:var(--brand-text)}
.btn-t:hover{color:var(--brand-deep)}

/* Pills are square now, which makes them tags. Both names survive because
   ~200 call sites use one or the other. */
.pill,.tag,.llm-badge,.chip{
  border-radius:0;
  font-family:var(--f-label);font-size:11px;font-weight:700;
  letter-spacing:.02em;
  padding:5px 11px;
  border:1px solid var(--n-300);
  background:transparent;color:var(--text-muted);
}
.pill.is-on,.tag-accent{background:var(--acc-100);border-color:var(--acc-300);color:var(--acc-800)}
.tag-neutral{background:var(--n-100);border-color:var(--n-300);color:var(--n-800)}
.tag-outline{background:transparent;border-color:var(--brand);color:var(--brand-text)}
.pill:hover,.llm-badge:hover{border-color:var(--ink);color:var(--text);background:transparent}

:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:0}
:where(a,button,input,select,textarea,summary,[tabindex]):focus-visible{
  outline:2px solid var(--brand);outline-offset:2px;border-radius:0}

/* SELECTION. "Selection: red at 30%." This was specified from v6 onward and
   never once implemented — there was no ::selection rule anywhere in this file
   or in any of the 107 templates, so every visitor who dragged across a
   headline got the browser's default BLUE. It was, by some distance, the most
   widely rendered second colour on the site: one hue, every page, including
   the pages that are otherwise exactly right.

   Two rules, not one. The first is the page. The second is the poster band and
   the ink bands, where the ground is already the accent or already ink — a 30%
   red veil on #1d4ed8 is invisible, so selection there inverts to paper and
   lets the ground show the selection instead. */
::selection{background:rgba(29,78,216,.3);color:var(--ink)}
.cta ::selection,.smb-cta ::selection,.lp-final-cta ::selection,
.band--ink ::selection,.band-deep ::selection,
.cta::selection,.band--ink::selection{
  background:rgba(248,244,244,.35);color:#fff;
}


/* ── 5 · KICKERS, LABELS, FINE PRINT ─────────────────────────────────────── */

/* The kicker is the most repeated component on the site: 269 instances across
   37 templates. Two changes. It loses the leading dot — the approved design
   uses typographic marks only and the reference kicker is plain type — and it
   goes red, because a red uppercase label above a headline is this system's
   way of opening a band. --brand-text (6.41), never --brand: this is 12px. */
.eyebrow{
  display:block;
  font-family:var(--f-label);
  font-size:12px;font-weight:700;
  text-transform:uppercase;
  letter-spacing:var(--tr-caps);
  color:var(--brand-text);
  margin-bottom:20px;
  text-align:left;
}
.eyebrow::before{display:none!important}

.band-head{max-width:720px;margin-bottom:56px}
.band-head--wide{max-width:900px}

/* Fine print — source lines, disclaimers, "results vary". --text-faint is the
   AA-corrected rung (4.64); see the DEVIATION note in :root. */
.fine,.fineprint,.source,.caption,figcaption,.stat-src{
  font-size:11px;color:var(--text-faint);letter-spacing:0;
}

/* Uppercase micro-label — column headers, trust rows, plan fine print. */
.ulabel,.table th,.col-head{
  font-size:10px;font-weight:700;text-transform:uppercase;
  letter-spacing:.08em;color:var(--text-faint);
}


/* ── 6 · SURFACES THAT USED TO BE RAISED ─────────────────────────────────── */

/* "No box shadows on cards — borders do the work." The shadow tokens already
   resolve to none, but ~89 inline box-shadow declarations and ~95 literal
   rules above do not go through a token, so this is the blanket. Cards also
   lose their white fill: there is no third surface, so a card is the tint. */
.card,.sc,.pf,.panel,.tile,.box,.terminal,.dash,.aoviz,.viz{
  border-radius:0;
  box-shadow:none;
  background:var(--bg-alt);
  border:1px solid var(--n-300);
}
.card:hover,.sc:hover,.pf:hover{box-shadow:none;border-color:var(--ink)}
/* .card--well is declared ~850 lines above this blanket and sets
   border-color:transparent. The blanket then re-specifies the full `border`
   shorthand at equal specificity and later in the file, so it won and every
   "well" on the site rendered as an ordinary bordered card. Restated here, on
   the winning side of the cascade. A well is a recessed surface, which means
   fill and no edge. */
.card--well{background:var(--bg-alt);border-color:transparent;box-shadow:none}
.card--well:hover{border-color:transparent}

/* Corners are zeroed at the SOURCE, not with a blanket. A `*{border-radius:0}`
   rule was tried and removed: the universal selector has zero specificity, so
   it loses to all ~160 class rules above it and to every one of the 455
   inline `border-radius:` declarations in the .php templates — it would have
   looked like it worked while changing almost nothing. Adding !important to
   reach them would then have flattened the two objects that must stay round.
   The literal radii were rewritten in place instead (see the v10 sweep), so
   by the time the cascade gets here there is nothing left to override.

   These are the only round objects in the system: a portrait frame is a circle
   because it is a portrait frame, and a radio control's affordance IS its
   shape. */
.avatar,.avatar img,.headshot,.team-photo,.initials,.radio .dot,.dot--round{
  border-radius:50%;
}

/* Console / product-mock surfaces. These are framed panels in the new system:
   2px outer border, no radius, mono inside. */
.terminal,.portal-mock,.dash-chrome,.pw,.console{
  border:var(--rule-w) solid var(--rule);
  background:var(--bg);
  overflow:hidden;
}
.terminal-title,.dash-title{font-family:var(--f-mono);font-size:12.5px;color:var(--text-faint);letter-spacing:.04em}
code,pre,kbd,samp,.mono,.diff,.log{font-family:var(--f-mono);font-size:12.5px}


/* ── 7 · DATA: TABLES, STATS, DIFFS ──────────────────────────────────────── */

.table{width:100%;border-collapse:collapse;font-size:14px}
.table th{
  text-align:left;padding:12px 8px;
  border-bottom:var(--rule-w) solid var(--rule);
  font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  color:var(--text-faint);
}
.table td{padding:14px 8px;border-bottom:1px solid var(--n-200)}
.table tbody tr:hover{background:var(--bg-alt)}

/* Count-up figures. tabular-nums so a number does not jitter its own width
   while it animates — the .tick pattern from v8 relied on this and it is worth
   stating on the stat itself rather than only on the animation class. */
.stat,.stat-num,.mnum,.figure,.tick{
  font-family:var(--f-display);font-weight:800;
  font-size:clamp(1.875rem,3vw,2.75rem);
  letter-spacing:-.02em;line-height:1;
  font-variant-numeric:tabular-nums;
  color:var(--text);
}
/* Exactly one figure per stat strip carries the accent, per the source. It is
   opt-in — never make every number red. */
.stat--accent,.mnum--brand{color:var(--brand-text)}
.stat-cap,.stat-label{
  font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;
  color:var(--text-muted);margin-top:10px;
}

/* Diff surface. Removed lines are muted ink, added lines are the deep red.
   Never green — see the note on the deleted colour law in :root. */
.diff-del,.diff .del,.d-out{color:var(--text-faint)}
.diff-add,.diff .add,.d-in{color:var(--brand-text)}

/* Live/crawling indicator: a square red dot, 7–8px, pulsing 1 → .2. Square
   because nothing in this system is round; it is the one place a dot survives
   at all, and it survives because it means "this is happening now". */
.pulse-dot,.live-dot{
  display:inline-block;width:8px;height:8px;flex:none;
  background:var(--brand);border-radius:0;
  animation:okPulse 1.5s ease infinite;
}
@keyframes okPulse{0%,100%{opacity:1}50%{opacity:.2}}


/* ── 8 · THE POSTER BAND ─────────────────────────────────────────────────── */

/* The one full-red section per page. There are 29 .cta bands across the site
   and this makes every one of them the poster — which is correct, because
   there is exactly one per page and it is always the closing band.

   Text on this fill is the third measured deviation. White on #1d4ed8 is 4.20,
   which clears AA for LARGE text (3:1) and fails it for small. The headline is
   40–72px and the buttons are 15px/800 inside a solid white fill, so both are
   fine; the paragraph is the problem. It is set at 19px/600 here, which is
   above the 18.66px-bold threshold WCAG treats as large. Do not drop it back
   to 16px to match the reference — that is the one size at which this band
   fails. */
.cta,.smb-cta,.lp-final-cta{
  background:var(--brand);
  color:#fff;
  padding:120px 0;
  border-top:none;
  border-bottom:none;
  text-align:left;
}
.cta h2,.cta h3,.smb-cta h2{
  color:#fff;
  font-size:clamp(2.5rem,5vw,4.5rem);
  font-weight:800;
  letter-spacing:-.03em;
  line-height:.98;
  max-width:900px;
  margin:0 0 28px;
}
/* `.smb-cta p` was in this selector and should never have been. Everything this
   rule encodes — white type, 19px/700 — exists to survive white-on-#1d4ed8, and
   .smb-cta has no type on the red: its only paragraphs sit on the opaque paper
   card, where section 16 recolours them to --text-body anyway. What survived
   the recolour was the WEIGHT, so the card's supporting paragraph rendered at
   19px/700 dark-on-white, heavy enough to compete with the h3 above it, and the
   `margin:0 0 44px` outranked .smb-cta-tagline's own margins and pushed the
   tagline flush against the button with 44px of nothing under it. */
.cta p,.cta .desc,.cta .ssub{
  color:#fff;
  /* 19px/700, and BOTH numbers are load-bearing. White on #1d4ed8 measures
     4.20, which clears AA for "large" text (3:1) and fails it for small
     (4.5:1). WCAG's large threshold is 18.66px AT BOLD — 600 does not count as
     bold, so 19px/600 was still being held to 4.5 and still failing. 700 is
     what actually moves this paragraph into the bracket its contrast passes. */
  font-size:19px;font-weight:700;line-height:1.6;
  max-width:600px;margin:0 0 44px;
}
.cta .eyebrow{color:#fff;opacity:.85}
/* Inside the poster the palette inverts: the solid button is white with an ink
   label, the outline button is white-on-transparent. A red button on red is
   the failure mode this guards against. */
.cta .btn-p,.cta .btn-primary,.smb-cta .btn-p{
  background:#fff;border-color:#fff;color:var(--ink);
  padding:16px 28px;font-size:15px;
}
.cta .btn-p:hover,.smb-cta .btn-p:hover{background:var(--n-200);border-color:var(--n-200);color:var(--ink)}
.cta .btn-s,.cta .btn-secondary,.smb-cta .btn-s{
  background:transparent;border:2px solid #fff;color:#fff;
  padding:15px 27px;font-size:15px;
}
.cta .btn-s:hover,.smb-cta .btn-s:hover{background:var(--acc-700);border-color:#fff;color:#fff}
.cta a{color:#fff}
.cta .fine,.cta .fineprint{color:var(--acc-200)}


/* ── 9 · CHROME: TICKER, NAV, FOOTER ─────────────────────────────────────── */

/* THE TICKER IS BACK, AND THIS IS NOT A REVERSAL OF THE v8 DELETION.
   v8 removed a 60s marquee for two specific, documented faults: an end-fade
   mask that cut words mid-character at both edges, so the first type on every
   page read as a rendering fault; and a speed at which the strip could be
   glimpsed but never read. Both are properties of that implementation, not of
   marquees. This one has NO MASK — items are separated by 1px rules and simply
   run off the edge, which is how a printed ticker behaves — and runs at 38s
   with the list tripled, so any given item is legible for the several seconds
   it takes to cross.

   The v8 concern that does survive is the motion budget: this is an infinite
   animation above the fold on every page. It is therefore paused off-screen by
   the same [data-loop] observer footer.php already runs, and disabled outright
   under prefers-reduced-motion (see section 11). */
/* ── THE MARKET STRIP (v10.1) ─────────────────────────────────────────────────
   Replaces the marquee. See the long note in includes/header.php for why.
   Markets left, account right, nothing moving. overflow:hidden is GONE, which
   is what lets a second group sit beside the first again.

   The old marquee CSS deleted with it: .top-bar-values, .top-bar-sep,
   @keyframes okMarquee, and the .top-bar-util{display:none} that hid the
   account pair. The v6 rules for these still sit around line 349 and are
   harmless — they style elements the header no longer emits — but if you are
   ever cleaning this file, they go together. */
.top-bar{
  background:var(--bg);
  border-bottom:var(--rule-w) solid var(--rule);
  position:relative;
  padding:0;
  overflow:visible;
}
.top-bar-inner{
  max-width:var(--content-width);margin:0 auto;
  padding:0 48px;height:auto;
  display:flex;align-items:center;justify-content:space-between;gap:24px;
}
.tb-markets,.tb-account{
  display:flex;align-items:center;
  min-width:0;
  font-family:var(--f-label);
  font-size:11px;font-weight:700;
  text-transform:uppercase;letter-spacing:.07em;
  padding:9px 0;
}
.tb-markets{gap:0;flex-wrap:nowrap;overflow:hidden}
.tb-account{gap:0;flex:none}

/* "US · UK" is the only item that is a statement rather than a link, so it is
   the only one in ink. Everything beside it is navigation and sits one rung
   back until hovered. */
.tb-label{color:var(--text);white-space:nowrap;padding-right:16px;flex:none}
.tb-rule{width:1px;height:11px;background:var(--n-300);flex:none;margin-right:16px}

.tb-item{
  color:var(--text-muted);
  white-space:nowrap;
  padding:0 14px 0 0;
  margin-right:2px;
  transition:color var(--dur-1) var(--ease-out);
}
.tb-item:hover,.tb-item:focus-visible{color:var(--brand-text)}
/* The trial is the one thing in this strip anyone is looking FOR, so it is the
   one thing in it that is not grey. Type, not a button — there is exactly one
   filled rectangle in the header and it is the nav CTA. */
.tb-item--strong{color:var(--brand-text);padding-right:0}
.tb-item--strong:hover{color:var(--brand-deep)}
.tb-all{color:var(--text-faint);padding-right:0}

/* RESPONSIVE: the strip drops groups rather than shrinking type or scrolling.
   Order of sacrifice is the reverse of usefulness — the long tail link goes
   first, then industries, then metros. "US · UK" and the account pair survive
   to the narrowest width, because between them they are the two things the
   strip exists to say: where we work, and how you get in. */
@media(max-width:1240px){.tb-all{display:none}}
@media(max-width:1080px){.tb-ind,.tb-rule--ind{display:none}}
@media(max-width:820px){
  .top-bar-inner{padding:0 24px}
  .tb-geo,.tb-rule{display:none}
  .tb-label{padding-right:0}
}
@media(max-width:420px){
  .tb-markets,.tb-account{font-size:10px;letter-spacing:.05em}
  .tb-item{padding-right:10px}
}

.main-nav{
  position:sticky;top:0;z-index:1000;
  background:var(--bg);
  border-bottom:var(--rule-w) solid var(--rule);
  backdrop-filter:none;
}
.nav-inner{
  max-width:var(--content-width);margin:0 auto;
  padding:18px 48px;
  display:flex;align-items:center;gap:36px;
}
@media(max-width:768px){.nav-inner{padding:14px 24px;gap:16px}}
.nav-links>a:not(.nav-cta),.nav-dd>a:not(.nav-cta){
  font-family:var(--f-label);font-size:15px;font-weight:600;
  color:var(--text);letter-spacing:0;
  border-radius:0;background-image:none;
}
.nav-links>a:not(.nav-cta):hover,.nav-dd>a:not(.nav-cta):hover,
.nav-links>a.active,.nav-dd>a.active{color:var(--brand-text)}
.mega-menu,.mm-inner{border-radius:0;box-shadow:none;border:var(--rule-w) solid var(--rule);background:var(--bg)}
/* ── 9b · NAV TYPE SIZE ───────────────────────────────────────────────────
   The v10 pass shrank the whole navigation and this is where it happened:
   13px links, 12px descriptions, 10px labels. That was the smallest type
   anywhere on the site sitting on the one surface every visitor has to read
   before they can go anywhere, and it was reported as hard to read. The
   mobile drawer had already been bumped back to 15px/13px in its own media
   block for exactly this reason — desktop simply never got the same fix.
   These are now the same sizes as the drawer. Do not shrink them back to
   match a mock measured at display size. */
.mm-label{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--text-faint)}
.mm-link{font-family:var(--f-label);font-size:15px;font-weight:600;color:var(--text);line-height:1.4}
.mm-desc{font-weight:400;color:var(--text-muted);font-size:13px;line-height:1.45}

/* ── 9c · MEGA-MENU GLYPHS ────────────────────────────────────────────────
   One glyph per item, painted from data-i in includes/header.php via
   generated content, which keeps them out of the accessibility tree — they
   are decoration, and a screen reader announcing "black right-pointing
   triangle" before every industry would be worse than no icon at all.

   Set in --f-mono, not --f-label: Archivo has no coverage for most of these
   codepoints, so in the label face they fall through to whatever the system
   substitutes and arrive at inconsistent widths and weights. The mono stack
   carries the Geometric Shapes and Mathematical Operators blocks properly, and
   a UI marker is machine furniture rather than a marketing figure, so mono is
   the right register for it.

   Two things move on hover: the glyph shifts right and takes the accent, and a
   2px rule wipes in down the left edge. Both are v10 vocabulary — the 2px rule
   is the same object the bands use — and both are hover-only, so nothing on
   this surface animates on its own. */
.mm-link{position:relative;padding-left:34px}
.mm-link::before{content:attr(data-i);position:absolute;left:10px;top:.72em;
  font-family:var(--f-mono);font-size:15px;line-height:1;color:var(--text-faint);
  transition:color .18s ease,transform .22s cubic-bezier(.22,1,.36,1)}
.mm-link:hover::before,.mm-link:focus-visible::before{
  color:var(--brand-text);transform:translateX(3px)}
.mm-link::after{content:'';position:absolute;left:0;top:5px;bottom:5px;width:2px;
  background:var(--brand);transform:scaleY(0);transform-origin:50% 50%;
  transition:transform .22s cubic-bezier(.22,1,.36,1)}
.mm-link:hover::after,.mm-link:focus-visible::after{transform:scaleY(1)}
/* .mm-more carries a border-top and extra padding, so its bar would start in
   the rule above it. Pull it clear. */
.mm-more::after{top:calc(0.875rem + 3px)}
@media(prefers-reduced-motion:reduce){
  .mm-link::before,.mm-link::after{transition:none}
}
.nav-toggle{border-radius:0;border:1px solid var(--ink)}

footer,.site-footer{
  background:var(--bg);
  border-top:var(--rule-w) solid var(--rule);
  padding:64px 0 40px;
}
.footer-legal,.footer-bottom{
  border-top:1px solid var(--n-300);
  padding-top:24px;margin-top:52px;
  font-size:12px;color:var(--text-faint);
}
footer a{color:var(--text-body);font-size:13px}
footer a:hover{color:var(--brand-text)}
.footer-col-label,.footer-heading{
  font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  color:var(--text-faint);
}


/* ── 10 · MEDIA ──────────────────────────────────────────────────────────── */

/* "Images: grayscale only — never tinted." Applied to content imagery, not to
   the logo mark (which is a two-colour drawing and would lose its red dash) or
   to inline SVG icons that inherit currentColor. */
img:not(.logo img):not(.no-gray),
picture img:not(.no-gray),
video:not(.no-gray),
.photo img,.pband img,.vband video{
  filter:grayscale(1) contrast(1.08);
}
img{display:block;max-width:100%;height:auto;border-radius:0}


/* ── 11 · MOTION ─────────────────────────────────────────────────────────── */

/* One easing curve. The reveal is a 22px fade-up over .7s, per the source; the
   existing observer in footer.php drives .reveal/.rise/.settle/.ledger and its
   selector list is unchanged, so this is a retune of the same mechanism rather
   than a new one. */
.reveal,.rise,[data-reveal]{
  opacity:0;transform:translateY(22px);
  transition:opacity .7s var(--ease-out),transform .7s var(--ease-out);
}
.reveal.visible,.rise.visible,[data-reveal].visible,
.reveal.in,.rise.in,[data-reveal].in{opacity:1;transform:translateY(0)}
.reveal-stagger>*{transform:translateY(22px);transition:opacity .7s var(--ease-out),transform .7s var(--ease-out)}
/* Above-the-fold content never animates in. */
.page-hero .reveal,.hero .reveal,.hero-enter{opacity:1!important;transform:none!important}

/* Blinking block cursor for the typing log — 1s step-end, red, square. */
.cursor,.type-cursor{
  display:inline-block;color:var(--brand);
  animation:okBlink 1s step-end infinite;
}
@keyframes okBlink{0%,49%{opacity:1}50%,100%{opacity:0}}

/* Hover lifts are gone with elevation: nothing rises off a flat page. */
.card:hover,.sc:hover,.tile:hover,.pf:hover{transform:none}

/* ONE reduced-motion block for this layer. The 21 per-include blocks in the
   .php files are LOAD-BEARING and must not be folded in here: each pairs with
   a JS branch in the same file that jumps that specific visualisation to its
   END STATE. The CSS block and the JS branch are two halves of one mechanism,
   and deleting either leaves a reduced-motion visitor looking at a half-built
   diagram. */
@media(prefers-reduced-motion:reduce){
  /* .top-bar-values (the marquee) was switched off here. It no longer exists —
     the strip that replaced it does not move, so there is nothing to disable. */
  .pulse-dot,.live-dot{animation:none}
  .cursor,.type-cursor{animation:none}
  .reveal,.rise,[data-reveal],.reveal-stagger>*{opacity:1!important;transform:none!important;transition:none}
}
/* Paused, not cancelled, so scrolling back does not restart the strip. */
[data-loop].is-off,[data-loop].is-off *{animation-play-state:paused}

/* ── 12 · NO GRADIENTS ───────────────────────────────────────────────────────
   "No gradients" is a stated law of this system, and 32 of them survived the
   token change because a gradient is written as a literal in a declaration,
   not as a colour token. They fall into three groups and each gets a different
   treatment — a blanket `background-image:none` would have been wrong for two
   of the three.

   GROUP A · SCRIMS. A radial scrim over video footage exists so white type
   stays readable on moving footage. Deleting it makes the type unreadable, so
   these become a FLAT ink veil at the same effective darkness. Flat is what
   the law asks for; transparent is not.

   GROUP B · ATMOSPHERE. Aurora washes, orbs, glows, 3D grids and hover sheens
   are decoration in a system whose decoration is supposed to be structure.
   These are removed outright, which also takes three composited layers and a
   blur off the paint path on the homepage hero.

   GROUP C · HAIRLINES AND BARS. A `linear-gradient(90deg,transparent,X,
   transparent)` is how the old system drew a rule that fades at both ends.
   It is a rule, so it becomes a flat 1px fill of the colour it was fading. The
   dashed feedback arc keeps its repeating-linear-gradient: that is a PATTERN
   (a dashed line), not a wash, and it is the one legitimate use left. */

/* GROUP A — flat ink veils. .72 alpha matches the mid-stop of the radial each
   replaces, so text contrast over footage is unchanged. */
.cta--video .cta-scrim,.smb-cta-scrim{background:rgba(32,30,29,.72)}
.ai-shift-veil{background:rgba(32,30,29,.78)}
.ai-shift-veil::after{background:none}

/* GROUP B — atmosphere out. */
.hero-aurora,.hero-grid3d,.hero-orb,.glow-wrap::after,.band-lit::before{
  display:none!important;
}
.sheen::after,.tick::after{background:none;display:none}
.ovn-clock-sky{background:var(--ink)}
.ovn-moon{background:var(--n-300)}
.bld-hero{background:var(--bg-alt)}
/* .tick is a count-up figure, not a surface — the sheen was painted on it. */
.tick{background:none}

/* GROUP C — fading rules become flat rules. */
.ai-shift h2::after{background:var(--brand)}
.ba-bar-fill{background:var(--ink)}
.aoviz-wire::after,.aoviz-wire-v::after,.aoviz-row:nth-child(5){background:var(--n-300)}

/* The ticker contrast fix that stood here is retired with the ticker. The rung
   discipline it encoded did NOT retire and is applied to the strip that
   replaced it: .tb-label takes ink, .tb-item takes --text-muted (5.00) and not
   --text-faint, because these are 11px caps and the fine-print rung is for
   fine print. See the market strip in section 9. */

/* ── 13 · TRUST MARKS ────────────────────────────────────────────────────────
   See includes/trust-marks.php for what these claims may and may not say. */
.trust-row{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px 14px;
  font-family:var(--f-label);
  font-size:11px;font-weight:700;
  text-transform:uppercase;letter-spacing:.06em;
  color:var(--text-muted);
  margin-top:28px;
}
.trust-sep{color:var(--n-400)}
/* In the poster band the row sits on red, where --text-muted is invisible. */
.cta .trust-row,.smb-cta .trust-row{color:#fff;opacity:.9}
.cta .trust-sep,.smb-cta .trust-sep{color:var(--acc-300)}

.trust-mark{
  font-family:var(--f-display);font-weight:800;
  font-size:clamp(1.375rem,2.2vw,1.75rem);
  letter-spacing:-.02em;line-height:1.05;
  color:var(--text);
  margin:14px 0 12px;
}
.trust-note{
  font-size:14px;line-height:1.7;color:var(--text-body);margin:0;max-width:none;
}
.trust-qualifier{margin-top:14px}
/* The footer row sits above the legal rule, so it takes the fine-print rung
   rather than the muted one — it is a credential line, not a claim being made
   for the first time at that point on the page. */
footer .trust-row{margin-top:0;margin-bottom:24px;color:var(--text-faint)}

/* ── 14 · FLUSH LEFT, FINISHED ───────────────────────────────────────────────
   "Everything flush left. Never center headings, copy, or button labels."
   Section 1 stated the law and covered the band-level containers; this covers
   the 34 rules in the component body that centre type inside them, which is
   where the centring actually survived.

   The distinction that matters, and the reason this is a list rather than a
   blanket `text-align:left`: centring a LINE OF TYPE is what the law forbids.
   Centring a BOX — an icon inside its 36px square, a node in a cycle diagram,
   a label inside a small button — is layout, and those are deliberately not
   touched. .bv-nav-icon, .dash-sb-icon, .aoviz-cnode, .aoviz-cycle-center,
   .cc-btn and .nav-cta all keep their centring for that reason.

   .btn-block is the one button that goes left, because the source calls it
   out: "Labels flush left in wide buttons". A short label centred in a
   300px-wide plan button is a lonely thing in the middle of a rectangle. */
.tc,.tc h1,.tc h2,.tc h3,.tc .desc,.tc .ssub,.tc .hero-sub,
.cta,.cta h2,.cta h3,.cta .desc,.cta .ssub,
.smb-cta-card,.smb-cta-card h2,.smb-cta-card .desc,
.smb-cta-close h3,.smb-cta-close .desc,.smb-cta-tagline,
.ai-shift-inner,.ai-shift h2,.ai-shift .desc,
.results-agg-item,.receipts-src,.strip-cap,.pf-note,.kpi{
  text-align:left;
}
/* Centred type is usually held there by `margin:0 auto` on a max-width too, so
   unsetting text-align alone leaves the block itself floating mid-container. */
.cta .desc,.cta .ssub,.smb-cta-card h2,.smb-cta-card .desc,
.ai-shift h2,.ai-shift .desc,.smb-cta-tagline{
  margin-left:0;margin-right:0;
}
.cta .eyebrow,.smb-cta-card .eyebrow,.ai-shift .eyebrow,.eyebrow{justify-content:flex-start}
/* The secondary link was the last centred thing left in that card. It is not
   text-align holding it there — it is `width:100%` on an inline-flex box with
   justify-content:center, so it stretched the full card and parked itself in
   the middle under a stack of flush-left lines. Shrink it back to its content
   and the whole card reads on one left edge. */
.smb-cta-secondary{width:auto;justify-content:flex-start}
/* The one heading centred by an inline attribute rather than a class. */
[style*="text-align: center"]>.eyebrow,[style*="text-align:center"]>.eyebrow{justify-content:flex-start}

/* ── 15 · CONTRAST CORRECTIONS ───────────────────────────────────────────────
   Found by running an alpha-compositing WCAG audit over six pages after the
   colour remap (scratchpad/contrast2.js). Every failure below has the SAME
   root cause, and it is worth naming because it will recur on the next colour
   change: a value that was measured at DISPLAY size being reused at LABEL
   size. #1d4ed8 measures 3.76 on the ground. That clears the 3:1 a headline or
   a graphic is held to, and fails the 4.5:1 an 8-11px status chip is held to.

   The rule this encodes, which is the same rung discipline v8 and v9 ran:
     --brand       #1d4ed8   >=24px type, fills, rules, dots, graphics
     --brand-text  #1e40af   ANY red under 24px — chips, deltas, labels, links
   If you are typing a red onto something small, it is --brand-text. */

/* Status chips and deltas inside the product mocks. All were #1d4ed8 at 8-11px. */
.pm-badge.bad,.ps-verdict.down,.ps-card .down,.down,
.dp-sev.cr,.dp-sev.wr,.ci-st-e,.hbv-flag{
  color:var(--brand-text);
}
/* The neutral "watch/warn" rung was #7d7979 (3.94) — the fine-print rung, used
   at 9px. One step darker clears AA and still reads as the quiet state. */
.pm-badge.warn,.dp-sev.in,.ts,.msg,.hbv-cta-label{color:var(--text-body)}

/* The ai-shift band's primary button was ink-on-brand-vivid. That pairing was
   measured under v9, where --brand-vivid was #FF683D and ink on it made 6.52.
   v10 collapses brand-vivid onto the one red, so the same declaration now
   paints ink on #1d4ed8 (3.95) — the button did not change, the token under it
   did. It takes the site's standard primary pairing instead, which is the
   answer to "what colour is a primary button" everywhere else on the site. */
.ai-shift-cta .btn-p{background:var(--btn-fill);border-color:var(--btn-fill);color:#fff}
.ai-shift-cta .btn-p:hover{background:var(--btn-fill-hover);border-color:var(--btn-fill-hover);color:#fff}

/* The poster band paints its type white (section 8). .smb-cta is a poster whose
   content sits inside a LIGHT card floating on the footage, so the white was
   landing on a near-white card and measuring 1.09 — the worst failure the audit
   found, and one this layer introduced. Scope the inversion to the band's own
   direct copy and hand the card back the ordinary ink palette. */
.smb-cta .smb-cta-card p,.smb-cta .smb-cta-card .desc,.smb-cta-tagline{color:var(--text-body)}
.smb-cta .smb-cta-card h2,.smb-cta .smb-cta-card h3{color:var(--text)}
.smb-cta .smb-cta-card .eyebrow{color:var(--brand-text)}
.smb-cta .smb-cta-card .btn-p{background:var(--btn-fill);border-color:var(--btn-fill);color:#fff}
.smb-cta .smb-cta-card .btn-s{background:transparent;border:1px solid var(--ink);color:var(--text)}

/* The observability log's timestamp and message. --text-faint is the correct
   ROLE for a timestamp, and it measures 4.64 on the ground — but these rows sit
   on a tinted panel inside a framed viz, which is a darker surface than the
   page, and there it lands at 3.49. This is the "measure against the WORST
   surface the value can land on" rule from v8, applied to a surface that did
   not exist when the rung was chosen. */
.aoviz-row .ts{color:var(--text-body)}
.aoviz-row .msg{color:var(--text)}

/* Last two AA failures from the six-page audit, both the same shape as the
   rest: a value chosen for one surface reused on another.

   .smb-cta-secondary is a 14.5px text link sitting on the poster's light card.
   It rendered --brand (#1d4ed8, 3.76) because the card's scoped token block
   sets --brand for the card's GRAPHICS. A link is text. */
.smb-cta-secondary,.smb-cta-secondary span{color:var(--brand-text)}
.smb-cta-secondary:hover,.smb-cta-secondary:hover span{color:var(--brand-deep)}

/* .aoviz-row .ts at --text-body still measured 4.39 on the viz's tinted inner
   panel (it clears 5.83 on the page, which is the value that was checked). The
   panel is the worst surface it lands on, so it takes ink. */
.aoviz-row .ts{color:var(--text)}

/* ── 16 · TYPOGRAPHY, MADE CONSISTENT ────────────────────────────────────────
   The first v10 pass changed the FACE everywhere and left two other axes
   behind — WEIGHT and CASE — so a stat figure could still be monospace at 500
   with a lowercase caption while the band beside it was Archivo 800 with
   uppercase caps. Same design system, two different voices, 40px apart.

   ── THE SCALE. Four weights, and every one of them means something ─────────
     400  body copy, long-form prose, paragraph text
     600  UI and secondary labels — nav, card meta, table cells, fine print
     700  micro-labels and kickers — 10-12px uppercase, letterspaced
     800  DISPLAY — headings, stat figures, prices, step numerals, buttons
   450 and 500 are deleted from the codebase (134 declarations normalised).
   They were v6-v9 nuances that only read on a continuous variable axis, and
   under a three-cut font request they silently collapsed onto their
   neighbours — which is what put the hero proof rail two steps light.

   ── THE FACE RULE, which is the one that was actually being broken ────────
   Monospace is for MACHINE OUTPUT: code, diffs, logs, terminals, and the
   simulated product UI (.hbv-*, .pm-*, .dash-*, .ci-*, .bgu-*, .cm-*) where
   the mono IS the product being shown. It is NOT for marketing figures. A
   number on a marketing band is a CLAIM, and claims are set in the display
   face at 800 — that is what makes "+210%" read like a headline rather than
   like a readout. Every figure below was mono and is now Archivo.

   ── THE CASE RULE ─────────────────────────────────────────────────────────
   A figure's caption is an uppercase micro-label: 11-12px, 700, letterspaced
   .05-.08em. Never sentence case, and never `text-transform:lowercase`, which
   .receipt-label was actually doing — forcing "SMBs" to render as "smbs". */

/* ---- DISPLAY FIGURES -----------------------------------------------------
   Every marketing stat number on the site, in one rule. tabular-nums so a
   count-up does not change its own width while animating. */
.receipt-num,.results-agg-value,.gc-val,.pp-stat-val,.sg-score,
.price-big,.plan-price,.smb-card-price,.obs-detail-val,.gs-plan-price,
.stat,.stat-num,.mnum,.figure,.tick{
  font-family:var(--f-display);
  font-weight:800;
  letter-spacing:-.02em;
  line-height:1;
  font-variant-numeric:tabular-nums;
}
.receipt-num{font-size:clamp(1.875rem,3vw,2.75rem);color:var(--text)}
.receipt-num.accent,.results-agg-value,.gc-hero .gc-val{color:var(--brand-text)}

/* ---- FIGURE CAPTIONS -----------------------------------------------------
   The uppercase micro-label under a figure. --text-muted (5.00), not
   neutral-600 (3.94) — see the DEVIATION note in :root. */
.receipt-label,.stat-cap,.stat-label,.istat-src,.gc-cap,.results-agg-label{
  font-family:var(--f-label);
  font-size:12px;
  font-weight:700;
  text-transform:uppercase;
  letter-spacing:.05em;
  color:var(--text-muted);
  line-height:1.35;
}
/* .receipt-label carried text-transform:lowercase, which downcased proper
   nouns. Stated explicitly rather than relying on the shorthand above,
   because the rule it is beating is equally specific. */
.receipt-label{text-transform:uppercase;margin-top:10px;display:block}

/* ---- NUMBERED STEP ROWS --------------------------------------------------
   The how-it-works pattern from the approved design: a big red numeral, a
   heavy title, body copy, and an outline tag pinned right, on 2px rules.
   It was rendering as an 11px grey numeral beside a 17px medium title with a
   plain uppercase tag — legible, but not this system. */
.how5 li{
  grid-template-columns:140px minmax(0,1fr) 180px;
  column-gap:36px;
  padding:36px 0;
  border-top:var(--rule-w) solid var(--rule);
  align-items:start;
}
.how5-n{
  font-family:var(--f-display);
  font-size:36px;font-weight:800;
  color:var(--brand);
  letter-spacing:-.02em;line-height:1;
  text-transform:none;
}
.how5 h3{
  font-family:var(--f-display);
  font-size:20px;font-weight:800;
  letter-spacing:-.01em;
  color:var(--text);
  margin:0 0 12px;
}
.how5 p{font-size:14px;line-height:1.7;color:var(--text-body);max-width:660px}
/* The tag becomes a real .tag-outline: red 1px box, red label. Squared, like
   everything else. */
.how5-t{
  justify-self:end;
  align-self:start;
  white-space:nowrap;
  font-family:var(--f-label);
  font-size:11px;font-weight:700;
  text-transform:none;
  letter-spacing:.01em;
  color:var(--brand-text);
  border:1px solid var(--brand);
  padding:5px 11px;
}
@media(max-width:820px){
  .how5 li{grid-template-columns:64px minmax(0,1fr);column-gap:20px;padding:28px 0}
  .how5-n{font-size:28px}
  .how5-t{grid-column:2;justify-self:start;margin-top:14px}
}

/* ---- CARD AND PANEL HEADINGS --------------------------------------------
   h3 was the one heading level v9 deliberately kept in the UI face at medium
   weight, because a serif display face made every card look like a pull
   quote. There is no second face now, so the exception has nothing left to
   protect against and h3 rejoins the display scale. */
h3,.card-title,.cap-tab-t,.gs-title,.gs-grade-lbl,.gs-plan-name,.al-brand{
  font-family:var(--f-display);
  font-weight:800;
  letter-spacing:var(--tr-h3);
}

/* ---- FINE PRINT ---------------------------------------------------------
   Source lines and disclaimers are sentence case at 11-12px. They are the one
   small type that is NOT uppercase: they are sentences, and letterspaced caps
   at sentence length is the thing that makes a legal line unreadable. */
.receipts-src,.istats-method,.fine,.fineprint,.source,.stat-src{
  font-family:var(--f-label);
  font-size:12px;font-weight:400;
  text-transform:none;letter-spacing:0;
  line-height:1.6;
  color:var(--text-faint);
}

/* ---- b / strong ---------------------------------------------------------
   <b> and <strong> default to `bold`, which resolves to 700 — on-scale. But a
   nested <b> inside an already-bold context computes to `bolder` = 900, which
   is off the four-step scale and renders as a heavier cut than anything the
   design uses. Pin both. */
b,strong{font-weight:700}
/* A <b> used as a display figure inside a stat block is the exception, and it
   is the display weight like every other figure. */
.ca-stats b,.ca-hero b,.ag-stat b,.stat b,.receipt b{font-weight:800}

/* ── 17 · v10.1 — THE STATUS-CHIP SWEEP ──────────────────────────────────────
   2026-08-17. A compliance audit against the brand guidelines found that v10
   changed every status chip's TEXT colour and none of its FILL. The failure is
   worth naming because it will recur on the next system change, and because
   nothing about it was visible in a diff:

     .pw-sev.ok{background:rgba(22,163,74,.08);color:var(--success);...}
                           ^^^^^^^^^^^^^^^^^^        ^^^^^^^^^^^^^^^
                           an rgba LITERAL           a TOKEN

   v10 remapped --success to red at the token layer, so every one of these rules
   ended up painting RED TYPE INSIDE A GREEN BOX. The colour law was enforced on
   exactly the half of each declaration that went through a token. Rendered on
   five pages: 12 green badges on /integrations, 12 blue rows on /platform and
   /agencies, teal on /platform, amber on /solutions detail pages.

   ── THE RULE THIS ENCODES ────────────────────────────────────────────────────
   A TOKEN SWEEP CANNOT REACH A LITERAL. When a hue changes meaning, grep for
   the rgba()/hex literals too, and grep the .php templates, not just this file
   — 22 of the declarations were in industries.php, integrations.php and
   solutions.php, where a colour is data (`'color' => '#3B82F6'`) rather than
   CSS. `scratchpad/hue.js` in the audit walks every rendered element and flags
   any computed colour outside the red band; run it, do not eyeball it.

   ── WHAT REPLACED THE HUES: FORM, NOT COLOUR ────────────────────────────────
   Four severities that used to be four colours are now four FORMS on one ramp,
   loudest to quietest — and the ladder is identical in .pw-sev, .dp-sev and
   .at-sev (solutions.php), so a chip means the same thing wherever it lands:

     crit   solid --brand fill, white label       the loudest thing in the panel
     warn   --acc-100 fill, --brand-text label    same hue, no fill
     info   --bg-alt fill, --n-300 border         neutral, still marked
     ok     no fill, no border, --text-faint      the absence of a problem

   Read that top to bottom and the severity is legible with no colour at all,
   which is the property a one-hue system has to buy back. DO NOT reintroduce a
   green "ok": ok is the resting state, and the resting state is unmarked.

   Where a component distinguished N ITEMS rather than N SEVERITIES — five
   clients in viz-activity-log.php, four in viz-portfolio-signals.php, six
   vendors in the solutions.php data-lake bars — the answer is the neutral ramp
   with the accent on ONE member, never N hues. The accent marks the item that
   is happening now (SYNCING) or the one the diagram is about (the hub), and
   everything else is ink. Identity is carried by the monogram and the label
   sitting next to the mark, which is why the marks can safely repeat.

   ── ALSO IN THIS PASS ───────────────────────────────────────────────────────
   · ::selection was specified from v6 and never once implemented — see §4.
   · Emoji removed sitewide (~30 across 20 templates). Icon slots take a
     two-letter monogram for named products and a typographic mark for
     concepts; .df-ico and .pw-sync-icon now set type accordingly.
   · Window-button traffic lights (§ .td, .browser-dots, .d-dots) were three
     hues on every product mock. Now accent + two neutrals.
   · --color-accent-2 deleted (0 consumers). .wt's --c2 brown and --c3 violet
     tints retired, and the comment above .wt that still argued for a "cooler
     console accent" rewritten — that stale comment is why they survived v10.
   · The six page types that never closed on the poster band now do.
   · prefers-reduced-motion blocks added to the 10 templates that animated
     without one.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 18 · THE AMBIENT FIELD (v10.2) ──────────────────────────────────────────
   Scoped back to band hosts. See assets/js/ao-three.js for what it draws, what
   it costs, and the honest note about "motion is informational, never
   decorative" — this ships at explicit request and the guidelines say
   otherwise.

   WHAT USED TO BE HERE, AND WHY DELETING IT IS THE FIX.
   v10.1 made the field one fixed, full-viewport canvas behind every page. A
   fixed canvas behind the page is invisible if the page paints over it, and
   this page paints over everything — .section carries background:var(--bg),
   .section-alt carries the tint, band after band. So v10.1 moved the ground up
   to `html`, made `body` transparent, lifted content with z-index, and turned
   .section transparent plus .section-alt into a 92% color-mix so the field
   could show through. Six structural rules, sitewide, in service of a
   decoration.

   All of it is gone with the layer it served. The field now mounts inside
   [data-ao3d] hosts (rule near .ao3d-canvas, ~line 2229) where it needs none of
   this: the host clips it, the host's own background sits behind it, and every
   other band on the site goes back to painting normally.

   Two things worth knowing if this is ever reverted again:

   · body{background:transparent} + html{background:var(--bg)} is load-bearing
     for a FIXED field and actively wrong without one. Restoring the ground to
     the element that owns it is part of this deletion, not separate from it.
   · The z-index block that lifted content over the canvas had a broken comment
     — the `*/` on the "POSITION IS NOT SET HERE" note closed early and left six
     lines of prose parsing as a selector, which swallowed the rule that
     followed. The lift may never have applied. If you write that block again,
     check it in devtools rather than trusting it. */

html{background:var(--bg)}
body{background:var(--bg)}


/* ============================================================================
   v11 "DEPTH" — SURFACE VARIETY + SCROLL-LAYERED BANDS + COLLAPSIBLES
   ============================================================================
   v10 gave the site one red, zero radius and a 2px rule, and it is right about
   all three. What it did not give it was a reason to keep scrolling: the whole
   page alternates between --bg #f3f2f2 and --bg-alt #f8f4f4, which are 2 points
   of lightness apart. At arm's length that is not an alternation, it is one
   grey. Fifteen bands of it in a row is the "bland" complaint, and it is a
   surface problem rather than a content problem.

   This layer is appended, not merged, for the same reason the v10 ENFORCEMENT
   LAYER was: it is a diff you can read and revert, not 3,300 lines of edits.

   THREE THINGS, AND THE SEO CONSTRAINT SHAPES ALL THREE
   ------------------------------------------------------------------------
   1 SURFACES  four light grounds instead of two, cycled automatically off
               nth-of-type so no template has to opt in, plus an explicit ink
               band. NOT gradients — v10 deleted all 32 of those and was right.
               The "field" surface is a 1px rule grid at hard stops, which is
               structure, the same object as the 2px band rule. If it ever
               softens into a haze, it has become the thing v10 removed.

   2 DEPTH     bands rise and settle as they enter, and the leaving band dims,
               so consecutive bands read as stacked planes. Done with CSS
               scroll-driven animation (animation-timeline: view()) and NO
               JAVASCRIPT, which is what makes it safe:
                 · transform and opacity only, so it cannot move layout and
                   cannot contribute to CLS, which is a ranking signal;
                 · the opacity floor is 0.55, never 0, so a band that has not
                   been scrolled to is still rendered, still readable, and
                   still text to anything parsing the page;
                 · wrapped in @supports, so a browser without scroll timelines
                   gets the static page rather than a broken one;
                 · no scroll listener, so it cannot cost INP.
               Enter is on the section and exit is on its .container, because
               two animations on one element fighting over transform/opacity
               is how this pattern usually breaks.

   3 COLLAPSE  the existing .faq-item <details> component, extended and rolled
               out to the templates that were still rendering FAQs as flat
               h3+p. <details> keeps the answer in the DOM and in the FAQPage
               graph — this is the one collapse pattern that does not hide text
               from a crawler. The first item stays open so the pattern is
               legible and so the page never opens on a wall of closed rows.
               Do NOT collapse anything that is the page's primary answer:
               .ao-answer is the extract target and stays open prose.
   ============================================================================ */

/* ── 1 · THE SURFACE SET ─────────────────────────────────────────────────────
   --bg-alt is NOT touched here. It fills cards, table heads and chips as well
   as bands, so moving it to add band contrast would drag 60-odd components
   with it. Band surfaces are their own tokens. */
:root{
  --surf-ground:#f3f2f2;   /* = --bg, the default band                        */
  --surf-tint:#f8f4f4;     /* = --bg-alt, the existing .section-alt           */
  --surf-paper:#e9e5e1;    /* a real step: ~4 points of lightness, warm       */
  --surf-field:#f1efee;    /* ground, under the rule grid                     */
  --surf-grid:rgba(32,30,29,.055);  /* the grid rule itself                   */
  /* NOT --ink. The inverted token scope that .section--ink joins redefines
     --ink to #f8f4f4, because inside a dark band "ink" is the colour text is
     drawn IN. background:var(--ink) therefore resolved to near-white and the
     band rendered light with light text on it: 33 contrast failures, all of
     them 1.00. The band's own ground needs a token nothing inverts. */
  --surf-ink:#201e1d;
}

/* THE CYCLE IS POSITIONAL, NOT SEMANTIC, AND THAT IS THE POINT.
   The first attempt keyed paper off .section:nth-of-type(4n+3) and .section-alt
   :nth-of-type(4n+4). nth-of-type counts every <section> sibling regardless of
   class, so those two selectors match ADJACENT bands and the page rendered
   paper/paper, paper/paper four times over — the alternation gone entirely.
   Cycling on position alone and letting .section/.section-alt fall through
   guarantees no two neighbours can share a surface, which is the one property
   this has to have.

   Excluded: the hero, the CTA poster, the ink bands and the video band all
   carry their own ground and their own measured contrast. A band that inherits
   a surface by accident is a band whose cards were measured against a
   different one. */
main>section:not(.cta):not(.page-hero):not(.hero):not(.hero-build):not(.ai-shift):not(.vband):not(.section--ink):not(.smb-cta){
  background-image:none;
}
main>section:not(.cta):not(.page-hero):not(.hero):not(.hero-build):not(.ai-shift):not(.vband):not(.section--ink):not(.smb-cta):nth-of-type(4n+1){
  background-color:var(--surf-ground);
}
main>section:not(.cta):not(.page-hero):not(.hero):not(.hero-build):not(.ai-shift):not(.vband):not(.section--ink):not(.smb-cta):nth-of-type(4n+2){
  background-color:var(--surf-tint);
}
main>section:not(.cta):not(.page-hero):not(.hero):not(.hero-build):not(.ai-shift):not(.vband):not(.section--ink):not(.smb-cta):nth-of-type(4n+3){
  background-color:var(--surf-paper);
}
/* The field: ground under a 1px rule grid, at HARD stops. There is no soft stop
   in here and there must never be one — a 1px line drawn with gradient syntax
   is a rule; a 40px fade drawn with the same syntax is the atmosphere v10
   deleted on purpose. */
main>section:not(.cta):not(.page-hero):not(.hero):not(.hero-build):not(.ai-shift):not(.vband):not(.section--ink):not(.smb-cta):nth-of-type(4n+4),
.section--field{
  background-color:var(--surf-field);
  background-image:
    linear-gradient(var(--surf-grid) 1px,transparent 1px),
    linear-gradient(90deg,var(--surf-grid) 1px,transparent 1px);
  background-size:72px 72px;
  background-position:center top;
}
.section--paper{background-color:var(--surf-paper);background-image:none}

/* Ink is deliberately NOT on the cycle. A band that inherits near-black by
   accident is a band whose cards, chips and charts were all measured on a light
   ground, and the contrast bugs land in whichever component happened to be
   there. It is opt-in per template, and it borrows the inverted token scope
   that .vband/.ai-shift already carry (see the v10 dark-band block above). */
.section--ink{
  background-color:var(--surf-ink);
  background-image:none;
  border-bottom-color:var(--surf-ink);
}
.section--ink h1,.section--ink h2,.section--ink h3,.section--ink h4{color:var(--text)}
/* Anything with a light fill has to flip with the band. --bg-card is #f8f4f4;
   left alone it paints a pale tile onto near-black in every card, panel and
   list row that happens to land inside an ink band. */
.section--ink .card,.section--ink .rcard,.section--ink .pmap,.section--ink .dcal,
.section--ink .riv,.section--ink .cmp-wrap,.section--ink .faq-item{
  background:rgba(247,246,242,.04);border-color:var(--border);
}
.section--ink .rcard-bar,.section--ink .pmap-head,.section--ink .dcal-head,
.section--ink .rcard-foot,.section--ink .pmap-foot,.section--ink .dcal-foot{
  background:rgba(247,246,242,.05);
}
.section--ink .btn-s{border-color:var(--border-hover);color:var(--text)}
.section--ink .btn-s:hover{background:rgba(247,246,242,.08)}
.section--ink .lf-node{background:var(--surf-ink)}

/* Cards sitting on paper or field need to separate from them. Both surfaces are
   darker than --bg, so the card reads as raised without any shadow — the v10
   way of doing elevation, which is to not do elevation. */
main>section:nth-of-type(4n+3) .card,
main>section:nth-of-type(4n+4) .card,
.section--paper .card,.section--field .card{background:var(--bg)}

/* ── 2 · SCROLL-LAYERED BANDS ──────────────────────────────────────────────
   FIRST VERSION WAS INVISIBLE AND HERE IS WHY, BECAUSE IT WILL HAPPEN AGAIN:
   the range was `entry 0% entry 45%`. "entry" ends the moment the element is
   fully inside the viewport, so 45% of entry is over while the band is still
   sitting near the BOTTOM of the screen. Every band finished animating before
   it reached the part of the screen anyone reads from. Combined with a 24px
   move and a 0.72 opacity floor, the effect completed off in the corner of the
   eye and the page looked completely static.

   The range now runs `entry 0% -> cover 30%`, which crosses out of the entry
   phase and keeps going while the band travels up into the reading zone. The
   move is 64px and the scale 0.955, which is roughly three times the first
   attempt. Two rules of thumb worth keeping: time the range to where the eye
   is, and if you cannot see it on a 1440px screen at normal scroll speed, it
   is not there.

   Still transform and opacity only, so this cannot move layout and cannot
   contribute to CLS. Opacity floor is 0.4 — visibly dimmed, never hidden. */
/* OPACITY LIVES ON THE CHILDREN, NOT ON THE BAND. Opacity multiplies through
   nesting, so band 0.40 x child 0.00 put below-fold content at an effective
   zero — and a renderer that does not scroll (which is the case that matters
   for indexing) would snapshot the page in exactly that state. The band moves;
   the content fades. One opacity in the chain, floor 0.35, never zero. */
@keyframes ao-band-rise{
  from{transform:translate3d(0,64px,0) scale(.955)}
  to  {transform:none}
}
/* The outgoing band drops back and dims harder than the incoming one rises, so
   consecutive bands read as stacked planes rather than as a list that scrolls.
   This is the "layered" half; the rise on its own is just a fade-in. */
@keyframes ao-band-recede{
  from{opacity:1;transform:none}
  to  {opacity:.35;transform:translate3d(0,-26px,0) scale(.965)}
}
/* Content inside a band unveils on its own timeline, so the band does not move
   as one slab. Each direct child of .container gets its own view() progress,
   which staggers naturally because they sit at different heights, and the
   per-child ranges below widen that into something you can actually watch. */
@keyframes ao-unveil{
  from{opacity:.35;transform:translate3d(0,42px,0)}
  to  {opacity:1;transform:none}
}

@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    main>section{
      transform-origin:50% 100%;
      animation:ao-band-rise linear both;
      animation-timeline:view();
      animation-range:entry 0% cover 30%;
    }
    main>section>.container{
      transform-origin:50% 0;
      animation:ao-band-recede linear both;
      animation-timeline:view();
      animation-range:exit 0% exit 100%;
    }
    /* :not(style):not(script) — several viz includes emit their <style> and
       <script> as direct children of .container, and without this they take an
       animation and, worse, a slot in the nth-child stagger below, so the
       visible children were being offset by phantom siblings. */
    main>section>.container>*:not(style):not(script){
      animation:ao-unveil linear both;
      animation-timeline:view();
      animation-range:entry 0% cover 26%;
    }
    /* Explicit stagger. Later children start later and finish later, so the
       band assembles top-down instead of arriving all at once. */
    main>section>.container>*:not(style):not(script):nth-of-type(2){animation-range:entry 4% cover 32%}
    main>section>.container>*:not(style):not(script):nth-of-type(3){animation-range:entry 8% cover 38%}
    main>section>.container>*:not(style):not(script):nth-of-type(4){animation-range:entry 12% cover 44%}
    main>section>.container>*:not(style):not(script):nth-of-type(n+5){animation-range:entry 16% cover 50%}

    /* NO .reveal NEUTRALISER HERE, AND THAT WAS THE SECOND BUG. Writing
       `.reveal{opacity:1;transform:none}` inside this block looks like it stops
       the JS reveal fighting the scroll timeline. It does the opposite: `.reveal`
       scores (0,1,0) and `main>section>.container>*` scores (0,0,3), so the
       neutraliser beat the unveil on every element that had a reveal class —
       which is most of them — and the staggered content animation silently did
       nothing while the band moved as one slab.
       Nothing is needed. A CSS animation wins over any normal author
       declaration regardless of selector weight, so ao-unveil already governs
       these elements; the IntersectionObserver keeps running underneath and is
       simply outranked. Where view() is unsupported this whole block is absent
       and .reveal drives as it always did. */

    /* The hero is already on screen at first paint. Animating it means the LCP
       element renders dim and 64px low, which is both a Core Web Vitals problem
       and the first thing anyone sees. */
    main>section:first-of-type,
    main>section:nth-of-type(2),
    main>section.page-hero,
    main>section.hero,
    main>section.hero-build{animation:none;opacity:1;transform:none}
    main>section:first-of-type>.container,
    main>section:nth-of-type(2)>.container,
    main>section.page-hero>.container{animation:none;opacity:1;transform:none}
    main>section:first-of-type>.container>*,
    main>section:nth-of-type(2)>.container>*,
    main>section.page-hero>.container>*{animation:none;opacity:1;transform:none}
  }
}

/* ── 3 · NO-SCRIPT HARDENING FOR THE EXISTING .reveal PATTERN ───────────────
   .reveal starts at opacity:0 and is turned on by an IntersectionObserver. If
   that script never runs, the page renders as a set of empty bands. Googlebot
   executes JS so this is not an indexing bug today, but it is one bad deploy
   away from being one, and it is two lines. */
@media (scripting: none){
  .reveal,.reveal-stagger>*,.reveal-scale,.reveal-left,.reveal-right{
    opacity:1!important;transform:none!important;
  }
}

/* ── 4 · COLLAPSIBLES ───────────────────────────────────────────────────────
   .faq-item already existed and is already boxed-card-with-gaps on index.php,
   lp-smb.php, what-is-your-agency-doing.php, website-design.php and three
   more. The first pass at this layer re-declared border, padding and max-width
   and produced a hybrid — a card with an extra bottom rule inside it. That is
   the wrong fight to pick: the component is deployed, so the new FAQs adopt it
   rather than the other way round.

   What is left here is only what is genuinely additive. */

/* A <summary> is not a heading, and these questions were <h3> before they were
   collapsible. summary's content model allows heading content, so the h3 stays
   inside it and the document outline survives the conversion — otherwise 24
   vertical pages would each shed six headings to gain an accordion. */
.faq-item>summary>h3{margin:0;font:inherit;color:inherit;letter-spacing:normal}

/* The marker is the only affordance a disclosure has, and --text-faint at 11px
   was barely one. Red reads as interactive at this size, and this applies to
   every .faq-item on the site at once rather than only the new ones. */
.faq-item>summary::before{color:var(--brand-text);font-weight:700}
.faq-item>summary:hover{color:var(--brand-text)}

/* Opening is animated where the browser can interpolate height to auto, and is
   a plain instant open everywhere else. No max-height guess: getting it wrong
   clips the longest answer, which is always the one worth reading. */
@supports (interpolate-size: allow-keywords){
  @media (prefers-reduced-motion: no-preference){
    .faq-item{interpolate-size:allow-keywords}
    .faq-item::details-content{height:0;overflow:hidden;
      transition:height .3s cubic-bezier(.22,1,.36,1),content-visibility .3s allow-discrete}
    .faq-item[open]::details-content{height:auto}
  }
}


/* ============================================================================
   v12 — TRUST BLUE + THE LIVE WORDMARK
   ============================================================================
   Two changes, both decided on evidence rather than taste.

   1 THE HUE. Red was doing three jobs at once: brand, success AND danger were
     all #ae1800, across 114 usages, so a passing check and a failing one were
     literally the same colour. The brand moved to blue on the trust research
     (Labrecque & Milne 2012 map blue to competence/reliability; Su & Cui 2019
     find blue beats red on trust across three studies) and, more importantly,
     the three state hues were pulled apart. Green was considered and rejected:
     it collides with the success chip it would sit next to.

     The whole ramp was remapped by POSITION, not by eye, so every contrast
     relationship v10 measured is preserved or improved — and re-measured
     against #e9e5e1, the paper band v11 added, which is darker than anything
     v10 was checked on. 179 hex + 40 rgba() literals. The rgba pass is not
     optional; it is where the last sweep left its survivors.

   2 THE WORDMARK. It was ~7KB of SVG path data traced from Archivo, shipping
     on every page, unselectable and frozen at one weight. It is live text now,
     set in Manrope 700 — rounder terminals and more open apertures than
     Archivo, which is the "smoother" that was asked for. Manrope is requested
     with `&text=askoter`, so Google serves a subset of the six glyphs in the
     word rather than a family, which is why a second face here costs well
     under a kilobyte and does not undo v10's one-family saving.
   ============================================================================ */

/* .wm was an <svg> sized by height. It is a <span> now, so it is sized by
   font-size, and cap height rather than the em box has to match what the SVG
   drew: 19px of glyph. Archivo outlines at height:19px measured ~26px of type. */
.wm{
  font-family:'Manrope',ui-sans-serif,system-ui,sans-serif;
  font-weight:700;
  font-size:26px;
  line-height:1;
  letter-spacing:-.035em;
  display:block;
  color:inherit;
  /* Manrope carries no descender in "askotter", so the em box has dead space
     under the baseline that the SVG did not. Trimming it keeps the mark and the
     word optically centred in the lockup. */
  margin-bottom:-.09em;
  white-space:nowrap;
}
.nav-logo .wm,.f-brand .wm{height:auto;width:auto;font-size:26px}
@media(max-width:1024px){
  .nav-logo .wm{height:auto;font-size:23px}
}
/* The wipe used to target .wm-type, the <g> inside the SVG. That element no
   longer exists, so the entrance moved onto .wm itself — same keyframes, same
   timing, same `backwards` fill so the resting state is always visible. */
.nav-logo .wm{animation:wm-wipe .62s cubic-bezier(.16,1,.3,1) .08s backwards}
@media(prefers-reduced-motion:reduce){
  .nav-logo .wm{animation:none}
}
