/* RedactWall marketing site — dark security-console language.
   Palette from web/brand: phosphor #FF5A2B / #FF9152 / #FFC08A on near-black.
   No external requests: no web fonts, no CDN, no remote images. */

/* Palette is the operator console's own — "RedactWall Phosphor" — so the site a
   customer reads and the console they log into are visibly one product.
   Token values are lifted from web/console's stylesheet; names are kept as they
   were so no existing page has to change. */
:root {
  color-scheme: dark;

  --bg: #04060a;          /* console --ground */
  --bg-raise: #0a0e15;    /* console --surface */
  --card: #111621;        /* console --surface-raised */
  /* --inset (#070a10) was deleted: at 1.024:1 from --bg it never actually
     inset anything, and every consumer sat on --bg. Those fills now use
     --bg-raise (1.049:1 vs --bg — the delta is imperceptible, the name true). */
  --line: #2c3648;        /* console --border-strong */
  --line-control: #516485; /* 3.39:1 on --bg. Same hue axis as --line, lightness raised:
                              a .btn has no icon and no link colour, so its 1px hairline is
                              the only thing saying "control", and 1.4.11 asks 3:1 of that. */
  --line-soft: #1b2231;   /* console --border */
  --line-quiet: #141a26;
  --line-container: #44536f; /* 2.62:1 on --bg: the edge for containers whose
                                fill is near-indistinguishable from the ground
                                (.scen li, .keep .kdoc, .flow .fgate).
                                DELIBERATELY below the 3:1 non-text floor
                                (2.50:1 on --bg-raise, 2.34:1 on --card): these
                                edges are decorative, no container's identity
                                depends on them (.scen li keeps its 6.5:1 --phos
                                left bar). NEVER the boundary of a control —
                                anything .btn-shaped takes --line-control. */
  --ink: #e8ecf4;
  --ink-dim: #8b94a8;
  --ink-faint: #828ca6;   /* 5.19:1 on --card; #78829b was 4.709:1 with no overlay at all */

  /* The mark's phosphor. Full chroma belongs to the brand and to severity; the
     accent family stays under ~25% — washes, hairlines, glows, focus rings. */
  --phos: #ff5a2b;
  --phos-2: #ff9152;
  --phos-3: #ffc08a;
  --accent-deep: #c43b14;   /* graphics + >=24px text only: 3.43:1 on --card, 3.85:1 on --bg. Never body text. */
  --accent-deep-2: #e04a1e; /* the primary button gradient's deep stop. --on-accent on it is 4.81:1 —
                               the floor is 4.5 (13.5px/700 is not large text), so do not darken. */
  --accent-edge: #3a2417;   /* warm hairline: decide-chip border, hover card edge, wall-node edge */
  --on-accent: #160904;     /* ink on phosphor fills (.skip, .btn.primary) */
  --header-ground: #0a0c10; /* header composite (used at 92% alpha there) and .bar ink.
                               On the h1 bar gradient its darkest stop is --accent-deep: 3.71:1
                               against the 3:1 large-text floor — do not darken that stop. */

  /* Grounds and edges that used to live inline in component rules. */
  --term-ground: #0c0e12;
  --code-ground: #191412; --code-edge: #2a201a;

  /* Status-chip roles: each foreground pairs with its own tinted container
     (5.94–10.74:1 measured) — the model the rest of the palette follows. */
  --status-detect: #7fb4e0;  --status-detect-edge: #24313f;  --status-detect-ground: #0d1620;
  --status-partial: #e0c56a; --status-partial-edge: #3a3117; --status-partial-ground: #191507;
  --status-proven-edge: #1e3320; --status-proven-ground: #0c150d;
  --status-decide-ground: #191007;
  --status-complement-edge: #26303a;
  --status-alert: #ffb9a3;
  --alert: 242 86 95;       /* alert red, as a triplet: rgb(var(--alert) / 0.14) etc. — no hex token existed for this hue */

  /* Cool counterweight, for measurement rather than status. */
  --signal: #4de0e8;
  --ok: #74d3a6;

  --mono: ui-monospace, "Cascadia Mono", "Cascadia Code", "JetBrains Mono", Consolas, monospace;
  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* One measure for secondary prose. In this sans stack 1ch (the advance of
     "0") is 1.2353x the average glyph advance — measured over 5,750 characters
     of this site's own copy, identically at 13/14/16/18px — so 62ch renders
     ~75 characters. That factor is also why the old 74ch values measured 90+
     characters and are corrected below. In --mono, ch is exactly one
     character: footer .disclaimer's 74ch is correct as written. */
  --measure: 62ch;
  --lift: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 30px 80px -30px rgba(0, 0, 0, 0.9);
  --glow: 0 0 0 1px rgba(255, 90, 43, 0.07), 0 30px 80px -30px rgba(0, 0, 0, 0.9);
  --radius: 9px;

  /* ── Type scale ──────────────────────────────────────────────────────────
     rem so the site answers the browser's own text-size setting (every size
     was hard px before, so raising the default font size changed nothing on
     any page). Each size is paired with an absolute line-height so line boxes
     land on the 4dp grid instead of half-pixel multiples; only the fluid
     display size keeps a ratio. Every adjacent step is >=9%.
     The mixed-case chips (.tag, .verdict, .stamp, .kchip) are a separate
     chip/badge role and deliberately stay off this scale. */
  --t-label: 0.6875rem;   --t-label-lh: 1rem;       /* 11 / 16 — uppercase mono labels only */
  --t-caption: 0.75rem;   --t-caption-lh: 1rem;     /* 12 / 16 */
  --t-body-sm: 0.875rem;  --t-body-sm-lh: 1.25rem;  /* 14 / 20 */
  --t-body: 1rem;         --t-body-lh: 1.5rem;      /* 16 / 24 */
  --t-title: 1.125rem;    --t-title-lh: 1.75rem;    /* 18 / 28 */
  --t-headline: 1.625rem; --t-headline-lh: 2rem;    /* 26 / 32 */
  --t-display: clamp(1.875rem, 5vw, 3.125rem); --t-display-lh: 1.12;
  --t-mono-body: 0.84375rem; --t-mono-body-lh: 1.4375rem; /* 13.5 / 23 — terminals keep their own role */

  /* ── Spacing scale (4dp grid) ────────────────────────────────────────────
     Applied per component as blocks are touched; values that are deliberate
     exceptions (the .wall 2px brick gap, the .tag 2px 8px chip, inline-code
     1px 6px, the equal-and-opposite 28px nav hover bridge) stay off it. */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px; --sp-5: 20px; --sp-6: 24px;
  --sp-8: 32px; --sp-10: 40px; --sp-11: 44px; --sp-12: 48px; --sp-14: 56px; --sp-22: 88px;
}

/* ── Atmosphere: the console's CRT treatment ───────────────────────────────
   A one-in-three scanline and a phosphor haze off the top-right corner. Both
   are fixed, both ignore the pointer, both sit above the content deliberately
   (a scanline behind the page is a scanline nothing shows through). BOTH PAINT
   ABOVE THE CONTENT, so their alpha is subtracted from every contrast ratio
   underneath -- at the old 0.09 the haze pulled --ink-faint on --card from
   4.709:1 down to 4.27-4.35:1, a shipped 1.4.3 AA failure on the 10.5-12.5px
   labels. Measured ceiling including a scanline row is alpha 0.033. Purely decorative:
   neither carries any meaning. */
body::before {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 99;
  background: repeating-linear-gradient(
    to bottom, rgba(255, 255, 255, 0.014) 0 1px, transparent 1px 3px);
}
body::after {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 98;
  background: radial-gradient(120% 60% at 82% 0%, rgba(255, 90, 43, 0.03), transparent 60%);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* 100%, not a px: the whole scale above is rem, so this is the one place the
   reader's own browser text-size setting enters the cascade. */
html { font-size: 100%; scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: var(--t-body-lh);
  -webkit-font-smoothing: antialiased;
}

.wrap { max-width: 1080px; margin: 0 auto; padding: 0 24px; }

/* A link inside prose is separated from the text around it by HUE ALONE at
   1.37:1 -- --phos-2 against --ink-dim -- and WCAG technique G183 asks 3:1 of a
   colour-only distinction. No colour in this palette reaches it; --phos-3 tops
   out at 1.91:1. That makes the underline the only available carrier, and its
   absence WCAG 2.2 failure F73 at Level A, across 81-99 mid-sentence links on
   24-28 of the 33 pages. The underline is 70% rather than 55% because at 55% it
   composites to ~#8E5231 = 2.93:1 on --card, just under the 3:1 non-text floor
   on the surface most of these links sit on. 70% gives ~#B86C43 = 4.54:1. */
a {
  color: var(--phos-2);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, var(--phos-2) 70%, transparent);
}
a:hover, a:focus-visible { color: var(--phos-3); text-decoration: underline; }

