/* ============================================================
   Page CSS for the expressive build — shared by index.html,
   platform.html and trust.html. Extracted from index.html's
   inline <style> when the single page split into three (client
   instruction, 18 Aug 2026), so the three pages consume one
   layout system instead of drifting copies.
   tools/audit-hardcodes.py audits this file.
   ============================================================ */
html, body { background: var(--bg-base); }
  /* light-first: "deepest" reads as a recessed tint, not a darker black */
  body[data-theme="light"] { --bg-deepest: var(--color-light-mist); }

  /* ============================================================
     Texture and depth.

     A flat #1E1E1E is what reads as cheap. Real dark surfaces
     have grain, a light source, and edges that catch it. Three
     layers, every value derived from a token:

       grain  a fixed film plate over the whole page, the way a
              grade applies grain last, over everything
       lift   a soft radial light source at the top of each dark
              band, so a band has a direction rather than being
              an even slab
       edge   a 1px highlight on the top of raised surfaces, the
              single detail that separates a designed dark panel
              from a grey rectangle
     ============================================================ */
  :root {
    /* fractalNoise desaturated to neutral — no colour literals, and
       nothing to download */
    --grain-plate: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  }

  .v2-grain {
    position: fixed; inset: 0; z-index: 60; pointer-events: none;
    background-image: var(--grain-plate);
    background-size: 190px 190px;
    opacity: 0.045;
    mix-blend-mode: overlay;
  }

  /* NB the lift is applied inline per band, not here: every band declares
     `background:` as a shorthand, which resets background-image, so a rule at
     this level would always lose. */

  /* ============================================================
     Derived surface tints. Every value here is mixed from a
     semantic alias, so it re-resolves inside each themed band
     instead of being pinned to one surface.

     Declared on the theme selectors as well as :root: a var()
     nested inside a custom property is substituted where it is
     DECLARED, not where it is used, so a single :root definition
     would freeze these at the light palette.
     ============================================================ */
  /* ------------------------------------------------------------
     PROPOSED TOKEN — completes an existing pattern rather than
     inventing one. Mint and Signal Orange each already carry an
     AA-safe on-light variant (--color-deepest-mint,
     --color-warning-text-small); Success had none, so
     --color-success (#2E9E63) was being used as 11px text on
     Light Mist at 2.98:1. #1F7A4A is the same hue, darkened to
     clear 4.5:1 on both light surfaces:
         on --color-off-white  #F6FBF8 → 5.09:1
         on --color-light-mist #EDF1EF → 4.68:1
     On dark, --color-success itself already clears ink at 4.91:1.
     ------------------------------------------------------------ */
  :root {
    --color-success-text:      #1F7A4A;  /* success as text ON LIGHT  */
    --color-success-text-dark: #3FB77A;  /* success as text ON DARK   */
  }
  :root, [data-theme="dark"] { --text-success: var(--color-success-text-dark); }
  [data-theme="light"]       { --text-success: var(--color-success-text); }

  /* ------------------------------------------------------------
     PROPOSED TOKENS — accent as a MARK, which is not the same
     requirement as accent as a fill.

     --accent is #00C18B on every surface. That is 7.15:1 on ink
     and 2.33:1 on white, and WCAG 1.4.11 asks 3:1 of a graphical
     object that carries meaning — which a diagram's live state
     is, by construction. Four of the ten bands are light, and the
     coded instruments on them draw their live state in exactly
     that colour. These two aliases resolve per theme, so an
     instrument states intent once and stays legible wherever the
     band puts it:

        --mark-live    on dark #00C18B  7.15:1 · on light #1E8C6C  4.18:1
        --mark-alert   on dark #E8923A  6.83:1 · on light #9A5715  5.60:1

     Verified numerically by tools/token-contrast.py against a 3:1
     floor. Declared on all three selectors deliberately: a var()
     inside a custom property resolves where it is declared, so a
     single :root definition would freeze both at the light value.
     ------------------------------------------------------------ */
  :root, [data-theme="light"], [data-theme="dark"] { --mark-live: var(--text-accent); }
  :root, [data-theme="dark"] { --mark-alert: var(--color-warning); }
  [data-theme="light"]       { --mark-alert: var(--color-warning-text-small); }

  :root,
  [data-theme="light"],
  [data-theme="dark"] {
    /* hero console — a raised panel derived from the card surface */
    --console-bg:        color-mix(in srgb, var(--surface-card) 88%, transparent);
    --console-bg-hover:  color-mix(in srgb, var(--surface-card) 96%, transparent);
    --console-foot:      color-mix(in srgb, var(--surface-raised) 75%, transparent);

    /* nav glass, once it has something to sit on */
    --nav-glass:         color-mix(in srgb, var(--bg-base) 86%, transparent);

    /* wash tints */
    --tint-accent-weak:  color-mix(in srgb, var(--accent) 8%, transparent);
    --tint-accent-soft:  color-mix(in srgb, var(--accent) 14%, transparent);
    --tint-accent-ring:  color-mix(in srgb, var(--accent) 16%, transparent);
    --tint-neutral-weak: color-mix(in srgb, var(--text-primary) 6%, transparent);
    --tint-slate-weak:   color-mix(in srgb, var(--color-brand-slate) 12%, transparent);
    --dot-ring:          color-mix(in srgb, var(--bg-base) 95%, transparent);
    --edge-highlight:    color-mix(in srgb, var(--text-primary) 9%, transparent);
    --band-lift:         radial-gradient(130% 90% at 50% -10%,
                           color-mix(in srgb, var(--text-primary) 5%, transparent) 0%,
                           transparent 62%);
  }
  [data-theme="dark"] {
    /* the mark's slate reads too dark on ink — use the on-dark variant */
    --tint-slate-weak:   color-mix(in srgb, var(--color-brand-slate-light) 14%, transparent);
  }
  /* wide screens: let the shared content column breathe with the viewport */
  #root { --content-max: min(1440px, 92vw); }
  body { overflow-x: hidden; margin: 0; }

  /* ============================================================
     Responsive layout system — the ONLY place breakpoints live.
     Sections consume these custom properties and never carry a
     media query of their own, so the three widths stay in step.

       1140px — laptop: gaps tighten, nothing reflows
        900px — tablet: every multi-column grid becomes one column
        640px — phone

     Every value stays on the 8px scale (§14).
     ============================================================ */
  :root {
    /* horizontal page margin — no section hardcodes 64px */
    --page-pad:      var(--margin-desktop);              /* 64 */

    /* vertical rhythm */
    --section-y:     112px;
    --section-y-lg:  144px;   /* closing CTA */
    /* the nav is fixed and its height changes when links wrap, so the hero
       clears the measured height rather than a guessed constant */
    --hero-clear:    104px;
    --hero-pad-top:  calc(var(--nav-height, 72px) + var(--hero-clear));
    --hero-pad-bot:  120px;
    --hero-min-h:    92vh;

    /* column templates — minmax(0,…) so a wide child can never
       push a track past its share and overflow the page */
    --cols-2:        minmax(0,1fr) minmax(0,1fr);
    --cols-3:        repeat(3, minmax(0,1fr));
    --cols-hero:     minmax(0,1.08fr) minmax(0,0.92fr);
    --cols-security: minmax(0,0.9fr) minmax(0,1.1fr);
    --cols-pipe:     minmax(0,1fr) minmax(0,1fr);
    --cols-trust:    220px minmax(0,1fr);
    --cols-logos:    repeat(auto-fit, minmax(140px, 1fr));
    --cols-strip:    repeat(4, minmax(0,1fr));
    --cols-footer:   1.6fr repeat(5, minmax(0,1fr));
    --cols-report:   200px minmax(0,1fr);
    /* the export band: the file and its four beats beside the seven answers */
    --cols-audit:    minmax(0,1fr) minmax(0,1fr);
    --cols-schema:   148px minmax(0,1fr);
    --cols-beat:     72px minmax(0,1fr);
    /* the drift Q&A: question in the left column, answer beside it */
    --cols-qa:       minmax(0,0.8fr) minmax(0,1.2fr);
    --pad-band:      48px 56px;
    --pad-cell:      40px 40px 36px;
    /* hairline between grid cells: vertical on desktop, horizontal once stacked */
    --cell-div-x:    1px solid var(--border-subtle);
    --cell-div-y:    none;

    /* ------------------------------------------------------------
       Vertical rhythm. Every gap between stacked things comes from
       this ladder — one step per RELATIONSHIP, not per section, so
       the same relationship is the same distance everywhere.

       Before this existed the page expressed one relationship with
       whatever number was to hand: headline to lead paragraph was
       20, 22 and 24 in three sections; header block to content was
       72 and 80; title to body copy was 4, 8, 10 and 12. Uniform
       spacing is not a polish pass, it is what lets a reader learn
       the page's structure once and then trust it.
       ------------------------------------------------------------ */
    --gap-pair:    var(--space-2);   /*   8 — a value and its caption   */
    --gap-text:    var(--space-3);   /*  12 — a title and its body copy */
    --gap-label:   var(--space-4);   /*  16 — a label above a title     */
    --gap-lead:    var(--space-6);   /*  24 — a headline and its lead   */
    --gap-action:  var(--space-8);   /*  32 — copy and a button row     */
    --gap-stack:   var(--space-12);  /*  48 — sibling blocks in a group */
    --gap-group:   80px;             /*  80 — a header and its content  */
    --gap-band:    var(--section-y); /* 112 — inside a band, top/bottom */

    /* gaps that ride with the columns */
    --gap-col:       80px;
    --gap-col-lg:    96px;
    --gap-hero:      clamp(64px, 6vw, 112px);
    --gap-grid:      48px;

    /* per-breakpoint behaviour switches */
    --justify-lead:  end;      /* second-column lead paragraphs */
    --pos-sticky:    sticky;   /* rails that pin while their list scrolls */
    --trust-divider: 1px solid var(--border-subtle);

    /* nav — one row until the bar genuinely cannot hold it, then
       logo + CTA on top and the links wrap onto a second row */
    --nav-cols:      minmax(0,1fr) auto minmax(0,1fr);
    --nav-areas:     "logo links cta";
    --nav-h:         72px;
    --nav-pad-y:     0px;
    --nav-gap-links: 26px;
  }

  @media (max-width: 1140px) {
    :root {
      --gap-col:      56px;
      --gap-col-lg:   64px;
      --gap-hero:     56px;
      --hero-clear:   72px;
    }
  }

  @media (max-width: 900px) {
    :root {
      --page-pad:      var(--space-10);   /* 40 */
      --section-y:     80px;
      --section-y-lg:  96px;
      --hero-clear:    48px;
      --hero-pad-bot:  80px;
      --hero-min-h:    auto;

      --cols-2:        minmax(0,1fr);
      --cols-3:        minmax(0,1fr);
      --cols-hero:     minmax(0,1fr);
      --cols-security: minmax(0,1fr);
      --cols-pipe:     minmax(0,1fr);
      --cols-trust:    minmax(0,1fr);
      --cols-strip:    repeat(2, minmax(0,1fr));
      --cols-footer:   minmax(0,1fr);
      --cols-report:   minmax(0,1fr);
      --cols-audit:    minmax(0,1fr);
      --cols-schema:   minmax(0,1fr);
      --cols-qa:       minmax(0,1fr);

      --gap-col:       40px;
      --gap-col-lg:    40px;
      --gap-hero:      40px;
      --gap-grid:      32px;
      --gap-group:     var(--space-12);   /* 48 */
      --gap-stack:     var(--space-8);    /* 32 */

      --justify-lead:  start;
      --pos-sticky:    static;
      --trust-divider: none;
      --cell-div-x:    none;
      --cell-div-y:    1px solid var(--border-subtle);
    }
  }

  @media (max-width: 640px) {
    /* 92vw is a wide-screen breathing rule; on a phone it stacks a second
       margin on top of --page-pad and starves the nav's link row. The page
       padding IS the margin here. (#root, because the base rule is #root.) */
    #root { --content-max: 100%; }
    :root {
      --page-pad:     var(--space-6);     /* 24 */
      --section-y:    56px;
      --section-y-lg: 72px;
      --hero-clear:   32px;
      --hero-pad-bot: 56px;
      --pad-band:     var(--space-8);
      --pad-cell:     var(--space-6);
      --gap-grid:     24px;
      --gap-group:    var(--space-8);      /* 32 */

      --cols-beat:     minmax(0,1fr);
      --timeline-min:  640px;

      --nav-cols:      minmax(0,1fr) auto;
      --nav-areas:     "logo cta";
      --nav-h:         56px;
      --nav-pad-y:     var(--space-2);   /* 8 */
    }
    /* The links row is gone on a phone (it held one cramped 11px row down to
       320 and owned a slice of every screen). In its place: the burger beside
       the Talk to us button, opening the full-screen menu (.v2-mm). */
    nav .v2-nav-links { display: none !important; }
    .v2-burger { display: inline-flex !important; }
    /* Hero and close action buttons step down to the md voice on a phone —
       the lg size was built for desktop heroes and crowds a 375px row.
       Height stays 44, the locked touch minimum; !important because the
       design-system Button carries its size inline. */
    .v2-actions button {
      height: 44px !important; font-size: 14px !important; padding: 0 16px !important;
    }
    /* the hero lead steps down as the heading steps up (its clamp floor
       rises phone-only); !important because V2Lead carries its font inline */
    .v2-hero-lead p { font-size: 14px !important; line-height: 1.6 !important; }
  }

  /* ── The burger + the full-screen menu it opens ────────────────
     Three 2px lines (borders, not spacing) on a 40px tap target;
     hidden until the links row collapses above. The menu rows rise
     in on the site's stagger; with motion off everything is simply
     there. */
  .v2-burger {
    display: none; appearance: none; -webkit-appearance: none;
    border: none; background: none; cursor: pointer;
    flex-direction: column; justify-content: center; align-items: center;
    gap: 4px; width: 40px; height: 40px; padding: var(--space-2);
  }
  .v2-burger span {
    display: block; width: 20px; border-top: 2px solid currentColor;
  }
  .v2-burger:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
  /* the panel lands at once — it is an opaque surface, and fading it while
     its rows also fade compounds into flicker (review, 21 Aug 2026); the
     rows alone carry the motion */
  :root[data-motion="live"] .v2-mm-item {
    animation: mm-item-in 200ms cubic-bezier(0.16, 1, 0.3, 1) both;
  }
  @keyframes mm-item-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
  }
  /* Tap targets (WCAG 2.5.8): links that rendered under 24px tall get their
     height from padding, not from layout guesswork. */
  #footer a.v2-navlink { display: inline-block; padding: var(--space-1) 0; }
  /* The scrollbar, quieted (review, 20 Aug 2026): a slim translucent pill on
     a transparent track instead of the solid 10-wide bar, deepening under
     the pointer. Firefox gets the same reading via scrollbar-color. */
  ::-webkit-scrollbar { width: 6px; height: 6px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--color-mist-grey) 45%, transparent);
    border-radius: 999px;
  }
  ::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--color-mist-grey) 75%, transparent);
  }
  html {
    scrollbar-width: thin;
    scrollbar-color: color-mix(in srgb, var(--color-mist-grey) 45%, transparent) transparent;
  }

  /* ============================================================
     Deep links.

     Every section id on this page is a published destination —
     the nav, the footer and llms.txt all point at them. Two
     things were breaking them.

     First, the nav is FIXED, so a browser that scrolls an id to
     the top of the viewport puts it underneath the bar. Hence
     scroll-margin-top, measured rather than guessed: --nav-height
     is published by the nav component itself and grows when the
     links wrap onto a second row.

     Second, #autothought is a 180vh sticky scrolltelling section
     above 900px. Its scroll position IS its state: landing at
     exactly its top is stage 01 with the sticky panel engaged,
     and the panel already carries its own nav padding. Offsetting
     it by a nav height would start the reader 72px into a
     scroll-driven sequence with the panel not yet stuck. So it
     opts out of the offset at the width where it is sticky, and
     keeps it at the width where it is an ordinary stack.
     ============================================================ */
  section[id], #footer, #compliance, #autoeval {
    scroll-margin-top: calc(var(--nav-height, 72px) + var(--space-2));
  }
  @media (min-width: 901px) { #autothought { scroll-margin-top: 0; } }

  a { color: var(--text-primary); }
  a:hover { color: var(--text-accent); }

  /* Link hover — mint underline grows in from left (§19) */
  .v2-navlink { position: relative; }
  .v2-navlink::after {
    content: ""; position: absolute; left: 0; bottom: 4px; height: 1px; width: 100%;
    background: var(--color-pints-mint); transform: scaleX(0); transform-origin: left;
    transition: transform 150ms var(--ease-out);
  }
  .v2-navlink:hover { color: var(--text-primary) !important; }
  .v2-navlink:hover::after { transform: scaleX(1); }

  .v2-replay:hover { color: var(--text-accent); }

  /* The export band's download control. It is an <a download>, not a button,
     so it gets the primary button's affordances explicitly. */
  .v2-download:hover { background: var(--button-fill-hover, var(--accent)) !important; }

  /* A compliance table has four columns of dates and clause names and cannot
     be made to fit 320px. It scrolls inside its own box rather than pushing the
     page sideways, and the box is focusable so the scroll is reachable from a
     keyboard. */
  .v2-table-scroll { overflow-x: auto; }

  /* The delivery rail is 960 units wide and carries four captions in the
     smallest mono on the page. Below the tablet breakpoint it stops scaling
     down — the captions would fall under legibility — and scrolls inside its
     own box instead, with the box focusable so a keyboard can reach the
     scroll. */
  .v2-timeline { overflow-x: auto; }
  .v2-timeline > svg { min-width: var(--timeline-min, 0); }
  .v2-timeline:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }
  .v2-table-scroll:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }

  /* Table captions carry the summary a sighted reader gets from the layout. */
  .v2-visually-hidden {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .v2-download:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }

  /* Pipeline — staged entry, tab affordance, tick pop */
  @keyframes v2-stage-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
  @keyframes v2-sweep { from { transform: scaleX(0); } to { transform: scaleX(1); } }
  @keyframes v2-tick-pop { 0% { opacity: 0; transform: scale(0.4); } 60% { opacity: 1; transform: scale(1.25); } 100% { opacity: 1; transform: scale(1); } }
  .v2-tick { animation: v2-tick-pop 420ms var(--ease-out) both; }
  .v2-pipecard {
    box-shadow: inset 0 1px 0 var(--edge-highlight);
    transition: transform 260ms var(--ease-out), box-shadow 260ms var(--ease-out);
  }
  .v2-pipecard:hover { transform: translateY(-3px); box-shadow: var(--shadow-raised); }
  .v2-pipetab:hover .t-label { color: var(--text-primary) !important; }
  .v2-pipetab:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }
  /* data-motion is stamped on <html> by the head script: the OS preference
     sets the default, the visitor's chip overrides it in both directions.
     CSS guards read the attribute so they follow the same switch as the JS. */
  :root[data-motion="still"] .v2-tick,
  :root[data-motion="still"] .v2-pipecard,
  :root[data-motion="still"] [class*="v2-stage"] { animation: none !important; transition: none !important; }

  /* Client logos — neutral at rest, brand colour on hover.
     The marks are dark-inked artwork, so on an ink band they are knocked
     out to flat off-white instead of merely desaturated. */
  .v2-clientlogo, .v2-partnerlogo {
    filter: grayscale(1) contrast(0.85); opacity: 0.55;
    transition: filter 250ms var(--ease-out), opacity 250ms var(--ease-out);
  }
  [data-theme="dark"] .v2-clientlogo,
  [data-theme="dark"] .v2-partnerlogo {
    filter: grayscale(1) brightness(0) invert(1); opacity: 0.68;
  }
  .v2-logocell:hover .v2-clientlogo,
  [data-theme="dark"] .v2-logocell:hover .v2-clientlogo {
    filter: grayscale(0) contrast(1); opacity: 1;
  }

  /* Hero console — workflow tabs. They wrap rather than scroll: the console
     sits in a narrow column, so a single row would push two of the five out of
     sight behind a scrollbar nobody sees. The whole point of the strip is that
     the choices are discoverable. */
  /* The tab strip floats over open plate now that the mask opens at 62%, and
     over Singapore's lit towers the muted labels measured 1.19:1 — invisible.
     It gets the same glass the HUD chips use (78% bg-base), which puts the
     inactive labels at 4.66:1 and the active one at 9.68:1 against the
     brightest plate. The console below carries its own surface already; this
     makes the tabs read as the top of that instrument rather than type
     stranded on a skyline. */
  .v2-usecases {
    display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4);
    margin-bottom: var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--hud-chip);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-card);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  }
  .v2-usecase {
    flex: none; padding: 8px 0; background: none; border: none;
    border-bottom: 2px solid transparent; cursor: pointer; white-space: nowrap;
    font: 500 13px/1.3 var(--font-sans); color: var(--text-muted);
    transition: color 200ms var(--ease-in-out), border-color 200ms var(--ease-in-out);
  }
  .v2-usecase:hover { color: var(--text-primary); }
  .v2-usecase[aria-selected="true"] { color: var(--text-primary); border-bottom-color: var(--accent); }
  .v2-usecase:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }

  /* Hero console — background deepens on hover; the box itself holds still
     (the pointer tilt came out at client review) */
  .v2-console { transition: background 300ms var(--ease-out); }
  .v2-console:hover { background: var(--console-bg-hover) !important; }

  /* Hero console — step rows */
  .v2-step:hover { background: var(--tint-accent-weak) !important; }
  .v2-step:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
  :root[data-motion="live"] .v2-pulse { animation: v2-pulse 1.2s var(--ease-in-out) infinite; }
  @keyframes v2-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }

  /* Agent grid cell hover — surface lift, no scale (§19) */
  .v2-agent-cell { box-shadow: inset 0 1px 0 var(--edge-highlight); }
  .v2-agent-cell:hover { background: var(--surface-card) !important; }

  /* ============================================================
     Full-bleed media plates. The four generated city plates stop
     being 4:3 footnotes and become grounds: behind the hero,
     behind the footprint strip, behind the close. Per client
     instruction the plates run in their own colour — no duotone,
     no tint layer. What keeps the type legible is a fade INTO
     the band ground where the type is, not a wash over the whole
     frame:

       fade   brightness over the band's deep ground — the
              plate is lifted, not pushed back
       mask   an alpha ramp on the media itself. Where text
              lives the plate fades to the band ground; on the
              open side it plays at nearly full strength
       scrim  a vertical gradient holding the nav's strip at the
              top and the annotation row at the foot

     The mask stops are DERIVED, not eyeballed. p99 luminance
     was measured over the four posters per zone (canvas scan,
     2026-08-18): text column ≤40% width → 196, nav strip → 237,
     open side → 235.

     Second brightening, 21 Aug 2026 ("too dark and depressing —
     increase the transparency, show more of the video on the
     right"). Three moves, and the reason they compose: the
     footage is LIFTED (brightness 0.95 → 1.12, saturate back to
     1) rather than merely un-dimmed; the ramp reaches full
     strength at 62% instead of 72%, so the whole right third
     plays clear; and the scrims stop standing in for the mask
     (top 84% → 70%, foot 38% → 26%). The close, whose ramp
     never opened past 0.9, now reaches #000 at 82% — its right
     third carries no type at all.

     Brighter footage means the text zones must hold their floor
     on LESS mask, so those stops tightened rather than relaxed:

       lead zone   ≤34% width  mask ≤0.22 — the ramp is steeper
                   AND starts from a brighter plate, so the
                   ground under --text-secondary is no lighter
                   than before
       headline    ≤46% width  mask ≤0.62, off-white display
                   type (large text, needs 3:1)
       nav strip   84% bg-base scrim, and it must stay there.
                   Lifting it to 70% put the bar's 12px links at
                   3.25:1 over Mumbai's dawn haze — the brightest
                   top strip of the four plates, and the case
                   that governs. 0.84 is the first step that
                   clears 4.5. The brightening belongs on the
                   RIGHT of the frame, not the top: the nav lives
                   in that strip and the ramp does not reach it
       tab strip   the workflow tabs sit at 55–80% width, where
                   the ramp is now fully open. They measured
                   1.19:1 on bare plate, so they carry their own
                   .v2-hud-style ground → 4.66:1 inactive,
                   9.68:1 active
       phones      flat veil 0.24 → 0.20, because text can sit
                   at any x and the plate underneath is brighter

     A WARNING FOR WHOEVER TOUCHES THIS NEXT. The in-page
     contrast sweep cannot see any of it. It composites DOM
     backgrounds, and a plate is an <img>/<video>, not a
     background-color — so it reports zero failures over footage
     that is objectively illegible. Both regressions above passed
     the sweep. Text over a plate is checked by sampling the
     poster in that zone and compositing the mask arithmetic by
     hand (the method is in this file's history, and in the
     session that derived these stops), never by the sweep alone.

     Every figure above is checked by the in-page contrast sweep
     at 375 and 1280 after each change; the numbers here are the
     intent, the sweep is the proof.

     Point speculars (a light source, max 253) can exceed p99
     for a few pixels; they are why the bounds use p99 plus
     headroom rather than pretending a max. Below 900px the
     columns stack and text can sit anywhere, so the mask goes
     flat at 0.24 — atmosphere on a phone, legal at every x.
     ============================================================ */
  :root, [data-theme="light"], [data-theme="dark"] {
    --hud-chip:    color-mix(in srgb, var(--bg-base) 78%, transparent);
    /* the trail viewer's overlay — carbon on either theme */
    --modal-scrim: color-mix(in srgb, var(--color-deep-carbon) 62%, transparent);
    --scrim-top:   color-mix(in srgb, var(--bg-base) 84%, transparent);
    --scrim-foot:  color-mix(in srgb, var(--bg-base) 26%, transparent);
    --bleed-scrim-y: linear-gradient(180deg, var(--scrim-top) 0%, transparent 20%, transparent 66%, var(--scrim-foot) 100%);
    /* instrument frames — registration-grid line, kept near-subliminal */
    --frame-line:  color-mix(in srgb, var(--text-primary) 5%, transparent);
  }
  :root {
    --bleed-mute: 1;      /* the global fade came off at client review */
    --cinema-h:   clamp(320px, 52vh, 560px);   /* the footprint strip */
    /* the alpha ramps (black = plate shows). The colour literals here are
       mask alphas, not colours — same standing as the old lattice mask. */
    --bleed-mask: var(--bleed-mask-ramp);
    --bleed-mask-ramp: linear-gradient(90deg, transparent 0%, rgba(0,0,0,0.22) 34%, rgba(0,0,0,0.62) 46%, rgba(0,0,0,0.9) 56%, #000 62%);
    /* the close: its headline runs to 67% width, so the ramp holds longer
       and never fully opens — derived the same way as the hero's */
    --bleed-mask-cta: linear-gradient(90deg, transparent 0%, rgba(0,0,0,0.18) 36%, rgba(0,0,0,0.52) 55%, rgba(0,0,0,0.82) 70%, #000 82%);
    /* below 900px the columns stack and text can sit at any x, so the ramp
       gives way to this flat veil */
    --bleed-mask-flat: linear-gradient(90deg, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.2) 100%);
  }
  @media (max-width: 900px) {
    :root { --bleed-mask: var(--bleed-mask-flat); }
  }
  .v2-bleed {
    position: absolute; inset: 0; overflow: hidden;
    background: var(--bg-deepest);
  }
  .v2-bleed > video, .v2-bleed > img {
    position: absolute; inset: 0; width: 100%; height: 100%; display: block;
    object-fit: cover; object-position: 50% 50%;
    filter: saturate(1) brightness(1.12);
    opacity: var(--bleed-mute);
    mask-image: var(--bleed-mask);
    -webkit-mask-image: var(--bleed-mask);
    transition: opacity 600ms var(--ease-in-out);
  }
  /* crossfade partner: a stacked plate that is not the resting one */
  .v2-bleed > .is-under { opacity: 0; }
  .v2-bleed-scrim {
    position: absolute; inset: 0; pointer-events: none; z-index: 1;
    background-image: var(--bleed-scrim-y);
  }

  /* HUD chip — the only surface small mono gets over a plate. Measured and
     visible ground are the same thing: 78% bg-base over the duotone ceiling. */
  .v2-hud {
    display: inline-flex; align-items: center; gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    background: var(--hud-chip); border: 1px solid var(--border-subtle);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  }

  /* The footprint strip — all four plates at once, looping side by side.
     The 1px gap over the strip's own border-subtle ground is the hairline
     between cells, in both directions once the grid wraps. */
  .v2-strip {
    position: relative; display: grid; grid-template-columns: var(--cols-strip);
    gap: 1px; background: var(--border-subtle); height: var(--cinema-h);
  }
  .v2-strip-cell { position: relative; overflow: hidden; min-width: 0; }
  @media (max-width: 900px) {
    .v2-strip { height: auto; }
    .v2-strip-cell { aspect-ratio: 4 / 3; }
  }

  /* ============================================================
     Instrument frames — one chrome for every coded drawing on
     the page. A hairline box, a registration grid at 24px, four
     corner ticks and a mono caption row: the drawings were
     already one language (hairlines, mono leaders, --mark-live);
     the frame makes them one SYSTEM, read at a glance as figures
     in a technical document rather than decorations in cards.
     ============================================================ */
  .v2-frame {
    position: relative; border: 1px solid var(--border-subtle);
    background-image:
      repeating-linear-gradient(0deg, var(--frame-line) 0, var(--frame-line) 1px, transparent 1px, transparent 24px),
      repeating-linear-gradient(90deg, var(--frame-line) 0, var(--frame-line) 1px, transparent 1px, transparent 24px);
  }
  .v2-frame-cap {
    display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
    gap: var(--space-1) var(--space-4); padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-subtle);
  }

  input::placeholder { color: var(--text-muted); }

  /* Machine-mode markdown — per-line reveal */
  :root[data-motion="live"] .v2-md-line { animation: v2-md-in 350ms var(--ease-out) both; }
  @keyframes v2-md-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

  /* Pipeline stage swap — calm rise, ease-out */
  :root[data-motion="live"] .v2-stage-enter { animation: v2-stage 600ms var(--ease-out); }
  @keyframes v2-stage {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: none; }
  }

  /* The motion chip — a .v2-hud that is a button */
  .v2-motion-toggle { appearance: none; -webkit-appearance: none; cursor: pointer; }
  .v2-motion-toggle:hover { background: var(--tint-accent-weak); }
  .v2-motion-toggle:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

  /* ── The programme timelines on /events ───────────────────────
     One timeline per day: a spine down the left with a marker at
     every stop. The spine is the stop's own ::before, drawn from
     under its dot to the bottom of the row, so the last stop simply
     omits it and the line ends on the day's last marker instead of
     running into the next day's header. Breakpoints live only in
     this file, per the repo rule. */
  /* the RSVP's one checkbox: mint when ticked, nudged 2px so it sits on the
     first line of a label that wraps to two */
  .ev-check {
    width: 18px; height: 18px; flex: none; margin-top: 2px;
    accent-color: var(--accent); cursor: pointer;
  }

  .ev-programme { max-width: 880px; }

  .ev-timeline {
    list-style: none;
    margin: var(--space-6) 0 0;
    padding: 0;
  }
  .ev-stop {
    position: relative;
    display: grid;
    grid-template-columns: 28px minmax(0, 1fr);
    align-items: start;
    padding-bottom: var(--space-6);
  }
  /* the spine, from just under this stop's dot to the next one */
  .ev-stop::before {
    content: "";
    position: absolute;
    left: 5px;
    top: 18px;
    bottom: 0;
    width: 1px;
    background: var(--border-strong);
  }
  .ev-stop[data-last]::before { display: none; }
  .ev-stop[data-last] { padding-bottom: 0; }

  .ev-dot {
    width: 11px; height: 11px; margin-top: 7px; border-radius: 50%;
    border: 1px solid var(--border-strong); background: var(--bg-base);
  }
  /* a major stop is the substance of the day, so it takes the accent at
     full size; the meals and breaks around it get a smaller hollow marker
     and a tighter row, so the timeline reads as the things worth being
     there for with matter between them, not a list of equals */
  .ev-stop[data-kind="major"] .ev-dot {
    background: var(--accent); border-color: var(--accent);
  }
  .ev-stop[data-kind="minor"] .ev-dot {
    width: 7px; height: 7px; margin-top: 8px; margin-left: 2px;
  }
  .ev-stop[data-kind="minor"] { padding-bottom: var(--space-4); }

  /* the speaker credit under a session title: quieter than the title it
     belongs to, with the name carrying the weight and the affiliation
     trailing it, so the eye lands on the person rather than the institution */
  .ev-speaker {
    margin: var(--space-2) 0 0;
    font: 400 14px/1.4 var(--font-sans);
    color: var(--text-muted);
  }
  .ev-speaker .ev-speaker-name { font-weight: 500; color: var(--text-accent-small); }
  .ev-speaker a {
    font-weight: 500;
    color: var(--text-accent-small);
    transition: color 150ms var(--ease-in-out);
  }
  .ev-speaker a:hover { color: var(--text-accent); text-decoration: underline; }
  .ev-speaker a:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }

  /* the place line under a stop: a pin, the venue's name, and a link out to
     the map. Mint from the start, because it is the one thing here that is
     actually a control */
  .ev-place {
    display: inline-flex; align-items: center; gap: var(--space-2);
    margin-top: var(--space-2);
    font: 500 14px/1.4 var(--font-sans);
    color: var(--text-accent-small);
    transition: color 150ms var(--ease-in-out);
  }
  .ev-place:hover { color: var(--text-accent); text-decoration: underline; }
  .ev-place:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }

  .ev-stop-body { padding: var(--space-2) var(--space-4); }

  /* The timeline's entrance, behind the motion gate like everything else on
     the site: the stops arrive down the spine in order as their day scrolls
     in, each one a short rise with its marker landing a beat later, so a
     reader watches the day fill from the top rather than all at once. With
     motion off, nothing below applies and the timeline is simply there. */
  :root[data-motion="live"] .ev-timeline:not([data-in]) .ev-stop { opacity: 0; }
  :root[data-motion="live"] .ev-timeline[data-in] .ev-stop {
    animation: ev-stop-in 520ms var(--ease-out) both;
    animation-delay: calc(var(--i, 0) * 90ms);
  }
  /* the spine draws itself down from the marker it starts at */
  :root[data-motion="live"] .ev-timeline[data-in] .ev-stop::before {
    animation: ev-spine-in 420ms var(--ease-out) both;
    animation-delay: calc(var(--i, 0) * 90ms + 160ms);
    transform-origin: top center;
  }
  :root[data-motion="live"] .ev-timeline[data-in] .ev-dot {
    animation: ev-dot-in 380ms var(--ease-out) both;
    animation-delay: calc(var(--i, 0) * 90ms + 90ms);
  }
  @keyframes ev-stop-in {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes ev-spine-in {
    from { transform: scaleY(0); }
    to   { transform: scaleY(1); }
  }
  @keyframes ev-dot-in {
    from { opacity: 0; transform: scale(0.4); }
    to   { opacity: 1; transform: none; }
  }

  @media (max-width: 560px) {
    .ev-stop { grid-template-columns: 20px minmax(0, 1fr); }
    .ev-stop::before { left: 4px; }
    .ev-dot { width: 9px; height: 9px; }
    .ev-stop-body { padding: var(--space-1) var(--space-3); }
  }

  /* ── The contact page grids ────────────────────────────────────
     Form beside its live trail; the pair row inside the form; the
     three-step strip. */
  .ct-grid { grid-template-columns: minmax(320px, 1fr) minmax(0, 1.2fr); }
  .ct-pair { grid-template-columns: 1fr 1fr; }
  .ct-next { grid-template-columns: 1fr 1fr 1fr; }
  @media (max-width: 960px) {
    .ct-grid { grid-template-columns: 1fr; }
    .ct-next { grid-template-columns: 1fr; }
    .ct-next-cell { border-left: none !important; border-top: 1px solid var(--border-subtle); }
  }
  @media (max-width: 560px) {
    .ct-pair { grid-template-columns: 1fr; }
  }
  /* form polish: focus takes the ring colour, the send button deepens and
     lifts a hair on hover */
  .ct-input:focus { border-color: var(--focus-ring) !important; }
  .ct-send:hover { background: var(--button-fill-hover) !important; transform: translateY(-1px); }
  .ct-send:active { transform: translateY(0); }
  /* the headline types itself on: per-character rise, base state whole */
  .ct-type span { display: inline-block; }
  :root[data-motion="live"] .ct-type span {
    opacity: 0; transform: translateY(8px);
    animation: ct-type-in 200ms cubic-bezier(0.16, 1, 0.3, 1) both;
  }
  @keyframes ct-type-in { to { opacity: 1; transform: none; } }

  /* ── Solutions by industry ─────────────────────────────────────
     The workflow card grid: hairlines from a 1px gap over the
     border colour, the last card spanning the row (the events-grid
     move). Cards rise in on tab switch — the panel remounts, the
     per-card delay rides an inline style so this shorthand cannot
     flatten it — and the intro keeps a two-line slot so the grid
     below it never moves. Base state is visible: with motion off
     the guard never fires and nothing hides. */
  .sol-grid { grid-template-columns: 1fr 1fr; gap: 1px; }
  .sol-intro { min-height: 56px; }
  /* the industry control outgrows a phone; it scrolls in its own lane
     instead of widening the page */
  .sol-tabs { max-width: 100%; overflow-x: auto; }
  @media (max-width: 900px) {
    .sol-grid { grid-template-columns: 1fr; }
    .sol-intro { min-height: 0; }
  }
  .sol-card { transition: background 200ms var(--ease-out); }
  .sol-card:hover { background: var(--surface-card) !important; }
  /* the workflow glyphs: hairline stroke, muted at rest, the accent when
     the card lifts. createIcons copies the class onto the svg it swaps in. */
  .sol-icon {
    display: block; width: 24px; height: 24px; stroke-width: 1.5;
    color: var(--text-muted); transition: color 200ms var(--ease-out);
  }
  .sol-card:hover .sol-icon { color: var(--text-accent-small); }

  /* ── The About founding rail ───────────────────────────────────
     One line, three markers. Desktop: horizontal, the line drawing
     left to right on the locked rail timing, markers landing in
     sequence (year, then title, then detail — delays ride inline
     styles). Phone: the rail turns vertical down the left. Base
     state everywhere is the finished timeline: with motion off or
     JS rendering done, everything is simply there. A focused
     marker steps up; its siblings step back; nothing moves. */
  .ab-rail { position: relative; display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--gap-grid); }
  .ab-line {
    position: absolute; top: 4px; left: 0; right: 0; height: 1px;
    background: var(--border-strong); transform-origin: left;
  }
  .ab-mark { position: relative; transition: opacity 200ms var(--ease-out); }
  .ab-rail[data-active] .ab-mark:not(.is-active) { opacity: 0.45; }
  .ab-dot {
    display: block; width: 9px; height: 9px; border-radius: 50%;
    background: var(--text-accent-small); margin-bottom: var(--space-4);
    transition: transform 200ms var(--ease-out), background 200ms var(--ease-out);
  }
  .ab-mark.is-active .ab-dot { transform: scale(1.3); background: var(--accent); }
  .ab-detail { color: var(--text-muted); transition: color 200ms var(--ease-out); }
  .ab-mark.is-active .ab-detail { color: var(--text-secondary); }
  :root[data-motion="live"] .ab-line { animation: ab-draw 900ms cubic-bezier(0.16, 1, 0.3, 1) both; }
  :root[data-motion="live"] .ab-dot,
  :root[data-motion="live"] .ab-in { animation: ab-land 200ms cubic-bezier(0.16, 1, 0.3, 1) both; }
  @keyframes ab-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
  @keyframes ab-draw-y { from { transform: scaleY(0); } to { transform: scaleY(1); } }
  @keyframes ab-land { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
  @media (max-width: 900px) {
    .ab-rail { grid-template-columns: 1fr; }
    .ab-line {
      top: 0; bottom: 0; left: 4px; right: auto; width: 1px; height: auto;
      transform-origin: top;
    }
    :root[data-motion="live"] .ab-line { animation-name: ab-draw-y; }
    .ab-mark { display: grid; grid-template-columns: auto 1fr; gap: var(--space-4); }
    .ab-dot { margin-bottom: 0; margin-top: 4px; }
  }

  /* ── The FAQ page — served static ──────────────────────────────
     faq.html is plain HTML by design (the AEO rules demand every
     answer in the source with JS off), so its type and layout live
     here as classes rather than inline component styles. The rail
     is sticky navigation on desktop and a pinned chip row on a
     phone; it never hides content. */
  .faq-hero {
    background: var(--bg-base); background-image: var(--band-lift);
    border-bottom: 1px solid var(--border-subtle);
  }
  .faq-hero-inner {
    max-width: var(--content-max); margin: 0 auto;
    padding: calc(var(--nav-height, 72px) + var(--gap-band)) var(--page-pad) var(--gap-band);
  }
  .faq-eyebrow {
    display: block; font: 500 12px/1 ui-monospace, "SF Mono", Menlo, monospace;
    letter-spacing: 1.2px; text-transform: uppercase; color: var(--text-secondary);
  }
  .faq-hero h1 {
    margin: var(--gap-lead) 0 0; max-width: 900px;
    font: 700 clamp(44px, 5.2vw, 80px)/1.05 var(--font-sans);
    letter-spacing: -1.5px; color: var(--text-primary); text-wrap: balance;
  }
  .faq-sub {
    margin: var(--gap-lead) 0 0; max-width: 640px;
    font: 400 17px/1.6 var(--font-sans); color: var(--text-secondary);
  }
  .faq-stamp {
    display: block; margin-top: var(--gap-lead);
    font: 400 11px/1 ui-monospace, "SF Mono", Menlo, monospace;
    letter-spacing: 1.2px; text-transform: uppercase; color: var(--text-muted);
  }
  .faq-band { background: var(--bg-base); border-bottom: 1px solid var(--border-subtle); }
  .faq-wrap {
    max-width: var(--content-max); margin: 0 auto;
    padding: var(--gap-band) var(--page-pad);
    display: grid; grid-template-columns: minmax(200px, 1fr) minmax(0, 3fr);
    gap: var(--gap-col); align-items: start;
  }
  .faq-nav {
    position: sticky; top: calc(var(--nav-height, 72px) + var(--space-6));
    display: flex; flex-direction: column; gap: var(--space-1);
  }
  .faq-nav a {
    font: 500 14px/1.4 var(--font-sans); color: var(--text-muted);
    text-decoration: none; padding: var(--space-2) 0 var(--space-2) var(--space-3);
    border-left: 2px solid transparent;
    transition: color 200ms var(--ease-out), border-color 200ms var(--ease-out);
  }
  .faq-nav a:hover { color: var(--text-primary); }
  .faq-nav a[aria-current="true"] { color: var(--text-primary); border-left-color: var(--accent); }
  .faq-cat + .faq-cat {
    margin-top: var(--gap-group); padding-top: var(--gap-group);
    border-top: 1px solid var(--border-subtle);
  }
  .faq-cat h2 {
    margin: 0; font: 600 clamp(28px, 2.6vw, 36px)/1.15 var(--font-sans);
    letter-spacing: -0.5px; color: var(--text-primary);
    scroll-margin-top: calc(var(--nav-height, 72px) + var(--space-6));
  }
  .faq-qa h3 {
    margin: var(--gap-lead) 0 0; font: 600 18px/1.35 var(--font-sans);
    letter-spacing: -0.2px; color: var(--text-primary);
    scroll-margin-top: calc(var(--nav-height, 72px) + var(--space-6));
  }
  .faq-qa p {
    margin: var(--space-3) 0 0; max-width: 680px;
    font: 400 16px/1.7 var(--font-sans); color: var(--text-secondary);
  }
  .faq-qa a { color: var(--text-primary); text-underline-offset: 3px; }
  @media (max-width: 900px) {
    .faq-wrap { grid-template-columns: 1fr; }
    .faq-nav {
      top: var(--nav-height, 56px); z-index: 5;
      flex-direction: row; overflow-x: auto; gap: var(--space-2);
      background: var(--bg-base); padding: var(--space-3) 0;
      border-bottom: 1px solid var(--border-subtle);
    }
    .faq-nav a {
      white-space: nowrap; flex: none; font-size: 13px;
      border: 1px solid var(--border-subtle); border-radius: var(--radius-card);
      padding: var(--space-2) var(--space-3);
    }
    .faq-nav a[aria-current="true"] {
      color: var(--text-primary); border-color: var(--border-strong);
      background: var(--surface-card);
    }
  }

  /* ── The blog index grid ───────────────────────────────────────
     Three across; the featured card spans two of the three columns
     and leaves the third as air. On a phone everything is one
     column and the span comes off (!important, because the span is
     an inline style on the card). The whole card is the link; the
     arrow nudges with it. */
  .bl-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  @media (max-width: 900px) {
    .bl-grid { grid-template-columns: 1fr; }
    .bl-card { grid-column: auto !important; }
  }
  .bl-card { transition: border-color 200ms var(--ease-out), box-shadow 200ms var(--ease-out); }
  .bl-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-raised); }
  .bl-arrow { transition: transform 200ms var(--ease-out); }
  .bl-card:hover .bl-arrow { transform: translateX(4px); }

  /* ── The engineered mark on /about ─────────────────────────────
     Manan's drawing of the Pints mark, re-set on the tokens: ring
     and crosshairs as hairlines, blades in ink and the small
     accent. The ring turns like a dial (guarded by the motion
     switch), the blades land in sequence, and a click rotates the
     six-point mark one sixth of a turn — onto itself. */
  /* the LinkedIn badge on the founder cards: muted at rest, the whole
     block lights to full ink on hover — no underline */
  .ab-li { color: var(--text-secondary); transition: color 200ms var(--ease-out); }
  .ab-li:hover { color: var(--text-primary); }

  /* the mark centres itself in its column and against the prose opposite;
     on a phone it returns to the flow, centred under the label */
  .ab-markwrap {
    position: absolute; inset: 0; display: flex;
    align-items: center; justify-content: center; pointer-events: none;
  }
  .ab-markwrap .abm { width: 288px; pointer-events: auto; }
  @media (max-width: 900px) {
    .ab-markwrap { position: static; display: block; margin-top: var(--gap-lead); }
    .ab-markwrap .abm { margin: 0 auto; }
  }
  .abm { display: block; width: 100%; height: auto; cursor: pointer; }
  .abm-ring, .abm-tick { stroke: var(--border-strong); transition: stroke 200ms var(--ease-out); }
  .abm-dot { fill: var(--text-muted); }
  /* the mark in the logo's own two tones */
  .abm-a { fill: var(--color-brand-ink); }
  .abm-b { fill: var(--color-brand-slate); }
  .abm:hover .abm-ring, .abm:hover .abm-tick { stroke: var(--text-muted); }
  .abm-spinner { transform-box: fill-box; transform-origin: center; }
  .abm-mark {
    transform-box: fill-box; transform-origin: center;
    transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  :root[data-motion="live"] .abm-spinner { animation: abm-spin 48s linear infinite; }
  :root[data-motion="live"] .abm-blade { animation: ab-land 200ms cubic-bezier(0.16, 1, 0.3, 1) both; }
  @keyframes abm-spin { to { transform: rotate(360deg); } }
  :root[data-motion="live"] .sol-card { animation: sol-card-in 200ms var(--ease-out) both; }
  :root[data-motion="live"] .sol-fade { animation: sol-fade-in 200ms var(--ease-out) both; }
  @keyframes sol-card-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes sol-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