/* Chrome opts out: these are identified by position and shape, not by sitting
   inside a sentence, and F73 governs links within blocks of text. */
.skip, .nav a, .navshell > summary, .sitemap a,
.btn, .cta-nav, .faq summary { text-decoration: none; }
.sitemap a:hover, .sitemap a:focus-visible { text-decoration: underline; }

/* ---- focus ---- */
:focus-visible {
  outline: 2px solid var(--phos-2);
  outline-offset: 2px;
}
[id] { scroll-margin-top: 76px; }

.skip {
  position: absolute; left: 12px; top: -120px; z-index: 50;
  background: var(--phos); color: var(--on-accent); font-weight: 700;
  font-family: var(--mono); font-size: var(--t-body-sm); letter-spacing: 0.04em;
  padding: var(--sp-3) var(--sp-4); border-radius: 0 0 6px 6px;
}
.skip:focus { top: 0; text-decoration: none; color: var(--on-accent); }

/* ---- header ---- */
header {
  border-bottom: 1px solid var(--line);
  background: rgba(10, 12, 16, 0.92);  /* --header-ground at 92% */
  position: sticky; top: 0; z-index: 10;
  backdrop-filter: blur(8px);
}
/* min-height, not height: the row can grow, and a fixed-height flex parent let
   it render outside the header's painted background on every phone.
   position: relative makes this row the containing block for BOTH the burger
   sheet and the mega panels, which is what keeps either of them from ever
   sticking out past the page's own content width. */
.nav {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--sp-2) 28px; min-height: 60px; padding: var(--sp-2) 0;
  position: relative;
}
.nav .logo { display: flex; align-items: center; }
.nav .logo .lockup { height: 26px; width: auto; display: block; }

/* ════════════════════════════════════════════════════════════════════════════
   NAVIGATION — one markup tree, two layouts, zero JavaScript
   There is no script-src in the CSP at all, so none of this can be scripted.

   Small screens (default, below 1001px): the <details> IS the burger. Its
   <summary> is the control — keyboard operable and announced as a disclosure
   for free — and the whole nav lives inside it, so when it is closed the
   browser does not render the nav at all. That is the fix for the old bug
   where absolutely-positioned panels with a min-width still contributed to
   scrollWidth while "hidden": closed <details> content contributes nothing.
   Open, the nav is an absolutely-positioned sheet pinned to the page's content
   edges (left/right: 24px), so it cannot be wider than the viewport either.

   Wide screens: the same markup becomes a horizontal bar with mega panels.
   That needs the closed <details> content forced visible, which only
   ::details-content can do — so the whole wide layout is nested inside
   @supports for it. An engine that lacks the pseudo simply keeps the burger at
   every width: every link stays reachable, nothing is hidden behind a control
   that is not there. Fail-safe in both directions.
   ════════════════════════════════════════════════════════════════════════════ */

.navshell { margin-left: auto; }
.navshell > summary {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3); min-height: 48px; border: 1px solid var(--line); border-radius: 6px;
  color: var(--ink-dim); font-family: var(--sans); font-size: var(--t-body-sm);
  font-weight: 600; letter-spacing: normal;
}
.navshell > summary::-webkit-details-marker { display: none; }
.navshell > summary:hover, .navshell > summary:focus-visible,
.navshell[open] > summary { color: var(--phos-2); border-color: var(--phos); }
/* Three rules that become an X, drawn from two pseudo-elements: the middle bar
   is the top bar's own box-shadow. */
.burger .bars { display: block; position: relative; width: 16px; height: 12px; }
.burger .bars::before, .burger .bars::after {
  content: ""; position: absolute; left: 0; width: 100%; height: 2px;
  background: currentColor; border-radius: 1px;
}
.burger .bars::before { top: 0; box-shadow: 0 5px 0 currentColor; }
.burger .bars::after { top: 10px; }
.navshell[open] .burger .bars::before { top: 5px; box-shadow: none; transform: rotate(45deg); }
.navshell[open] .burger .bars::after { top: 5px; transform: rotate(-45deg); }

.navshell > nav {
  position: absolute; top: calc(100% + 6px); left: 24px; right: 24px; z-index: 40;
  display: flex; flex-direction: column; align-items: stretch;
  padding: 8px; background: var(--card);
  border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--lift);
  /* The sheet scrolls itself rather than growing the header past the screen.
     160px is the worst case above it: the pre-release banner at its tallest
     wrap (74px), the 60px header row, the 6px gap, and a margin at the foot so
     the last row is not flush with the bottom edge. dvh second, so browsers
     that have it use the visible viewport rather than the toolbar-less one. */
  max-height: calc(100vh - 160px);
  max-height: calc(100dvh - 160px);
  overflow-y: auto; overscroll-behavior: contain;
}
/* Nav labels read in the site sans at 14px, sentence case — the HTML already
   carries title-case labels, so no text-transform is the whole change. The
   old mono/uppercase/12px/letterspaced treatment read as terminal chrome, not
   navigation (owner feedback, 2026-08). The mono-uppercase role stays where
   it belongs: the wordmark, the .menu .sep eyebrows, and section eyebrows. */
.nav nav a {
  color: var(--ink-dim);
  font-family: var(--sans);
  font-size: var(--t-body-sm);
  letter-spacing: normal;
}
/* Hover/focus are hue-only; the CURRENT-PAGE marker is not in this block any
   more. As one shared declaration the "you are here" cue was a 1.37:1 hue
   rotation on desktop and, in the burger sheet, nothing at all — the
   (0,3,1) group-trigger rule below paints every trigger --phos-2 and outranks
   a.here at (0,2,2). The marker is now a geometric indicator, below. */
.nav nav a:hover, .nav nav a:focus-visible {
  color: var(--phos-2); text-decoration: none;
}
/* Current section, in the sheet: full-width block rows take a leading inset
   bar, not an underline. box-shadow rather than border so nothing shifts 2px.
   [aria-current] is what tools/apply-partials.ps1 now stamps; .here is kept as
   an alias so pages built before the next chrome re-run keep their marker. */
.nav nav .grp > .top.here,
.nav nav .grp > .top[aria-current] { box-shadow: inset 2px 0 0 var(--phos); }
/* THE BURGER SHEET IS THE WHOLE NAVIGATION ON EVERY TOUCH DEVICE, because the
   desktop bar is gated on `(min-width:1001px) and (hover:hover)` -- a 1280px
   tablet gets the sheet too, as does any engine without
   selector(::details-content). Every target in it is sized to Material 3's
   48dp floor. Measured before: summary 36.6px, group triggers 33.8px, menu rows
   37.1px with 0px between them, Log in 40.6px, the CTA 38.6px. The sheet
   already scrolls (max-height + overflow-y at the rule above), so the added
   column height is absorbed rather than clipped. */
.nav nav > a { display: block; padding: 14px 11px; min-height: 48px; border-radius: 6px; font-weight: 600; }
.nav nav > a:hover, .nav nav > a:focus-visible { background: rgba(255, 255, 255, 0.08); }

/* Every group is a labelled block in the sheet; there is no second disclosure
   to hunt for, and each group's trigger is still a real link to a real page. */
.grp { display: block; }
.grp + .grp { border-top: 1px solid var(--line-quiet); margin-top: 4px; padding-top: 4px; }
.nav nav .grp > .top {
  display: block; padding: 14px 11px 10px; min-height: 48px; color: var(--phos-2); font-size: var(--t-body-sm); font-weight: 600;
}
.menu { display: block; }
.menu .col { display: block; min-width: 0; }
/* Specificity note: `.nav nav a` is 0-1-2, so the menu's own rules are
   qualified to 0-2-2 to outrank it. The base rule is itself sans/normal-case
   now; the explicit resets here are belt-and-braces so these rows cannot
   regress if that base treatment ever changes. */
.nav nav .menu a {
  display: block; padding: 12px 11px; min-height: 48px; border-radius: 6px;
  text-transform: none; letter-spacing: 0; font-family: var(--sans); font-size: var(--t-body-sm);
}
/* An M3 state layer lightens on a dark surface: the on-surface colour is light,
   so the layer must move toward it. --bg-raise moved the other way (1.068:1,
   and darker), and as the only background state layer in this file it set the
   precedent for every one added after it. */
.nav nav .menu a:hover, .nav nav .menu a:focus-visible { background: rgba(255, 255, 255, 0.08); text-decoration: none; }
.nav nav .menu a .t {
  display: block; color: var(--ink); font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); font-weight: 600;
  text-transform: none; letter-spacing: 0; font-family: var(--sans);
}
.nav nav .menu a:hover .t, .nav nav .menu a:focus-visible .t { color: var(--phos-2); }
/* In the sheet the one-line descriptions would triple its height, so the
   titles carry it there and the descriptions return on wide screens. */
.nav nav .menu a .d { display: none; }
.menu .sep {
  display: block; margin: var(--sp-2) var(--sp-3) 2px;
  font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-faint);
}
/* A second section inside one column needs air above it. */
.menu a + .sep { margin-top: var(--sp-3); }
/* Adjacent rows had 0px between them; 2.5.8 asks that targets not abut. */
.nav nav .menu a + a { margin-top: 4px; }
.nav .cta-nav {
  border: 1px solid var(--line); border-radius: 6px; padding: 13px 14px; min-height: 48px;
  color: var(--phos-2); text-align: center; margin-top: 8px; font-weight: 600;
}
.nav .cta-nav:hover, .nav .cta-nav:focus-visible { border-color: var(--phos); color: var(--phos-3); }

/* `hover: hover` as well as the width: a 1366px tablet has the room for the bar
   but no pointer to open a panel with, and the burger is the better nav there. */
@media (min-width: 1001px) and (hover: hover) {
  @supports selector(::details-content) {
    .navshell > summary { display: none; }
    .navshell::details-content { content-visibility: visible; block-size: auto; }

    .nav nav {
      position: static; z-index: auto;
      display: flex; flex-direction: row; align-items: center; flex-wrap: wrap;
      gap: var(--sp-2) var(--sp-6); padding: 0; max-height: none; overflow: visible;
      background: none; border: 0; border-radius: 0; box-shadow: none;
    }
    /* The 48dp sheet floor is a TOUCH floor and is released here, but not to
       zero: 2.5.8's 24px applies to a mouse too, and these were 21px. Measured
       at 24px the nav row and .grp both stay 37px, so the hover bridge below is
       untouched. */
    .nav nav > a { display: inline-flex; align-items: center; padding: 0; min-height: 24px; }
    .nav nav > a:hover, .nav nav > a:focus-visible { background: none; }
    .nav .cta-nav { display: inline-flex; align-items: center; padding: 7px 14px; margin-top: 0; min-height: 24px; }

    /* THE POINTER HAS TO CROSS FROM THE LABEL TO THE PANEL WITHOUT LEAVING THE
       GROUP, or the panel closes on the way down.
       `align-self: stretch` was claimed to make the group as tall as the row.
       It does not: the group's flex container is the <nav> inside the burger's
       <details>, whose own height is its content (37px), not the 60px header
       row -- so stretching filled 37px and left a ~22px dead band under every
       label. Measured, not assumed: trigger bottom y=83, panel top y=105.
       So the group grows a transparent band downward with padding, and an equal
       negative margin keeps the layout exactly where it was. The band is gated
       on `nav:hover` so it exists ONLY while the pointer is somewhere in the
       navigation -- an always-on invisible strip under a sticky header would
       swallow clicks on the top of the page underneath it.
       Re-measured 2026-08-24, after the nav labels went sans/14px: the inner
       nav row is 40px (the CTA: 24px line box + 14px padding + 2px border),
       .grp stretches to fill it, trigger bottom y=81, panel top y=99 with the
       banner at one line -- an 18px crossing from the trigger's bottom, 10px
       from the group's. The label size change moved NOTHING (line-height in
       the bar is a fixed inherited 24px, so the line box was 24px at 12px
       text and is 24px at 14px); the 28px band still covers with 10px spare. */
    .grp { display: flex; align-items: center; align-self: stretch; transition: padding-bottom 0s linear 300ms, margin-bottom 0s linear 300ms; }
    /* CLOSE GRACE, band half: opening is instant — while nav is hovered the
       rule below applies and its transition: none wins — but on leaving, the
       base transition above holds the band in place for another 300ms, so a
       brief pointer excursion out of the nav does not tear the bridge down
       mid-crossing (a band vanishing early closes the panel just as surely as
       the panel closing early). 0s duration: the band snaps rather than
       animates, and since padding and margin always change together and
       cancel, layout never shifts at any point in the delay. Re-entering the
       still-present band re-matches nav:hover and cancels the collapse. */
    .nav nav:hover .grp { padding-bottom: 28px; margin-bottom: -28px; transition: none; }
    .grp + .grp { border-top: 0; margin-top: 0; padding-top: 0; }
    .nav nav .grp > .top { display: inline-flex; align-items: center; padding: 0; min-height: 24px; color: var(--ink-dim); font-size: var(--t-body-sm); }
    .nav nav .grp > .top:hover, .nav nav .grp > .top:focus-visible { color: var(--phos-2); }
    /* Current section, in the bar: the trigger is a text-sized flex item here,
       so an underline reads correctly — box-shadow, not border-bottom, so
       nothing shifts 2px. --phos on the header composite is 6.31:1. */
    .nav nav .grp > .top.here,
    .nav nav .grp > .top[aria-current] {
      color: var(--phos-2);
      box-shadow: inset 0 -2px 0 var(--phos);
    }
    .grp > .top::after {
      content: "\25BE"; font-size: 9px; margin-left: 5px;
      color: var(--ink-faint); vertical-align: middle;
    }

    /* The panel spans the row's full content width, pinned to both content
       edges. Two things fall out of that, and both were bugs before it.
       It cannot overflow the page, because it is pinned rather than sized.
       And it sits directly under EVERY trigger, so there is no sideways travel
       between a label and its panel -- a right-anchored panel put Resources'
       card ~100px to the right of the word "Resources", and the pointer left
       the trigger's hover band crossing that space, which closed the panel it
       was on its way to. */
    .menu {
      /* Flush under the header row, not floated 10px below it. The 10px gap was
         dead space the pointer had to cross, and a diagonal move toward a
         right-anchored panel drifted sideways out of the trigger's hover band
         before reaching it. Seating the panel against the row removes the
         crossing rather than trying to bridge it. */
      position: absolute; top: 100%; left: 24px; right: 24px; z-index: 30; margin-top: 0;
      display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 252px);
      justify-content: start; gap: 0 var(--sp-4);
      padding: var(--sp-4); background: var(--card); border: 1px solid var(--line);
      border-radius: 12px; box-shadow: var(--lift);
      /* opacity+visibility rather than display:none so the panel is not yanked
         out of the a11y tree mid-keyboard-traversal */
      opacity: 0; visibility: hidden;
      /* CLOSE GRACE, panel half: this transition governs the CLOSE (the
         destination state's transition applies). The panel stays fully opaque
         and hoverable for 300ms after the pointer leaves the group, then fades
         over 100ms; visibility flips only after the fade (400ms), so the fade
         is actually seen and the panel stays clickable through the grace.
         Re-entering the panel inside the window re-matches .grp:hover and
         cancels the close — this is the fix for "the menu closes too fast when
         I try to click something". The rule below carries its own transition
         so the OPEN stays instant-ish (60ms fade, no delay). */
      transition: opacity 100ms linear 300ms, visibility 0s linear 400ms;
    }
    .grp:hover > .menu, .grp:focus-within > .menu { opacity: 1; visibility: visible; transition: opacity 60ms linear, visibility 0s linear; }
    /* The bridge used to live here, on the panel. That stopped working when the
       panel was re-anchored to the nav ROW rather than to its own trigger: a
       bridge with `left: 0; right: 0` then spans the PANEL's x-range, which for
       a right-anchored panel is nowhere near the label the pointer is leaving.
       The group's own padding band above replaces it and is anchored, by
       construction, under the label. */
    .nav nav .menu a { padding: 8px 11px; }
    .nav nav .menu a .d {
      display: block; color: var(--ink-faint); font-size: var(--t-caption); line-height: var(--t-caption-lh);
      text-transform: none; letter-spacing: 0; font-family: var(--sans);
    }
    .menu .sep { margin: 2px var(--sp-3) var(--sp-2); }
    .menu a + .sep { margin-top: var(--sp-4); }
  }
}

/* ---- status banner ---- */
.prerelease {
  background: var(--bg-raise);
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--mono);
  font-size: var(--t-caption);
  line-height: var(--t-caption-lh);
  color: var(--ink-dim);
  text-align: center;
  padding: 7px var(--sp-4);
}
.prerelease b { color: var(--phos-3); font-weight: 600; }

/* ---- hero ---- */
/* padding-block, not the four-value shorthand: the hero div is `class="wrap hero"`,
   so a shorthand here also sets padding-left/right to 0 and wipes out the 24px
   gutter .wrap just established. That is what pushed every hero on the site flush
   against the screen edge at any width below about 1130px.
   40/32, not 88/56: the old top pad was an 88px empty band between the header
   and the first content on all 33 pages — 13.4% of a 657px viewport carrying
   nothing. The ≤640px override below MUST shrink with it, or a phone gets more
   hero padding than a laptop. */
.hero { padding-block: 40px 32px; }
/* The kicker is the page's name and now lives INSIDE the <h1> as a span, so the
   name is part of the page's own heading. display: block is what keeps it on
   its own line above 30-50px display type and keeps its margin-bottom alive;
   the selector still matches the five badge-carrying sibling divs too. */
.hero .kicker {
  display: block;
  font-family: var(--mono); font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh);
  letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--phos-2); margin-bottom: var(--sp-4);
}
.hero h1 {
  font-size: var(--t-display);
  line-height: var(--t-display-lh); font-weight: 650; letter-spacing: -0.01em;
  max-width: 21ch; color: var(--ink);
  /* Rag control for the h1s that used to carry a hand-placed <br>. Contributes
     zero line-count change; older engines ignore it gracefully. */
  text-wrap: balance;
}
.hero h1 .bar {
  background: linear-gradient(90deg, var(--phos), var(--phos-2));
  color: var(--header-ground); padding: 0 12px; border-radius: 8px; white-space: nowrap;
}
.hero p.lede { margin-top: var(--sp-6); max-width: 62ch; color: var(--ink-dim); font-size: var(--t-title); line-height: var(--t-title-lh); }
/* The bare .cta rule is NOT hero-scoped: the closing CTA block on the leaf
   pages uses the same class outside .hero, and with no rule at all its two
   buttons sat 4.4px apart on a laptop and flush edge-to-edge on a phone. */
.cta { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-6); }
.hero .cta { margin-top: var(--sp-8); }

/* Two-column hero: copy left, a real product panel right. */
.herogrid {
  display: grid; gap: var(--sp-11); align-items: center;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
}
.herogrid h1 { max-width: 17ch; }
.herogrid p.lede { font-size: var(--t-title); }
@media (max-width: 920px) {
  .herogrid { grid-template-columns: minmax(0, 1fr); gap: var(--sp-8); }
  .herogrid h1 { max-width: 21ch; }
}

.btn {
  /* 12px block padding, not 11: at 11 the button computed 46.27px against the
     48dp floor. Now 23px line box + 24px padding + 2px border = 49px. */
  display: inline-block; padding: var(--sp-3) var(--sp-6); border-radius: 6px;
  font-family: var(--mono); font-size: var(--t-mono-body); line-height: var(--t-mono-body-lh);
  letter-spacing: 0.04em;
  border: 1px solid var(--line-control); color: var(--ink);
}
.btn:hover, .btn:focus-visible {
  text-decoration: none; border-color: var(--phos); color: var(--phos-3);
}
.btn.primary {
  background: linear-gradient(135deg, var(--phos), var(--accent-deep-2));
  border-color: transparent; color: var(--on-accent); font-weight: 700;
}
.btn.primary:hover, .btn.primary:focus-visible { filter: brightness(1.12); color: var(--on-accent); }

/* ---- pressed states ------------------------------------------------------
   The file had 16 :hover and 12 :focus-visible rules and not one :active — on
   touch there is no hover to fall back on, so a tap gave no feedback until the
   page changed. A 12% white layer lightens (the M3 direction on a dark
   surface), stays in the file's existing rgba(255,255,255,…) vocabulary, and
   composites --card to #2e323c with no hue shift. filter is untouched by the
   reduced-motion block, so the primary press survives there.
   PLACEMENT IS LOAD-BEARING: these sit after .btn.primary:hover (same
   specificity — later wins while hovering and pressing) and after
   .nav nav .menu a:hover (0,3,2), which outranks .nav nav a:active (0,2,2). */
.btn:active,
.navshell > summary:active,
.faq summary:active,
.nav nav a:active,
.nav nav .menu a:active { background-color: rgba(255, 255, 255, 0.12); }

.btn.primary:active { filter: brightness(0.90); }

/* Inline targets get colour, not a background box — a fill paints ragged
   across the line box. */
.sitemap a:active, .nav nav .grp > .top:active { color: var(--phos-3); }

/* While the 12% wash is held it lightens the ground under two dim roles below
   AA — sheet rows at --ink-dim measured 4.22:1 and the mega-menu descriptions
   at --ink-faint 3.83:1 on the pressed composite (#2e323c) — so the press
   recolours them the way hover already does: rows take --phos-2 (5.77:1
   pressed) and the description steps up to --ink (10.85:1) for the duration
   of the press. On desktop, where :hover co-applies, the values coincide with
   the hover recolour, so nothing double-shifts. */
.navshell > summary:active,
.nav nav a:active { color: var(--phos-2); }
.nav nav .menu a:active .d { color: var(--ink); }

/* ---- sections ---- */
section { padding: 56px 0; border-top: 1px solid var(--line-soft); }
/* Heading ink is --ink for all levels: the three inks that used to live here
   (#eef1f5 / #e6eaf0 / #e8ecf4) differed by at most 8/255 — an unbuildable
   distinction collapsed into the one role token (17.13:1 on --bg).
   The h2 clamp exists because only h1 was fluid: at <=600px h1 pinned to its
   30px floor while h2 stayed 26, collapsing the ratio to 1.15. 22 -> 26px,
   reaching 26 at an 813px viewport. */
h2 {
  font-size: clamp(1.375rem, 3.2vw, var(--t-headline)); line-height: var(--t-headline-lh);
  font-weight: 650; color: var(--ink); margin-bottom: 8px;
  letter-spacing: -0.01em;
}
.h2sub { color: var(--ink-dim); font-size: var(--t-title); line-height: var(--t-title-lh); max-width: 55ch; margin-bottom: var(--sp-8); }
h3 { font-size: var(--t-title); line-height: var(--t-title-lh); color: var(--ink); margin-bottom: 8px; font-weight: 650; }

.grid { display: grid; gap: var(--sp-4); }
/* min() floors the track at the container width, so a 340px card cannot push a
   360px phone into horizontal scroll. */
.grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); }

.card {
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  padding: var(--sp-6); box-shadow: var(--lift);
}
.card .idx {
  font-family: var(--mono); font-size: var(--t-caption); line-height: var(--t-caption-lh); color: var(--phos-2);
  letter-spacing: 0.1em; margin-bottom: var(--sp-2); display: block;
}
.card p { color: var(--ink-dim); font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); }
.card code, td code, li code, p > code {
  font-family: var(--mono); font-size: 0.86em; color: var(--phos-3);
  background: var(--code-ground); border: 1px solid var(--code-edge); border-radius: 3px;
  padding: 1px 6px; white-space: nowrap;
}

/* ---- terminal block ---- */
.term {
  background-color: var(--term-ground); border: 1px solid var(--line); border-radius: 10px;
  font-family: var(--mono); font-size: var(--t-mono-body); line-height: var(--t-mono-body-lh);
  padding: var(--sp-4) 20px; overflow-x: auto; color: var(--ink);
  box-shadow: var(--lift);
  /* Scroll affordance, moved here from .tablewrap (where all 12 wraps measure
     scrollWidth == clientWidth, so it never fired). A transcript at
     white-space: pre genuinely scrolls sideways. The two outer layers are
     pinned to the content (attachment: local) and cover the two inner phosphor
     strips only while that edge is actually reached. (12,14,18) is this
     block's own ground at alpha 0.
     0.18, NOT MORE: transcript text scrolls UNDER the 15px strip whenever that
     edge is not flush, and at 0.38 the composited strip dropped .out
     (--ink-dim) to 3.53:1 and .dim (--ink-faint) to 3.20:1 — below AA on the
     one element that genuinely scrolls. At 0.18 the dimmest role measures
     4.66:1 and every other role clears 5:1; the all-roles ceiling is 0.20. */
  background-image:
    linear-gradient(to right, var(--term-ground), rgba(12, 14, 18, 0)),
    linear-gradient(to left, var(--term-ground), rgba(12, 14, 18, 0)),
    linear-gradient(to right, rgba(255, 90, 43, 0.18), rgba(255, 90, 43, 0)),
    linear-gradient(to left, rgba(255, 90, 43, 0.18), rgba(255, 90, 43, 0));
  background-position: 0 0, 100% 0, 0 0, 100% 0;
  background-size: 46px 100%, 46px 100%, 15px 100%, 15px 100%;
  background-repeat: no-repeat;
  background-attachment: local, local, scroll, scroll;
}
/* Real <pre>, really preserved: white-space: pre, not pre-wrap. pre-wrap meant
   the overflow-x above could never fire, and at 375px it rendered 9 authored
   lines as 24 while overflow-wrap: anywhere split identifiers and digests
   mid-token — on the page whose entire thesis is fidelity. The block scrolls
   sideways instead, which is what the affordance above signals. */
.term pre {
  margin: 0; font: inherit; color: inherit;
  white-space: pre; overflow-wrap: normal;
}
.term .ps1 { color: var(--phos-2); user-select: none; }
.term .out { color: var(--ink-dim); }
.term .good { color: var(--ok); }
.term .dim { color: var(--ink-faint); }
.term .caveat { color: var(--phos-3); }
.termcap {
  font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: var(--sp-2);
}

/* ---- tables ---- */
.tablewrap {
  overflow-x: auto; border: 1px solid var(--line); border-radius: 10px;
  box-shadow: var(--lift);
  /* The scroll-affordance gradients that used to live here moved to .term:
     all 12 wraps measure scrollWidth == clientWidth at every width, so on this
     element the affordance was unreachable code. */
  background-color: var(--card);
}
table { width: 100%; border-collapse: collapse; font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); background: transparent; }
/* The table's own name, styled as the same mono label the wrapper's aria-label
   already speaks. A <caption> is a child of <table>, not of the implicit
   <tbody>, so the ≤700px `tr:first-child { display: none }` cannot hide it. */
caption {
  text-align: left; font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-faint); padding: var(--sp-3) var(--sp-4);
}
th {
  text-align: left; font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh);
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint);
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line);
  background: var(--bg-raise); white-space: nowrap;
}
td { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line-soft); vertical-align: top; color: var(--ink-dim); }
td:first-child { color: var(--ink); white-space: nowrap; }
tr:last-child td { border-bottom: none; }


/* Chip/badge role: mixed-case, off the type scale by design. The explicit
   18px line box holds the chip's height where the old inherited 1.65 ratio
   put it, now that body line-height is absolute. */
.tag {
  display: inline-block; font-family: var(--mono); font-size: 11px; line-height: 18px;
  letter-spacing: 0.05em; border-radius: 3px; padding: 2px 8px;
  border: 1px solid var(--line); color: var(--ink-dim); white-space: nowrap;
}
.tag.decide { border-color: var(--accent-edge); color: var(--phos-2); background: var(--status-decide-ground); }
.tag.detect { border-color: var(--status-detect-edge); color: var(--status-detect); background: var(--status-detect-ground); }
.tag.complement { border-color: var(--status-complement-edge); color: var(--ink-dim); }
.tag.proven { border-color: var(--status-proven-edge); color: var(--ok); background: var(--status-proven-ground); }
.tag.partial { border-color: var(--status-partial-edge); color: var(--status-partial); background: var(--status-partial-ground); }

.legend {
  font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); color: var(--ink-dim);
  margin-top: var(--sp-3); max-width: 57ch;
}

/* ---- honesty note ---- */
.honest {
  border-left: 3px solid var(--phos);
  background: var(--bg-raise);
  padding: var(--sp-4) var(--sp-5); border-radius: 0 10px 10px 0;
  color: var(--ink-dim); font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); margin: var(--sp-6) 0;
}
.honest b { color: var(--ink); }

ul.plain { list-style: none; }
ul.plain li { padding-left: var(--sp-6); position: relative; margin-bottom: var(--sp-3); color: var(--ink-dim); font-size: var(--t-body); }
ul.plain li::before {
  content: "\25AA"; color: var(--phos); position: absolute; left: 0; top: 0;
}

/* ---- small utilities (kept as classes so no page needs a style attribute) ---- */
.mt-12 { margin-top: 12px; }
.mt-14 { margin-top: 14px; }
.mt-16 { margin-top: 16px; }
.mt-18 { margin-top: 18px; }
.note { font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); color: var(--ink-faint); }

/* ---- measure -------------------------------------------------------------
   The primary prose channel was already capped (.lede, .h2sub, .legend,
   .faq p, footer .disclaimer); these are the callouts, list items, captions
   and notes that were not — 23 of 32 pages carried at least one block over
   145 characters a line, the worst a 180-character figcaption. NOT applied to
   the .mt-* utilities: those sit on grids and button rows, not text. */
.note, .honest, ul.plain li, section li,
.fig > figcaption.cap-foot, .card p, p:not([class]) { max-width: var(--measure); }

/* The callout's 20px side padding sits inside its box, so the cap widens by
   the padding to keep the text column itself at the measure. */
.honest { max-width: calc(var(--measure) + 40px); }

/* ---- footer ---- */
footer { border-top: 1px solid var(--line); padding: var(--sp-10) 0 var(--sp-14); margin-top: var(--sp-14); }
footer .cols { display: flex; gap: var(--sp-12); flex-wrap: wrap; }

/* Footer sitemap: the full tree, so every page is reachable without the hover nav.
   130px tracks, not 160: at 160 two columns needed a ≥402px viewport, so every
   mainstream phone got all 31 links in one 1,330px column. At 130 a 360px
   screen fits two columns, halving that height; auto-fit still collapses
   surplus tracks, so wide screens keep their five. */
.sitemap {
  display: grid; gap: var(--sp-6) var(--sp-5);
  grid-template-columns: repeat(auto-fit, minmax(min(130px, 100%), 1fr));
  margin-bottom: var(--sp-8);
}
/* --t-label, not 10.5px: the column heading was 22% smaller than its own
   links, in site-wide footer wayfinding. The label is a <p class="sitemap-label">
   naming its <ul> via aria-labelledby, not an <h2>: the six of them were a
   median 67% of each page's headings while being labels, not sections. */
.sitemap .sitemap-label {
  font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh);
  letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--phos-2); font-weight: 600;
  margin-bottom: var(--sp-3);
}
.sitemap ul { list-style: none; }
/* A column may carry two short groups rather than opening a sixth column that
   would orphan itself onto a row of its own. */
.sitemap ul + .sitemap-label { margin-top: var(--sp-6); }
.sitemap li { margin-bottom: 0; }
.sitemap a { color: var(--ink-dim); font-size: var(--t-body-sm); }
.sitemap a:hover, .sitemap a:focus-visible { color: var(--phos-3); }
footer .disclaimer {
  /* Caption size with the 20px body-sm line box: 12/16 would cram a 72-char
     mono legal paragraph, and 12/20 keeps today's 1.7 rhythm on the grid.
     74ch is CORRECT here — in --mono, ch is exactly one character. */
  font-size: var(--t-caption); color: var(--ink-faint); max-width: 74ch; margin-top: var(--sp-6);
  font-family: var(--mono); line-height: var(--t-body-sm-lh);
}
footer .contact { color: var(--ink-dim); font-size: var(--t-body-sm); }

/* ---- target sizes ----------------------------------------------------------
   WCAG 2.2 SC 2.5.8 puts the floor at 24px for any target that is NOT inline in
   a sentence. MEASURED ON THE LIVE PAGE before these rules: 39 failing targets
   -- 31 sitemap links at 18px, four mega-menu group triggers and "Log in" at
   21px, and three standalone links (the pre-release banner, a section footer,
   the contact address) at 14-19px. Only three links on the whole page qualified
   for the inline-in-prose exemption, and none of these was among them.

   inline-flex rather than padding: vertical padding on an INLINE element grows
   the hit box without growing the line box, so adjacent rows begin overlapping
   each other's targets -- which SC 2.5.8 forbids in the same breath. Giving the
   link a box of its own keeps the rows apart.

   THE MEGA MENU SURVIVES THIS, and that was the thing to check rather than
   assume. Its hover bridge depends on `.grp` filling the 37px nav row; raising
   the trigger from 21px to 24px leaves both `.grp` and the row at 37px, which
   was measured on the live page before the rule was written here. */
footer .contact a,
.prerelease a,
.foot a {
  display: inline-flex; align-items: center; min-height: 24px;
}

/* The sitemap links go further than the 24px floor: each is a block row of its
   own, 13.5 x 1.65 = 22.27px of text + 22px of padding = 44.27px, with the old
   7px li margins folded into the padding so rows abut without overlapping.
   display: block rather than inline-flex because a row, not a tight box, is
   what a column of navigation links is. */
.sitemap a { display: block; padding: 11px 0; min-height: 24px; }

/* Where the primary pointer is a finger, Material 3's floor is 48px rather than
   24: "consider making touch targets at least 48 x 48dp", which it puts at
   about 9mm of physical screen. Reconciled by CAPABILITY rather than by
   viewport, so a narrow window with a mouse keeps the dense metrics. */
@media (pointer: coarse) {
  .sitemap a, footer .contact a { min-height: 48px; }
}

@media (max-width: 1000px) {
  /* The header stays sticky here now. It used to go static below 760px because
     a wrapped six-link nav inside a sticky bar ate a third of a phone screen;
     the burger reduces that row to a logo and a 38px control, and a nav you
     have to scroll back up to reach is worse than one that follows you. */
  .nav { gap: 6px 16px; }
  .nav .logo .lockup { height: 22px; }
}

@media (max-width: 700px) {
  /* An unbreakable command name is wider than a 320px phone's content box, so
     below this width code spans wrap instead of pushing the page sideways. */
  .card code, td code, li code, p > code {
    white-space: normal; overflow-wrap: anywhere;
  }

  /* Below this width the three-column tables stack into definition blocks.
     Sideways scrolling technically worked, but it hid the "truthful sentence"
     column on 100% of phone widths while leaving rows hundreds of pixels tall
     with nothing in them -- so the column that is the point of the page was the
     one nobody read. Stacked, the first cell is the term and the rest is its
     definition, which is what these tables already were. */
  .tablewrap {
    overflow: visible; border: 0; border-radius: 0;
    background: none; box-shadow: none;
  }
  table, tbody, tr, td { display: block; width: 100%; }
  /* A table-caption box inside a display:block table shrink-wraps to
     min-content — one word per line. Blockify it with the rest. */
  caption { display: block; width: 100%; padding: 0 0 var(--sp-2); }
  tr:first-child { display: none; }
  tr {
    background: var(--card); border: 1px solid var(--line); border-radius: 10px;
    box-shadow: var(--lift); padding: var(--sp-4); margin-bottom: var(--sp-3);
  }
  tr:last-child { margin-bottom: 0; }
  td, tr:last-child td {
    border: 0; padding: 0 0 var(--sp-3); white-space: normal;
  }
  td:last-child { padding-bottom: 0; }
  td:first-child {
    color: var(--ink); font-size: var(--t-body); font-weight: 650; white-space: normal;
  }
  /* The hidden first row above holds every <th> on the site, and display: none
     drops it from the a11y tree regardless of any role on it. Each cell's
     data-label restores its column's name — generated content is included in
     accessible-name computation, so this serves both readers. */
  td::before {
    content: attr(data-label);
    display: block;
    font-family: var(--mono);
    font-size: var(--t-label);
    line-height: var(--t-label-lh);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-bottom: var(--sp-1);
  }
}

@media (max-width: 640px) {
  .hero { padding-block: 32px 24px; }
  section { padding: 44px 0; }
}

/* ════════════════════════════════════════════════════════════════════════════
   SHOWPIECE LAYER
   Everything below is decoration. Nothing here is the sole carrier of any
   meaning, every animation is disabled under prefers-reduced-motion at the
   foot of this file, and no rule fetches anything: every visual is a gradient,
   a border, a shadow, or an inline <svg> in the markup.
   ════════════════════════════════════════════════════════════════════════════ */

/* ---- the chamfer: the console frame's asymmetric corner cut ---- */
.chamfer {
  clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
}

/* ---- the wall motif: a strip of redaction bricks ----
   Gapped 2px, wash-weight phosphor, every third brick heavier and every fourth
   fainter. On the landing page the bricks also breathe, staggered, so the mark's
   own motif is the thing that moves. */
.wall { display: flex; gap: 2px; height: 18px; align-items: flex-end; }
.wall span {
  flex: 1; height: 100%; border-radius: 1px;
  transform-origin: bottom;
  background: linear-gradient(180deg, var(--phos), rgba(196, 59, 20, 0.9));
  opacity: 0.28;
  animation: brick 3.6s ease-in-out infinite;
}
.wall span:nth-child(3n) { opacity: 0.5; }
.wall span:nth-child(4n) { opacity: 0.16; }
.wall span:nth-child(1)  { animation-delay: 0s; }
.wall span:nth-child(2)  { animation-delay: 0.11s; }
.wall span:nth-child(3)  { animation-delay: 0.22s; }
.wall span:nth-child(4)  { animation-delay: 0.33s; }
.wall span:nth-child(5)  { animation-delay: 0.44s; }
.wall span:nth-child(6)  { animation-delay: 0.55s; }
.wall span:nth-child(7)  { animation-delay: 0.66s; }
.wall span:nth-child(8)  { animation-delay: 0.77s; }
.wall span:nth-child(9)  { animation-delay: 0.88s; }
.wall span:nth-child(10) { animation-delay: 0.99s; }
.wall span:nth-child(11) { animation-delay: 1.1s; }
.wall span:nth-child(12) { animation-delay: 1.21s; }
.wall.tall { height: 34px; }
@keyframes brick {
  0%, 100% { opacity: 0.16; transform: scaleY(0.82); }
  50%      { opacity: 0.62; transform: scaleY(1); }
}

/* ---- gradient-clipped wordmark type ---- */
.brandtext {
  color: var(--phos-2);
  background: linear-gradient(160deg, var(--phos-3), var(--phos));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  text-shadow: 0 0 18px rgba(255, 90, 43, 0.28);
}

/* ---- the hero ---- */
.heroframe {
  position: relative; overflow: hidden;
  background: linear-gradient(180deg, var(--card), var(--bg-raise));
  border: 1px solid var(--line);
  box-shadow: var(--glow);
}
/* A beam that sweeps the panel once every few seconds, the way a scanner does. */
.heroframe::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 42%, rgba(255, 145, 82, 0.10) 50%, transparent 58%);
  transform: translateX(-120%);
  animation: sweep 7s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes sweep { 0%, 55% { transform: translateX(-120%); } 100% { transform: translateX(120%); } }

/* The headline's highlighted phrase: a live phosphor bar rather than a flat fill. */
.hero h1 .bar {
  background: linear-gradient(100deg, var(--accent-deep), var(--phos), var(--phos-2), var(--phos));
  background-size: 280% 100%;
  color: var(--header-ground); padding: 0 12px; border-radius: 8px; white-space: nowrap;
  box-shadow: 0 0 34px -6px rgba(255, 90, 43, 0.55);
  animation: barflow 9s ease-in-out infinite;
}
@keyframes barflow { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }

/* Primary CTA: a slow phosphor breath, so the one action on the page is alive. */
.btn.primary { box-shadow: 0 0 0 0 rgba(255, 90, 43, 0.55); animation: breathe 4.2s ease-in-out infinite; }
@keyframes breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 90, 43, 0.42); }
  50%      { box-shadow: 0 0 30px -4px rgba(255, 90, 43, 0.62); }
}

/* ---- capability icons ---- */
.featicon { width: 22px; height: 22px; display: block; margin-bottom: 12px; color: var(--phos-2); }
.featicon path, .featicon rect, .featicon circle, .featicon line, .featicon polyline {
  stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.featicon .solid { fill: currentColor; stroke: none; }

/* Cards lift toward the reader on hover, and light their top edge. */
.card { position: relative; transition: transform 180ms ease, border-color 180ms ease; }
.card.lift:hover, .card.lift:focus-within { transform: translateY(-3px); border-color: var(--accent-edge); }

/* ---- scroll-driven reveal: DELIBERATELY NOT IMPLEMENTED ----
   A `animation-timeline: view()` reveal was tried here and removed on
   2026-08-24. With `animation-fill-mode: both`, any element whose timeline
   resolves as before-range renders at the from-state — and that from-state was
   `opacity: 0`. Three whole sections of the landing page were invisible.
   A marketing page must never make its content conditional on a timeline
   resolving the way you expected, so the class is retained as an inert hook and
   nothing animates the page's own copy in or out. */
.reveal { /* intentionally empty */ }

/* ---- the decision card: what a reviewer sees, in plain language ---- */
.evcard {
  background: var(--card); border: 1px solid var(--line);
  box-shadow: var(--glow); overflow: hidden;
}
.evcard .head {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4); background: var(--bg-raise);
  border-bottom: 1px solid var(--line-soft);
}
.evcard .head .what { color: var(--ink); font-weight: 650; font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); }
.evcard .head .verdict {
  margin-left: auto; font-family: var(--mono); font-size: 11px; line-height: 18px; letter-spacing: 0.08em;
  color: var(--status-alert); background: rgb(var(--alert) / 0.14);
  border: 1px solid rgb(var(--alert) / 0.4); border-radius: 3px; padding: 3px var(--sp-2);
}
.evcard .body { padding: var(--sp-4) var(--sp-4) 6px; }
.evcard .summary { color: var(--ink); font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); margin-bottom: var(--sp-3); }
.evcard dl { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-2) var(--sp-4); margin-bottom: var(--sp-3); }
.evcard dt {
  font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-faint); white-space: nowrap;
}
.evcard dd { font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); color: var(--ink-dim); text-align: right; }
.evcard dd b { color: var(--ink); font-weight: 600; }
.evcard .foot {
  border-top: 1px solid var(--line-soft); padding: var(--sp-3) var(--sp-4);
  font-size: var(--t-caption); line-height: var(--t-caption-lh); color: var(--ink-faint);
}

/* ---- scenario lines: situation, then what happened ---- */
.scen { list-style: none; display: grid; gap: var(--sp-3); }
.scen li {
  background: var(--bg-raise); border: 1px solid var(--line-container);
  border-left: 2px solid var(--phos);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--sp-3) var(--sp-4); color: var(--ink-dim); font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh);
}
.scen li b { color: var(--ink); font-weight: 600; }
.scen li .then { color: var(--phos-3); }

/* ---- FAQ: native <details>, no script needed ---- */
.faq { display: grid; gap: var(--sp-3); }
.faq details {
  background: var(--card); border: 1px solid var(--line-soft); border-radius: var(--radius);
  padding: 0 var(--sp-4);
}
.faq summary {
  cursor: pointer; padding: 14px 0; color: var(--ink); font-weight: 600; font-size: var(--t-body);
}
.faq summary::marker { color: var(--phos); }
.faq summary:hover { color: var(--phos-2); }
/* The question is a real <h3> inside the <summary> so it joins the outline.
   color: inherit is mandatory: the font shorthand does not carry colour, so
   the h3 element rule would otherwise pin the text and kill the summary's
   hover recolour. display: inline keeps the phosphor ::marker in place. */
.faq summary h3 { display: inline; font: inherit; color: inherit; margin: 0; }
.faq details[open] summary { border-bottom: 1px solid var(--line-quiet); }
.faq p { color: var(--ink-dim); font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); padding: var(--sp-3) 0 var(--sp-4); max-width: 57ch; }

/* ---- trust bullets under the closing CTA ---- */
.trust { list-style: none; display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-6); margin-top: var(--sp-6); }
.trust li { color: var(--ink-dim); font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); padding-left: var(--sp-4); position: relative; }
.trust li::before {
  content: ""; position: absolute; left: 0; top: 8px;
  width: 8px; height: 3px; border-radius: 1px; background: var(--phos);
}

/* ════════════════════════════════════════════════════════════════════════════
   FIGURES — the landing page's diagrams
   Every figure here states STRUCTURE: who acted, which path it took, what stood
   on that path, and what was written down. None of them plots a quantity. There
   is no production telemetry behind this site, so a chart with a number on it
   would be a fabricated measurement, and none is drawn.

   Why these are laid out as elements rather than as one <svg> carrying its own
   <text>: text inside an SVG scales with the viewBox, so a diagram sized for a
   540px hero column renders its labels at roughly 7px on a 320px phone. Laid
   out as elements, every label keeps its real font size at every width and the
   figure reflows instead of shrinking. The glyphs — which carry no text — are
   inline SVG.
   ════════════════════════════════════════════════════════════════════════════ */

.fig { margin: 0; }
.fig > figcaption {
  font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: var(--sp-3);
}
.fig > .note { margin-top: 12px; }
/* A caption that sits under its figure reads as prose, not as a label, so it
   drops the mono-uppercase treatment the leading captions carry. */
.fig > figcaption.cap-foot {
  font-family: var(--sans); font-size: var(--t-body-sm); letter-spacing: 0;
  text-transform: none; color: var(--ink-faint);
  margin: var(--sp-3) 0 0; line-height: var(--t-body-sm-lh);
}

/* Line-art glyphs, inheriting colour so one rule paints them all. */
.glyph { width: 22px; height: 22px; flex: none; color: var(--phos-2); }
.mini { width: 14px; height: 14px; flex: none; }
.glyph path, .glyph rect, .glyph circle, .glyph line, .glyph polyline, .glyph polygon,
.mini path, .mini rect, .mini circle, .mini line, .mini polyline, .mini polygon {
  stroke: currentColor; fill: none;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}

/* ---- Figure A: one incident, top to bottom ----
   Actor, then the thing they moved, then what stood in the path, then where it
   was going and what happened. Vertical at every width, so the phone layout is
   the desktop layout and nothing has to be re-taught. */
.flow { display: block; }
.flow .fnode {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3);
  background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; padding: var(--sp-3) var(--sp-4); box-shadow: var(--lift);
}
.flow .fnode .ft { flex: 1 1 auto; min-width: 0; }
.flow .fnode .ft b { display: block; color: var(--ink); font-size: var(--t-body-sm); font-weight: 650; line-height: var(--t-body-sm-lh); }
.flow .fnode .ft span { display: block; color: var(--ink-faint); font-size: var(--t-caption); line-height: var(--t-caption-lh); }
.flow .fnode.out { border-color: var(--accent-edge); }
.flow .stamp {
  margin-left: auto; font-family: var(--mono); font-size: 11px; line-height: 18px; letter-spacing: 0.08em;
  color: var(--status-alert); background: rgb(var(--alert) / 0.14);
  border: 1px solid rgb(var(--alert) / 0.4); border-radius: 3px; padding: 3px var(--sp-2);
}

/* The connector: a rule with an arrowhead, and optionally the asset riding on it. */
.flow .fconn {
  position: relative; height: 42px;
  display: flex; align-items: center; justify-content: center;
}
.flow .fconn::before {
  content: ""; position: absolute; top: 0; bottom: 8px; left: 50%;
  width: 2px; transform: translateX(-50%);
  background: var(--phos); opacity: 0.55;
}
.flow .fconn::after {
  content: ""; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 8px solid var(--phos); opacity: 0.7;
}
/* After the wall the path is drawn, but broken: the arrow is still pointed at
   the destination and nothing travelled it. */
.flow .fconn.stop::before {
  background: repeating-linear-gradient(to bottom, var(--ink-faint) 0 3px, transparent 3px 8px);
  opacity: 0.5;
}
.flow .fconn.stop::after { border-top-color: var(--ink-faint); opacity: 0.4; }
.flow .fasset {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: var(--sp-2);
  background: var(--bg-raise); border: 1px solid var(--line); border-radius: 6px;
  padding: 5px var(--sp-3); font-family: var(--mono); font-size: var(--t-caption); line-height: var(--t-caption-lh); color: var(--phos-3);
}
.flow .fasset .mini { color: var(--ink-faint); }

/* The checkpoint is the mark's own motif, doing the job the mark is named for. */
.flow .fgate {
  background: var(--bg-raise); border: 1px solid var(--line-container);
  border-radius: 10px; padding: var(--sp-3) var(--sp-4) var(--sp-4);
}
.flow .fgatelab {
  display: block; font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--phos-2); margin-bottom: var(--sp-2);
}
.flow .fgate .wall { height: 20px; }

/* ---- Figure B: the paths out, and the coverage standing on each ----
   Derived row-for-row from /coverage.html. A solid marker sits ON the path
   (decided before anything moves); an outline marker sits BESIDE it (found
   after the fact, or a device-side complement). The chips are the same
   components the coverage table uses, so the two pages cannot drift apart in
   appearance while claiming the same thing. */
.lanes {
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  padding: var(--sp-1) var(--sp-5) var(--sp-3); box-shadow: var(--lift);
}
.lanehead, .lane {
  display: grid; gap: 0 var(--sp-4); align-items: center;
  grid-template-columns: minmax(0, 0.72fr) 150px minmax(0, 1.12fr);
}
.lanehead {
  padding: var(--sp-3) 0 var(--sp-2); border-bottom: 1px solid var(--line-quiet);
  font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-faint);
}
.lane { padding: var(--sp-4) 0; }
.lane + .lane { border-top: 1px solid var(--line-quiet); }
.lane .src { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.lane .src b { color: var(--ink); font-size: var(--t-body-sm); font-weight: 600; line-height: var(--t-body-sm-lh); }
.lane .res { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }
.lane .say { flex: 1 1 100%; color: var(--ink-dim); font-size: var(--t-body-sm); line-height: var(--t-body-sm-lh); }

.lane .track { position: relative; display: block; height: 30px; }
.lane .track::before {
  content: ""; position: absolute; left: 0; right: 9px; top: 50%; height: 2px;
  transform: translateY(-50%); background: var(--phos); opacity: 0.5;
}
.lane .track::after {
  content: ""; position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  width: 0; height: 0;
  border-top: 5px solid transparent; border-bottom: 5px solid transparent;
  border-left: 9px solid var(--phos); opacity: 0.6;
}
.lane .mark {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 11px; height: 26px; border-radius: 2px;
  background: linear-gradient(180deg, var(--phos), var(--accent-deep));
  box-shadow: 0 0 16px -3px rgba(255, 90, 43, 0.8);
}
/* Nothing stands in these two paths, so the path is drawn quiet and the marker
   steps off it. Cyan is the measurement colour; grey is the complement. */
.lv-post .track::before, .lv-end .track::before { background: var(--ink-faint); opacity: 0.34; }
.lv-post .track::after, .lv-end .track::after { border-left-color: var(--ink-faint); opacity: 0.45; }
.lv-post .mark, .lv-end .mark {
  top: auto; bottom: 0; transform: translateX(-50%);
  width: 14px; height: 16px; border-radius: 2px;
  background: none; box-shadow: none;
}
/* A short tick up to the path, so an off-path marker reads as watching that
   path rather than as an ornament floating near it. */
.lv-post .mark::before, .lv-end .mark::before {
  content: ""; position: absolute; left: 50%; top: -7px;
  width: 1px; height: 7px; transform: translateX(-50%);
  background: currentColor; opacity: 0.55;
}
.lv-post .mark { border: 1.5px dashed var(--signal); color: var(--signal); }
.lv-end .mark { border: 1.5px dashed var(--ink-dim); color: var(--ink-dim); }

@media (max-width: 760px) {
  /* Three columns will not fit a phone. The lane becomes a stack — source, the
     track, then the coverage — which is the same left-to-right reading turned
     downward, and the header row that labelled the columns goes with it. */
  .lanes { padding: 6px var(--sp-4) var(--sp-3); }
  .lanehead { display: none; }
  .lane { grid-template-columns: minmax(0, 1fr); gap: var(--sp-3) 0; padding: var(--sp-4) 0; }
  .lane:first-child { padding-top: var(--sp-3); }
  .lane .track { height: 22px; }
}

/* ---- Figure C: what is found, and what is kept ----
   The abstract lines are not a rendering of anything: they stand for a message
   whose content the site never shows. The two phosphor bars are where sensitive
   values were found, and the card below holds their labels — never them. */
.keep { display: block; }
.keep .ksidecap {
  display: block; font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: var(--sp-2);
}
.keep .kdoc {
  background: var(--bg-raise); border: 1px solid var(--line-container);
  border-radius: var(--radius); padding: var(--sp-4); display: grid; gap: var(--sp-2);
}
.keep .kline { display: block; height: 7px; border-radius: 2px; background: var(--line); }
.keep .kline.redact {
  background: linear-gradient(90deg, var(--phos), var(--accent-deep));
  box-shadow: 0 0 15px -3px rgba(255, 90, 43, 0.75);
}
.keep .w80 { width: 80%; }
.keep .w70 { width: 70%; }
.keep .w60 { width: 60%; }
.keep .w40 { width: 40%; }
.keep .karrow {
  display: block; width: 0; height: 0; margin: var(--sp-4) auto var(--sp-4);
  border-left: 6px solid transparent; border-right: 6px solid transparent;
  border-top: 9px solid var(--phos); opacity: 0.6;
}
.keep .kcard {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-4); box-shadow: var(--lift);
  display: flex; flex-wrap: wrap; gap: var(--sp-2);
}
.keep .kchip {
  font-family: var(--mono); font-size: 11.5px; line-height: 19px; letter-spacing: 0.02em;
  color: var(--phos-3); background: var(--code-ground); border: 1px solid var(--code-edge);
  border-radius: 3px; padding: var(--sp-1) var(--sp-2);
}
.keep .kchip.out {
  color: var(--ink-faint); background: transparent;
  border: 1px dashed var(--line);
}
/* Side by side once there is room for it: sent on the left, kept on the right,
   the arrow between them. Each side is its own two-row grid so the two labels
   land on one line and the two panels share a baseline. Below 761px the whole
   thing falls back to the block stack above, arrow pointing down. */
@media (min-width: 761px) {
  .keep {
    display: grid; gap: 0 var(--sp-6); align-items: stretch;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  }
  .keep .kside { display: grid; grid-template-rows: auto 1fr; min-width: 0; }
  /* The two panels share a height, so the shorter one centres its contents
     rather than stranding its whole lower half empty. */
  .keep .kdoc { align-content: center; }
  .keep .kcard { align-content: flex-start; }
  .keep .karrow {
    align-self: center; margin: 0;
    border-top: 6px solid transparent; border-bottom: 6px solid transparent;
    border-right: 0; border-left: 9px solid var(--phos);
  }
}

/* ---- Figure D: where it sits ----
   Three nodes and two connectors: the applications you already run, the wall,
   and the providers on the other side of it. It states position, not quantity —
   the only claim in the picture is that everything crosses the middle node. */
.fit { display: grid; grid-template-columns: minmax(0, 1fr); }
.fitnode {
  background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; padding: var(--sp-4); box-shadow: var(--lift);
}
.fitnode .glyph, .fitnode .wall { margin-bottom: var(--sp-3); }
.fitnode b { display: block; color: var(--ink); font-size: var(--t-body-sm); font-weight: 650; line-height: var(--t-body-sm-lh); }
.fitnode .fd { display: block; color: var(--ink-faint); font-size: var(--t-caption); line-height: var(--t-caption-lh); margin-top: 5px; }
.fitnode.wallnode { border-color: var(--accent-edge); }

/* The connector. Vertical while the figure is a single column. */
.fitlink {
  position: relative; height: 46px;
  display: flex; align-items: center; justify-content: center;
}
.fitlink::before {
  content: ""; position: absolute; top: 0; bottom: 8px; left: 50%;
  width: 2px; transform: translateX(-50%); background: var(--phos); opacity: 0.5;
}
.fitlink::after {
  content: ""; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 8px solid var(--phos); opacity: 0.65;
}
.fitlab {
  position: relative; z-index: 1;
  font-family: var(--mono); font-size: var(--t-label); line-height: var(--t-label-lh); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--phos-3);
  background: var(--bg); border: 1px solid var(--line);
  border-radius: 3px; padding: 3px var(--sp-2); white-space: nowrap;
}
@media (min-width: 861px) {
  .fit { grid-template-columns: minmax(0, 1fr) 120px minmax(0, 1fr) 120px minmax(0, 1fr); }
  .fitlink { height: auto; }
  .fitlink::before {
    top: 50%; bottom: auto; left: 0; right: 9px; width: auto; height: 2px;
    transform: translateY(-50%);
  }
  .fitlink::after {
    top: 50%; bottom: auto; left: auto; right: 0; transform: translateY(-50%);
    border-top: 5px solid transparent; border-bottom: 5px solid transparent;
    border-right: 0; border-left: 9px solid var(--phos);
  }
}

/* A figure that would look stretched at the full content width holds its own
   column and centres in the section instead. */
.fig.mid { max-width: 640px; margin-inline: auto; }

/* ---- narrow-screen overrides ----
   These MUST live after the showpiece layer. `.hero h1 .bar` is redefined above
   with `white-space: nowrap`, and an identical-specificity rule placed earlier
   in the file loses to it — which is exactly how a long highlighted phrase kept
   pushing three pages sideways on a 320px screen after the "fix" was written. */
@media (max-width: 700px) {
  /* nowrap keeps the phosphor bar from breaking mid-phrase on a wide screen. On
     a narrow one the phrase is wider than the viewport, so it wraps instead, and
     box-decoration-break draws the bar around each line rather than as one torn
     box. */
  .hero h1 .bar {
    white-space: normal;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
  }
}

/* ---- the one rule that outranks all of the above ---- */
@media (prefers-reduced-motion: reduce) {
  .wall span, .heroframe::after, .hero h1 .bar, .btn.primary {
    animation: none !important;
    transform: none !important;
  }
  .card.lift:hover { transform: none; }
}
