/* ============================================================================
   WIZARD CYBER — CYBERSHIELD AI
   Design system: LIGHT-dominant (~65%), futuristic, brand red #c30017.
   Dark is used as an accent for: agent showcase, live SOC feed, footer, CTAs.
   Squad colours: Blue #2f6bff (Defense) · Red #c30017 (Offensive) · Green #12a150 (Engineering)
   Sections: 1 Tokens · 2 Base · 3 Layout · 4 Type · 5 Buttons · 6 Header
             7 Footer · 8 Hero · 9 Components · 10 Agents/figure · 11 Article
             12 Utilities · 13 Motion · 14 Responsive
   ============================================================================ */

/* ---------- 1. TOKENS ---------- */
:root{
  --red:#c30017; --red-2:#e21029; --red-3:#ff3b52; --red-dk:#98000f;
  --red-glow:rgba(195,0,23,.35); --red-wash:#fdeff1;

  --blue:#2f6bff; --green:#12a150;
  /* The same two, lifted for dark surfaces. --blue and --green are tuned to
     carry on paper and go muddy on --void and --dk-panel, so the dark panels
     were reaching for raw hex instead. Named here so they stop being
     reinvented per component. */
  --blue-dk:#7fb0ff; --green-dk:#3ddc8a;
  /* --amber was used by .console .row .st.hold and .matrix .add but only ever
     DEFINED in admin.css, which the public site does not load - so both have
     been rendering with no colour at all (the home page's "held" status pill
     included). Two values because the two uses sit on opposite surfaces:
     --amber reads on the dark consoles, --amber-ink is dark enough to be
     legible on paper. */
  --amber:#e5a13a; --amber-ink:#8a5b05;
  /* --blue-ink AND --green-ink, added 22 Sep 2026, completing a pattern that
     had existed for one colour. The note above --amber-ink states the whole
     problem - a brand colour tuned to read on a dark console is not dark
     enough to read as small text on paper - and blue and green had it too:
     measured on --mist, --green is 3.19 and --blue 4.27 against the 4.5 a
     label under 18px needs.

     These are the same hues scaled toward black until they clear it: green
     4.53 on --mist and 4.77 on paper, blue 4.58 and 4.82. Close enough to the
     originals that a reader sees the same colour, far enough to be readable.

     USE THEM FOR SMALL TEXT ON A LIGHT GROUND, and nothing else. Fills,
     borders, rails, icons and anything on a dark surface keep the originals -
     --blue-dk and --green-dk already exist for the dark side. */
  --blue-ink:#2d67f5; --green-ink:#0f8442;

  /* light surfaces */
  --paper:#ffffff;
  --bg:#f4f6f9;            /* page base */
  --bg-2:#eef1f6;          /* alt band */
  --mist:#f8f9fb;
  /* the 64px lattice, shared by body::after and every light band */
  --lattice-ink:rgba(15,20,35,.038);
  --lattice-img:linear-gradient(var(--lattice-ink) 1px,transparent 1px),
                linear-gradient(90deg,var(--lattice-ink) 1px,transparent 1px);
  --lattice-size:64px 64px;
  /* the ambient wash - a warm blob top right, a cool one top left. Defined here
     so body::before and every light band draw the identical thing. */
  --ambient-img:radial-gradient(50vw 50vw at 82% -6%,rgba(195,0,23,.08),transparent 60%),
                radial-gradient(46vw 46vw at 6% 8%,rgba(47,107,255,.07),transparent 55%);
  /* What a light band paints over its own background: the lattice, then the
     ambient wash. Four layers, so the size and repeat lists have four entries.
     Keep them in step if either token above gains or loses a gradient. */
  --band-bg-image:var(--lattice-img),var(--ambient-img);
  --band-bg-size:64px 64px,64px 64px,100% 100%,100% 100%;
  --band-bg-repeat:repeat,repeat,no-repeat,no-repeat;
  --line:rgba(15,20,35,.10);
  --line-2:rgba(15,20,35,.16);

  /* ink / text on light */
  --ink:#12151d;           /* headings */
  --fg:#2c313d;            /* body */
  --fg-soft:#565d6e;
  /* #727a8c until 22 Sep 2026, which measured 4.31 on paper and 3.98 on --bg
     against a 4.5 requirement - so every use of it for body-size text was
     under AA, on both grounds, everywhere on the site. Package descriptions,
     card paragraphs, the "Package 01" labels, form hints, the agent roles.
     #697183 is the first value along the same hue that clears it on the
     HARDER ground: 4.52 on --bg, 4.89 on paper. Nine steps darker, which is
     not a visible change to the design and is the difference between passing
     and failing. */
  --fg-mut:#697183;
  /* DECORATION ONLY - NOT FOR TEXT, from 22 Sep 2026.
     #9aa1b1 measures 2.46 on --mist and 2.59 on paper against the 4.5 any
     text under 18px needs, so every one of its seven text uses failed:
     the ordinal in the corner of a baseline card, the "Worth knowing"
     label, the WIP chip, the Purdue caption, the unowned SIEM row, the
     partner logo slot and the post meta line. All seven now take --fg-mut.

     IT WAS NOT DARKENED, and that is the decision worth recording. The
     value that clears 4.5 on --mist is #6c7383; --fg-mut is #697183. They
     would be the same colour, which does not fix the tier so much as
     delete it and leave two names for one grey. Better to keep the tier
     honest about what it is: a wash for icons and rules, and never a
     colour you can set type in. Its one remaining use is an SVG. */
  --fg-faint:#9aa1b1;

  /* dark surfaces (for .sec-dark blocks) */
  --void:#080a10;
  --dk:#0d1017;
  --dk-panel:#151925;
  --dk-line:rgba(255,255,255,.10);
  --dk-line-2:rgba(255,255,255,.18);
  --dk-fg:#eef0f6;
  --dk-soft:#b9bfce;
  --dk-mut:#828a9d;

  --display:'Poppins',system-ui,-apple-system,'Segoe UI',sans-serif;
  --body:'Barlow',system-ui,-apple-system,'Segoe UI',sans-serif;
  --mono:'JetBrains Mono',ui-monospace,'SF Mono',Menlo,monospace;

  --wrap:1240px;
  --radius:14px; --radius-sm:10px; --radius-lg:22px;
  --ease:cubic-bezier(.22,.61,.36,1);
  --shadow:0 22px 50px -24px rgba(20,30,60,.28);
  --shadow-sm:0 6px 18px -8px rgba(20,30,60,.20);
}

/* ---------- 1b. BACKDROP ----------
   SINGLE SOURCE OF TRUTH for the background of every dark surface on the site:
   hero backgrounds, mid-page dark bands, and the footer. Edit a token here and
   every page changes with it. Nothing below this block hard-codes a backdrop
   colour, and no page template carries a background of its own.

   The FOOTER is a separate element: it re-declares these same tokens on .foot
   (section 7) and so is tuned on its own, without duplicating any machinery.

   Layers, back to front:
     element background   --bd-1 / --bd-2   base wash
     ::before             --bd-mesh-*       square grid, fading out downward
     ::after              lights + the pulse travelling along the bottom edge
     .aura (when present) animated stand-in for the lights

   Everything here is a DIAL — a colour, a size, a speed. The geometry that
   uses them lives with the layer rules in section 3. That split matters: a
   custom property that itself contains var() is resolved where it is DECLARED,
   so composing the gradients up here would bake in the :root colours and
   silently ignore whatever .foot overrides for itself.

   Squad colours are the same three the agents use: red, blue, green. */
:root{
  /* base wash — near-black, carrying just enough red to not read as grey */
  --bd-1:#180c0f;
  --bd-2:#0b0507;

  /* square grid. The tile is 1:1, so --bd-mesh-size alone sets the cell.
     Swapping --bd-mesh-tile for a tile of another shape is all it takes to
     change the lattice everywhere; keep --bd-mesh-h in step with its ratio. */
  --bd-mesh-ink:rgba(255,255,255,.085);
  --bd-mesh-size:52px;
  --bd-mesh-h:var(--bd-mesh-size);   /* tile ratio 1:1 */
  --bd-mesh-opacity:.55;
  --bd-mesh-opacity-hero:.62;   /* heroes carry the lattice a touch heavier */
  --bd-mesh-tile:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='10'%20height='10'%20viewBox='0%200%2010%2010'%3E%3Cpath%20d='M5%200V10M0%205H10'%20fill='none'%20stroke='%23000'%20stroke-width='.2'/%3E%3C/svg%3E");

  /* lights. Green sits far left, behind the headline column. */
  --bd-red:rgba(195,0,23,.24);
  --bd-red-2:rgba(195,0,23,.11);
  --bd-blue:rgba(47,107,255,.15);
  --bd-green:rgba(18,161,80,.20);
  /* the animated .aura carries the same three colours a little hotter, since
     it is blurred much harder than the static lights */
  --bd-blue-aura:rgba(47,107,255,.22);
  --bd-green-aura:rgba(18,161,80,.30);

  /* pulse travelling along the bottom edge, plus the hairline it rides on */
  --bd-pulse-img:radial-gradient(closest-side at 50% 100%,
    rgba(255,59,82,.55),rgba(255,59,82,.14) 46%,transparent 100%);
  --bd-pulse-w:46%;
  --bd-pulse-h:160px;
  --bd-pulse-speed:9s;
  --bd-edge-img:linear-gradient(90deg,transparent,rgba(255,255,255,.13),transparent);
}

/* ---------- 2. BASE ---------- */
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--fg);
  font-family:var(--body);font-size:17px;line-height:1.65;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;overflow-x:hidden;
}
img,svg,video{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;cursor:pointer}
ul{margin:0;padding:0;list-style:none}
:focus-visible{outline:2px solid var(--red);outline-offset:3px;border-radius:3px}
::selection{background:var(--red);color:#fff}

/* soft ambient tints + faint grid behind the light page */
body::before{
  content:"";position:fixed;inset:0;z-index:-2;pointer-events:none;
  background-image:var(--ambient-img);
}
/* The lattice behind the light page.
   NO MASK. There was a radial one that faded the grid out towards the bottom of
   the screen, and it was pushed back once to try to save it. That was treating
   the symptom. This layer is position:FIXED, so a mask anchored to the viewport
   fades by SCREEN POSITION rather than by page position: whatever section
   happened to be near the top of the window had a visible grid and whatever sat
   lower had none, and the same section changed as you scrolled past it. Two
   different whites in one screenshot, which is exactly what it looked like.
   An even grid everywhere is the only version that reads as deliberate. */
body::after{
  content:"";position:fixed;inset:0;z-index:-2;pointer-events:none;opacity:.75;
  background-image:
    linear-gradient(rgba(15,20,35,.050) 1px,transparent 1px),
    linear-gradient(90deg,rgba(15,20,35,.050) 1px,transparent 1px);
  background-size:64px 64px;
}

/* Pages that open on a dark hero pull the hero UP behind the header, rather
   than painting the body dark. Painting the body was wrong: sections with no
   background of their own (plain .sec) then rendered dark while keeping
   light-page text colours, which put black text on a black background.
   The hero simply starts taller and the header floats on top of it. */
/* Height of the utility row + floating pill. Overshooting is safe: the hero's
   padding uses the same variable, so content always lands the same distance
   below the real header regardless of the exact value. Only undershooting
   would show a light gap, so this is set generously for taller mobile rows. */
:root{--head-h:150px}
@media (max-width:760px){:root{--head-h:215px}}
.hero-top .utility{position:relative;z-index:201}
/* EVERY DARK OPENING SECTION BELONGS IN THIS SELECTOR, not just `.hero`. The
   404 opens with `.err` and was left out, so its dark band started below the
   header instead of behind it - a white strip across the top with the utility
   bar's white text invisible against it. If a page ever opens with some third
   class, it goes here too, or it gets the same gap. */
.hero-top .hero.sec-dark,
.hero-top .err.sec-dark{
  margin-top:calc(-1 * var(--head-h));
  padding-top:calc(var(--head-h) + clamp(46px,6vw,84px));
}
.hero-top .utility a{color:var(--dk-mut)}
.hero-top .utility a:hover{color:#fff}
.hero-top .utility .incident{color:var(--red-3)}

/* ---------- 3. LAYOUT ---------- */
.wrap{width:100%;max-width:var(--wrap);margin:0 auto;padding:0 24px}
.sec{padding:clamp(60px,8vw,112px) 0;position:relative}
.sec-tight{padding:clamp(44px,6vw,76px) 0}
/* .sec-mist and .sec-alt are RETIRED (15 Sep 2026) and deliberately left
   undefined. They were the tinted light bands, and alternating them was what
   made the page look like several different whites stitched together. Every
   light section is now transparent over one body base. Do not reinstate them:
   if a section needs separating from its neighbour, use .sec-dark, which is a
   deliberate change of surface rather than a shade nobody can name. */

/* ---------- the lattice AND the ambient wash on light bands ----------
   body::before (the wash) and body::after (the lattice) are both painted on the
   canvas, so any section with a background of its own covers BOTH. That is why
   the grid, and then the blue-to-white tint, appeared on plain .sec and vanished
   on every mist, alt, paper and gradient band - which is most of the page. The
   tint one was the more obvious of the two: it put a hard horizontal edge
   wherever a plain section met a banded one.

   Light bands re-draw both for themselves, using the tokens below. Two details:

   * `background-attachment:fixed` anchors the tile to the VIEWPORT, exactly as
     the fixed body layer does, so a mist section sitting next to a plain one
     shares the same lines rather than starting a new 64px grid of its own.
     Without it the lattice visibly steps at every section boundary.

   * `isolation:isolate` on the band plus `z-index:-1` on the layer puts the
     lattice above the band's own background and below its content, so nothing
     needs a z-index of its own.

   Dark sections are deliberately excluded - they carry their own mesh. The
   bands that live in home.css (.scaleband, .proof, .defband, .personcta) apply
   the same two tokens there, because a component may only be declared in the
   file that owns it. */
.sec-head{max-width:720px;margin:0 auto clamp(38px,6vw,60px);text-align:center}
.sec-head.left{margin-left:0;text-align:left}
.sec-head h2{margin-bottom:18px}
.divider{height:1px;background:linear-gradient(90deg,transparent,var(--line-2),transparent);border:0;margin:0}

/* DARK section block — flips everything inside to the dark palette.
   .sec-dark, .darkband and .foot all paint the SAME three layers, written once
   here and driven entirely by the backdrop tokens in section 1b. Do not add a
   background to a dark surface anywhere else — override a token instead. */
/* layer 0 — the base wash. Composed here rather than as a --bd-base token so
   that var(--bd-1) resolves against the surface being painted, which is what
   lets .foot dial in its own without re-stating the gradient. */
.sec-dark,.darkband,.foot{background:linear-gradient(180deg,var(--bd-1),var(--bd-2))}
.sec-dark,.darkband{color:var(--dk-fg);position:relative;isolation:isolate;overflow:hidden}

/* layer 1 — the grid. The lattice is a MASK, so its colour comes from the
   paint behind it, which is also what does the downward fade. */
.sec-dark::before,.darkband::before,.foot::before{content:"";position:absolute;inset:0;
  pointer-events:none;opacity:var(--bd-mesh-opacity);
  background-image:radial-gradient(120vw 92vh at 50% 0,var(--bd-mesh-ink),transparent 76%);
  -webkit-mask-image:var(--bd-mesh-tile);mask-image:var(--bd-mesh-tile);
  -webkit-mask-size:var(--bd-mesh-size) var(--bd-mesh-h);
  mask-size:var(--bd-mesh-size) var(--bd-mesh-h)}

/* layer 2 — the coloured lights, plus the pulse travelling along the bottom
   edge. Six background layers, so six entries in each list below: those are
   positional, and a miscount silently shifts every layer after it. */
.sec-dark::after,.darkband::after,.foot::after{content:"";position:absolute;inset:0;
  pointer-events:none;
  background-image:
    var(--bd-pulse-img),
    var(--bd-edge-img),
    radial-gradient(44vw 52vw at 2% 46%,var(--bd-green),transparent 60%),
    radial-gradient(52vw 52vw at 78% 26%,var(--bd-blue),transparent 62%),
    radial-gradient(46vw 46vw at 10% 6%,var(--bd-red),transparent 64%),
    radial-gradient(34vw 34vw at 92% 86%,var(--bd-red-2),transparent 62%);
  background-repeat:no-repeat;
  background-size:var(--bd-pulse-w) var(--bd-pulse-h),100% 1px,auto,auto,auto,auto;
  background-position:-90% 100%,0 100%,0 0,0 0,0 0,0 0;
  animation:bdPulse var(--bd-pulse-speed) linear infinite}
/* Where the animated .aura is present it supplies the lights instead, so the
   four static ones are dropped and only the pulse is left. Setting the colours
   on the pseudo-element is safe: it has no children, so the .aura spans keep
   reading the surface's own values. */
.sec-dark:has(.aura)::after,.darkband:has(.aura)::after{
  --bd-green:transparent;--bd-blue:transparent;--bd-red:transparent;--bd-red-2:transparent}
/* The pulse is the only thing moving here, but background-position is one
   property: every layer's position has to be restated on both keyframes. */
@keyframes bdPulse{
  from{background-position:-90% 100%,0 100%,0 0,0 0,0 0,0 0}
  to{background-position:190% 100%,0 100%,0 0,0 0,0 0,0 0}}
@media (prefers-reduced-motion:reduce){
  .sec-dark::after,.darkband::after,.foot::after{animation:none}}

/* Consecutive dark sections are wrapped in a .darkband, which paints ONE
   surface for the whole run. The sections inside keep .sec-dark purely for
   their typography and paint nothing themselves — stitching two independently
   painted surfaces together will not line up. */
.darkband>*{position:relative;z-index:1}
/* the sections inside contribute type colours only */
.darkband .sec-dark{background:none}
.darkband .sec-dark::before,.darkband .sec-dark::after{display:none;content:none}

.sec-dark>*{position:relative;z-index:1}
/* The node field sits above the triangle mesh and below the content. Needs to
   out-specify .sec-dark>* above, which would otherwise put the canvas in flow
   and push the hero content down by its full height. */
.sec-dark>canvas.nodefx{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:1;opacity:.9}
.sec-dark h1,.sec-dark h2,.sec-dark h3,.sec-dark h4{color:#fff}
.sec-dark .lede,.sec-dark p{color:var(--dk-soft)}
.sec-dark .muted{color:var(--dk-mut)}
/* THE FOOTER AND THE BAND ARE DARK GROUNDS TOO, and until 22 Sep 2026 only
   .sec-dark got these. The footer styles most of its own text explicitly -
   .foot .brand p, .foot .col a, .foot h5 all name a dark token - so the gap
   only showed on the classes it did not anticipate: `.muted` in the footer
   legal strip was taking --fg-mut, a paper-ground grey, on near-black.

   It measured 4.31 before and would have been wrong either way; darkening
   --fg-mut to clear AA on paper took it to 4.1, which is how it surfaced. A
   token tuned for one ground cannot be fixed for the other by moving it, so
   the ground picks the token, which is what these three lines do. */
.darkband .muted,.foot .muted{color:var(--dk-mut)}
.darkband .lede,.foot .lede{color:var(--dk-soft)}
.darkband h1,.darkband h2,.darkband h3,.darkband h4{color:#fff}

/* ---------- 4. TYPE ---------- */
h1,h2,h3,h4,h5{font-family:var(--display);letter-spacing:-.03em;margin:0;line-height:1.03;font-weight:800;color:var(--ink)}
h1{font-size:clamp(42px,7vw,86px);line-height:1.0}
h2{font-size:clamp(30px,4.4vw,56px);line-height:1.04}
h3{font-size:clamp(20px,2.3vw,26px);line-height:1.2;letter-spacing:-.02em}
h4{font-size:18px;font-weight:700;letter-spacing:-.01em}
p{margin:0 0 18px}
.accent{color:var(--red)}
/* Solid brand red, no gradient fade. These are display headings at 42px+,
   so #c30017 clears the 3:1 WCAG threshold for large text on both surfaces. */
.grad{color:var(--red)}
.sec-dark .grad{color:var(--red)}
.eyebrow{display:inline-flex;align-items:center;gap:12px;font-family:var(--mono);font-weight:700;font-size:12.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--red);margin:0 0 20px}
.eyebrow::before{content:"";width:28px;height:2px;background:var(--red);flex:none}
/* --red-3 ON DARK SECTIONS, 22 Sep 2026, and the reason is the sentence three
   rules up: "#c30017 clears the 3:1 WCAG threshold for LARGE text". That is
   true and it was measured - 3.03 against the band - but it only licenses
   .grad, which is 86px. The eyebrow is 12.5px, so its requirement is 4.5 and
   3.03 fails it. Same for the text link at 14-15px.

   --red-3 measures 5.46 on the same ground and is already the site's red for
   dark surfaces - the utility bar's incident link, .sec-dark .card .ico and
   the tier numbers all use it. Eleven pages were affected. */
.sec-dark .eyebrow,.darkband .eyebrow,.foot .eyebrow{color:var(--red-3)}
.sec-dark .eyebrow::before,.darkband .eyebrow::before,.foot .eyebrow::before{background:var(--red-3)}
.sec-dark .textlink,.darkband .textlink,.foot .textlink{color:var(--red-3)}
.sec-head:not(.left) .eyebrow{justify-content:center}
.lede{font-size:clamp(18px,2vw,21px);line-height:1.6;color:var(--fg-soft)}
.lede strong{color:var(--ink);font-weight:600}
.sec-dark .lede strong{color:#fff}
.muted{color:var(--fg-mut)}
.mono{font-family:var(--mono);letter-spacing:.02em}
.tight{max-width:66ch}

/* ---------- 5. BUTTONS ---------- */
.btn{position:relative;display:inline-flex;align-items:center;gap:10px;font-family:var(--display);font-weight:600;font-size:15.5px;letter-spacing:-.01em;padding:15px 28px;border-radius:10px;border:1px solid transparent;transition:transform .2s var(--ease),box-shadow .25s var(--ease),background .2s,color .2s,border-color .2s;white-space:nowrap}
.btn svg{width:18px;height:18px}
.btn:hover{transform:translateY(-2px)}
.btn-primary{background:linear-gradient(180deg,var(--red-2),var(--red));color:#fff;box-shadow:0 10px 26px -8px var(--red-glow),inset 0 1px 0 rgba(255,255,255,.28)}
.btn-primary:hover{box-shadow:0 16px 38px -10px var(--red-glow),inset 0 1px 0 rgba(255,255,255,.4)}
.btn-ghost{background:var(--paper);color:var(--ink);border-color:var(--line-2);box-shadow:var(--shadow-sm)}
.btn-ghost:hover{border-color:var(--ink);background:var(--paper)}
.sec-dark .btn-ghost{background:rgba(255,255,255,.05);color:#fff;border-color:var(--dk-line-2);box-shadow:none}
.sec-dark .btn-ghost:hover{background:rgba(255,255,255,.12);border-color:var(--dk-mut)}
.btn-lg{padding:18px 34px;font-size:16.5px}
.btn-sm{padding:11px 18px;font-size:14px}
/* `.btn` is nowrap so labels never break mid-phrase on desktop. The responsive
   block near the end of this file already sets width:100% on a phone, but that
   does not help: nowrap forces a min-content width wider than the viewport, so
   any CTA past roughly thirty characters made the whole page scroll sideways.
   Allowing the label to wrap is the actual fix. The large variant also loses
   some padding, which costs nothing and buys back two characters. */
@media (max-width:560px){
  .btn{white-space:normal;text-align:center;justify-content:center}
  .btn-lg{padding:16px 22px;font-size:15.5px}
}
.btnrow{display:flex;flex-wrap:wrap;gap:14px}
.textlink{display:inline-flex;align-items:center;gap:8px;color:var(--red);font-weight:600;font-family:var(--display)}
.textlink svg{width:16px;height:16px;transition:transform .2s var(--ease)}
.textlink:hover svg{transform:translateX(4px)}

.chip{display:inline-flex;align-items:center;gap:8px;font-family:var(--mono);font-size:11.5px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--fg-soft);background:var(--paper);border:1px solid var(--line);border-radius:100px;padding:8px 14px;box-shadow:var(--shadow-sm)}
.sec-dark .chip{color:var(--dk-soft);background:rgba(255,255,255,.05);border-color:var(--dk-line);box-shadow:none}
.chip .dot{width:7px;height:7px;border-radius:50%;background:var(--red);box-shadow:0 0 10px var(--red)}
.chip .dot.live{animation:pulse 1.6s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.4;transform:scale(.7)}}

/* ---------- 6. HEADER / NAV ---------- */
/* Floating dark pill.
   The header has to read correctly in three situations: at rest on the light
   page background, stuck and floating over the dark hero, and stuck over a
   light article page. Dark is the only treatment that works in all three - and
   it matches the CYBERSHIELD AI console, which is dark. */

/* A GROUND OF ITS OWN, added 22 Sep 2026 after the audit walked this bar's
   ancestors and found every one of them transparent up to <body>, which is
   near-white --bg. The bar's text is #fff and --dk-mut. It is legible today
   purely because a dark opening section is pulled up behind it.

   THAT HAS ALREADY FAILED ONCE, and the comment above records it: the 404
   page opens with .err rather than .hero, .err was missing from the pull-up
   selector, and the result was "a white strip across the top with the utility
   bar's white text invisible against it". The fix then was to add .err to the
   selector - which works until the next page opens with a fourth class.

   --bd-1 is the first stop of the band gradient, which is what is painted
   behind this bar on every page today; measured rgb(24,12,15) against the
   hero's own top stop. So this changes nothing visually where the pull-up
   works, and stops the bar disappearing where it does not. A fallback rather
   than a fix, and the selector above is still the thing to keep right. */
.utility{font-size:12.5px;background:var(--bd-1)}
.utility .wrap{display:flex;align-items:center;gap:22px;min-height:38px;flex-wrap:wrap}
/* The strip sits on the dark page, so it needs dark-theme colours: it was
   using the light-theme muted ink and reading as near-black on near-black. */
.utility a,.utility .uitem{color:#fff;transition:color .2s,opacity .2s}
.utility a:hover{color:#fff;opacity:1}
.utility .uitem{display:inline-flex;align-items:center;gap:8px}
.utility .uitem .ri{width:14px;height:14px;flex:none;color:var(--red);opacity:1}
.utility .uitem.pending{color:rgba(255,255,255,.88)}
.utility .uitem.pending .ri{opacity:.75}
.utility .social a{color:rgba(255,255,255,.72)}
.utility .social a:hover{color:#fff}
.utility .incident{color:#fff;font-weight:700}
.utility .incident .ri{color:var(--red)}
.utility .incident .ri{animation:pulse 1.6s infinite}
.utility .spacer{margin-left:auto}
.utility .social{display:flex;gap:16px}

/* pointer-events: the sticky bar spans the full width but only the pill should
   catch the pointer, otherwise an invisible strip blocks the page beside it. */
/* Skip link. Off-screen until focused, then pinned to the top-left. Was done
   with onfocus/onblur attributes, which script-src-attr 'none' blocks. */
.skiplink{position:absolute;left:-9999px;top:0;z-index:999}
.skiplink:focus,.skiplink:focus-visible{left:12px;top:12px}

.masthead{position:sticky;top:10px;z-index:200;padding:0 16px;margin-bottom:10px;pointer-events:none}
.masthead .bar{position:relative;
  pointer-events:auto;display:flex;align-items:center;gap:10px;min-height:64px;
  padding:9px 10px 9px 26px;border-radius:20px;
  background:rgba(13,16,23,.94);
  /* The blur is desktop-only, and barely earns its place even there: the bar
     sits on rgba(13,16,23,.94) and goes to .97 when stuck, so at most 6% of
     what is behind it ever shows through. A sticky element with a
     backdrop-filter is re-composited on every frame, which on a phone is a
     large, continuous GPU cost for an effect nobody can see. */
  -webkit-backdrop-filter:blur(16px) saturate(150%);backdrop-filter:blur(16px) saturate(150%);
  border:1px solid rgba(255,255,255,.10);
  box-shadow:0 10px 34px -18px rgba(6,8,14,.70),inset 0 1px 0 rgba(255,255,255,.06);
  transition:background .3s var(--ease),box-shadow .3s var(--ease),border-color .3s}
.masthead.stuck .bar{background:rgba(8,10,16,.97);border-color:rgba(255,255,255,.14);
  box-shadow:0 18px 46px -20px rgba(6,8,14,.85),inset 0 1px 0 rgba(255,255,255,.07)}

@media (pointer:coarse){
  .masthead .bar{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgba(13,16,23,.985)}
  .masthead.stuck .bar{background:rgba(8,10,16,.99)}
}

/* NOTE — an animation-pause rule was tried here on 25 Sep 2026 and REMOVED
   the same day. The idea was sound on paper: the drawer covers the screen, so
   anything still animating behind it is painting frames nobody can see.

   It did not fix the reported delay, and it was a good candidate for making
   it worse. `html.menu-open *` forces 26 compositor-driven animations to
   flatten onto the main layer at the exact moment the drawer opens — raster
   work on OPEN, with the cheaper re-promotion happening on CLOSE. That is the
   precise shape of the symptom: slow to open, instant to close.

   Kept as a comment because the next person will have the same idea. If it is
   tried again, measure paint on a real device first — this file's own history
   is three speculative fixes deep on this one bug. */


/* THE LOGO IS NOW THE LOGO. It was typeset in Poppins 800 with a pulsing CSS
   dot where the shield belongs, which read correctly at a glance and was not
   the mark: different letterforms, and #ff3b52 against the brand's #d5292a.

   The .b/.r/.spark rules below are kept deliberately. Anything not yet
   migrated still uses them, and a logo that silently vanishes is worse than
   one that is slightly wrong. Delete them once nothing references them.

   width and height on the img reserve the space before the SVG arrives. A
   logo that lands a beat late and shoves the nav sideways is the most visible
   layout shift a site can have. */
.logo img{display:block;height:30px;width:auto}

/* THE DOT, cycling red, blue, green.

   Two animations rather than one, because they are two different ideas and
   want different timings: `pulse` is the heartbeat at 2.4s, and `sparkhue`
   moves through the palette every 7.2s. Running them together means the
   colour changes while it is mid-breath rather than in lockstep, which is
   what stops it looking like a status light on a router.

   The dark-surface blue and green are used, not --blue and --green. Those are
   tuned to carry on paper and go muddy on the masthead's near-black. Each
   colour holds for roughly a fifth of the cycle and then crossfades, so it
   reads as one dot changing rather than three dots taking turns. */
@keyframes sparkhue{
  0%,20%   {background:var(--red-3);   box-shadow:0 0 14px var(--red-2)}
  33%,53%  {background:var(--blue-dk); box-shadow:0 0 14px var(--blue)}
  66%,86%  {background:var(--green-dk);box-shadow:0 0 14px var(--green)}
  100%     {background:var(--red-3);   box-shadow:0 0 14px var(--red-2)}
}
.logo{font-family:var(--display);font-weight:800;font-size:22px;letter-spacing:-.03em;white-space:nowrap;display:inline-flex;align-items:center;gap:2px}
.logo .b{color:#fff}
.logo .r{color:var(--red-3);font-weight:600}
.logo .spark{width:8px;height:8px;border-radius:50%;background:var(--red-3);
  box-shadow:0 0 14px var(--red-2);margin-left:8px;flex:none;
  animation:pulse 2.4s infinite, sparkhue 7.2s infinite}

.mainnav{margin-left:auto;display:flex;align-items:center;gap:0;flex-wrap:nowrap}
.navitem>button{background:none;border:0;cursor:pointer;font:inherit;line-height:inherit}
/* Preview mode: a page that is not built yet is shown but never linked, so the
   structure can be reviewed without shipping a sitewide link to a 404. The dot
   is the only thing that says "not yet". */
.navitem .pending,.panel .col .pending{color:var(--dk-mut);cursor:default}
.wip{display:inline-block;width:5px;height:5px;border-radius:50%;background:var(--amber,#e5a13a);
  margin-left:7px;vertical-align:middle;flex:none;opacity:.85}

.panel .col h5 .pending{display:inline;color:var(--dk-mut);padding:0;border:0}
.navitem>button.pending{color:var(--dk-mut)}
.drawer .pending{display:block;padding:9px 4px;color:var(--dk-mut);font-size:15px}
.navitem{position:static}
/* Hover bridge across the gap between the bar and the panel. Belongs to
   .navitem, not .panel: the panel is pointer-events:none until hovered, so it
   can never be the thing that keeps itself open. */
.navitem.has-panel::after{content:"";position:absolute;left:0;right:0;top:100%;height:0;pointer-events:none}
.navitem.has-panel:hover::after,
.navitem.has-panel:focus-within::after,
.navitem.has-panel.open::after{height:26px;pointer-events:auto}
/* Hover bridge. The panel opens below the pill, so the pointer crossed dead
   space on its way down and the menu closed mid-travel. This extends the nav
   item's own hover area across that gap - .navitem:hover is what opens the
   panel, so keeping the pointer "inside the item" is what matters.
   Inert until hovered, so it never blocks a neighbouring item. */
.navitem>:is(a,button){position:relative;display:inline-flex;align-items:center;gap:6px;padding:10px 12px;border-radius:11px;font-family:var(--display);font-weight:500;font-size:15px;color:var(--dk-soft);white-space:nowrap;transition:color .2s,background .2s}
.navitem>:is(a,button) .caret{width:14px;height:14px;opacity:.6;transition:transform .25s}
.navitem>:is(a,button):hover{color:#fff;background:rgba(255,255,255,.09)}
.navitem.has-panel:hover>a,.navitem.has-panel.open>:is(a,button){color:#fff;background:rgba(255,255,255,.09)}
.navitem.has-panel:hover>a .caret,.navitem.has-panel.open>:is(a,button) .caret{transform:rotate(180deg)}
.navitem>:is(a,button)[aria-current]{color:#fff}
.navitem>:is(a,button)[aria-current]::before{content:"";position:absolute;left:14px;right:14px;bottom:2px;height:2px;border-radius:2px;background:var(--red-3)}
.navitem>:is(a,button):focus-visible{outline:2px solid var(--red-3);outline-offset:2px}

.panel{position:absolute;top:calc(100% + 14px);left:0;right:0;transform:translateY(8px);
  background:linear-gradient(180deg,#12161f,#0d1017);
  border:1px solid rgba(255,255,255,.12);border-radius:18px;
  box-shadow:0 34px 80px -34px rgba(0,0,0,.9),inset 0 1px 0 rgba(255,255,255,.05);
  padding:30px 32px;opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .22s var(--ease),transform .22s var(--ease),visibility .22s;transition-delay:.2s;
  display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:34px;z-index:210}
.panel .feature{max-width:300px;align-self:start}

.navitem.has-panel:hover .panel,.navitem.has-panel:focus-within .panel,
.navitem.has-panel.open .panel{opacity:1;visibility:visible;pointer-events:auto;transform:translateY(0);transition-delay:0s}
.panel::before{content:"";position:absolute;top:-20px;left:-12px;right:-12px;height:22px}
.panel .col h5{display:flex;align-items:center;gap:10px;font-family:var(--mono);font-size:11px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--red-3);margin:0 0 14px;line-height:1.3}
.panel .col h5 .gi{width:19px;height:19px;flex:none;opacity:.95}
.panel .col h5 a{display:inline;padding:0;border:0;color:inherit;font:inherit}
.panel .col h5 a:hover{background:none;transform:none;color:#fff}
.panel .grp + .grp{margin-top:26px;padding-top:24px;border-top:1px solid rgba(255,255,255,.08)}
/* Row styling must not reach the pending span inside a heading, or the heading
   becomes a space-between flex and its text wraps. Direct children only. */
.panel .col>a,.panel .col>span.pending,.panel .grp>a,.panel .grp>span.pending{
  display:flex;align-items:center;gap:11px;
  padding:9px 12px;border-radius:9px;color:var(--dk-soft);font-size:14.5px;font-weight:500;
  line-height:1.4;transition:.18s;border:1px solid transparent}
.panel .ri{width:18px;height:18px;flex:none;color:var(--red-3);opacity:.85;transition:opacity .18s}
.panel .lb{flex:1 1 auto;min-width:0}
.panel .col>a:hover .ri,.panel .grp>a:hover .ri{opacity:1}
.panel .wip{margin-left:auto}
.panel .col>a:hover,.panel .grp>a:hover{color:#fff;background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.10);transform:translateX(3px)}
.panel .feature{background:linear-gradient(150deg,rgba(195,0,23,.22),rgba(255,255,255,.02));border:1px solid rgba(255,255,255,.12);border-radius:14px;padding:20px}
.panel .feature .k{font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--red-3)}
.panel .feature h4{margin:10px 0;font-size:19px;color:#fff}
.panel .feature p{color:var(--dk-soft);font-size:14px;margin:0 0 14px}
.panel .feature .textlink{color:#fff;display:inline-flex;align-items:center;gap:8px}
.panel .feature.is-pending{opacity:.9}
.panel .feature.is-pending .textlink{color:var(--dk-mut);font-size:13px}

.nav-cta{margin-left:10px;border-radius:100px}
.nav-cta svg{width:16px;height:16px;transition:transform .25s var(--ease)}
.nav-cta:hover svg{transform:translateX(3px)}
.navtoggle{display:none;margin-left:auto;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.15);border-radius:12px;padding:10px 12px;color:#fff;box-shadow:none}
.navtoggle svg{width:22px;height:22px}

.drawer{position:fixed;inset:0;z-index:300;visibility:hidden;pointer-events:none}
.drawer .scrim{position:absolute;inset:0;background:rgba(4,6,11,.62);opacity:0;transition:opacity .3s}
.drawer .sheet{position:absolute;top:0;right:0;height:100%;width:min(420px,90vw);background:#0d1017;border-left:1px solid rgba(255,255,255,.10);transform:translateX(100%);transition:transform .34s var(--ease);overflow-y:auto;padding:20px}
.drawer.open{visibility:visible;pointer-events:auto}
.drawer.open .scrim{opacity:1}
.drawer.open .sheet{transform:translateX(0)}
.drawer .dhead{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.drawer .close{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.15);border-radius:11px;padding:9px;color:#fff}
.drawer .close svg{width:20px;height:20px}
.macc{border-top:1px solid rgba(255,255,255,.10)}
.macc>button,.macc>a{width:100%;display:flex;align-items:center;justify-content:space-between;padding:16px 4px;background:none;border:0;color:#fff;font-family:var(--display);font-weight:600;font-size:17px;text-align:left}
.macc>button svg,.macc>a svg{width:18px;height:18px;transition:transform .25s;color:var(--dk-mut)}
.macc.open>button svg{transform:rotate(180deg)}
.macc .sub{display:grid;grid-template-rows:0fr;transition:grid-template-rows .3s var(--ease)}
.macc.open .sub{grid-template-rows:1fr}
.macc .sub>div{overflow:hidden}
.macc .sub a{display:block;padding:10px 4px;color:var(--dk-soft);font-size:15px}
.macc .sub a:hover{color:var(--red-3)}
.drawer .dcta{margin-top:12px}

/* ---------- 7. FOOTER (dark accent) ---------- */
/* The footer is a SEPARATE surface from the sections: it re-declares the
   backdrop tokens from section 1b for itself, so it can be dialled in on its
   own without duplicating any of the layer machinery. */
.foot{
  --bd-1:#120809; --bd-2:#070405;
  --bd-mesh-opacity:.34;
  --bd-red:rgba(195,0,23,.16); --bd-red-2:rgba(195,0,23,.07);
  --bd-blue:rgba(47,107,255,.10); --bd-green:rgba(18,161,80,.13);
  --bd-pulse-speed:11s;
  position:relative;isolation:isolate;overflow:hidden;margin-top:0;
  color:var(--dk-soft)}

.foot>*{position:relative}
.foot .top{display:grid;grid-template-columns:1.4fr repeat(4,1fr);gap:36px;padding:72px 0 48px}
.foot .brand .logo .b{color:#fff}
.foot .brand .logo{margin-bottom:16px}
.foot .brand .logo img{height:32px}
.foot .brand p{color:var(--dk-mut);max-width:34ch;font-size:15px}
.foot .contact{margin-top:18px;display:grid;gap:6px;font-size:14.5px}
.foot .contact a{color:var(--dk-soft)}
.foot .contact a:hover{color:var(--red-3)}
.foot h5{font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--red-3);margin:0 0 16px}
.foot .col a{display:block;padding:6px 0;color:var(--dk-mut);font-size:14.5px;transition:.18s}
.foot .col a:hover{color:#fff;transform:translateX(3px)}
.foot .strip{display:flex;flex-wrap:wrap;align-items:center;gap:18px;padding:24px 0;border-top:1px solid var(--dk-line);color:var(--dk-mut);font-size:13.5px}
.foot .strip .spacer{margin-left:auto}
.foot .strip a{color:var(--dk-soft)}
.foot .strip a:hover{color:#fff}

/* ---------- 8. HERO (light) ---------- */
.hero{position:relative;overflow:hidden;padding:clamp(64px,10vw,128px) 0 clamp(56px,7vw,100px)}
/* Wash for a LIGHT hero only. A dark hero takes its ::after from the backdrop
   block in section 3 instead — this uses the background shorthand, so left
   unscoped it resets that layer's size and position back to initial. */
.hero:not(.sec-dark)::after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;background:radial-gradient(60vw 60vw at 78% 20%,rgba(195,0,23,.06),transparent 60%)}

/* ---------- the seam where dark meets light ----------
   A 3px spectrum line on the boundary between a dark block and the light page.
   It sits at the BOTTOM of every dark hero, and (with .at-top) at the TOP of
   the footer, which are the two places the site changes ground colour.

   THE THREE COLOURS ARE NOT DECORATIVE. They are the exact signal colours the
   console component already uses for its rows — #e11d2a, #2f6bff and #12a150
   (see .console .row in home.css). Nearly every dark hero on the site has a
   console in it using that vocabulary, so the line reads as part of the same
   system rather than a rainbow somebody added.

   IT IS A REAL ELEMENT RATHER THAN A PSEUDO-ELEMENT because .sec-dark::before,
   .sec-dark::after, .foot::before and .foot::after are ALL already taken by
   the texture and grid layers above. z-index 3 puts it over them.

   TO ADD IT ANYWHERE ELSE: drop <div class="seam" aria-hidden="true"></div> in
   as the last child of a position:relative dark block. No CSS change needed. */
/* The signal band. Declared once as a token because two components paint it:
   .seam here, and .readbar in home.css. It reads as the three console signal
   colours resolving through each other, and it only works FULL-BLEED - across
   a 28px eyebrow dash or a 60px nav underline the six stops compress into a
   rainbow smear, so do not reach for it as a small accent. */
:root{
  --seam-grad:linear-gradient(90deg,#e11d2a 0%,#ff3b52 14%,#7a3fb0 34%,#2f6bff 52%,#17a2a2 72%,#12a150 100%);
  /* Same stops running top-to-bottom, for the vertical rails on .svc cards.
     A 90deg gradient on a 3px-wide element paints one flat colour, so a
     vertical bar needs this one - it is not interchangeable. */
  --seam-grad-v:linear-gradient(180deg,#e11d2a 0%,#ff3b52 14%,#7a3fb0 34%,#2f6bff 52%,#17a2a2 72%,#12a150 100%)}
.seam{position:absolute;left:0;right:0;bottom:0;height:3px;z-index:3;pointer-events:none;
  background:var(--seam-grad)}
/* A short bloom off the line, so the seam glows rather than just stops. Masked
   to fade away from the line; the mask is progressive enhancement — without it
   the gradient's own transparency still does most of the work. */
.seam::after{content:"";position:absolute;left:0;right:0;bottom:3px;height:64px;
  background:linear-gradient(90deg,rgba(225,29,42,.16),rgba(47,107,255,.16) 52%,rgba(18,161,80,.14));
  -webkit-mask-image:linear-gradient(0deg,#000,transparent);
  mask-image:linear-gradient(0deg,#000,transparent)}
/* Footer variant: the line goes on the top edge and the bloom falls downward
   into the dark rather than rising out of it. */
/* The 404 banner is .err.sec-dark rather than .hero, and is not otherwise
   a positioned ancestor. Without this the seam would escape to the nearest
   positioned parent and land somewhere arbitrary. */
.err.sec-dark{position:relative}
.seam.at-top{top:0;bottom:auto}
.seam.at-top::after{bottom:auto;top:3px;
  -webkit-mask-image:linear-gradient(180deg,#000,transparent);
  mask-image:linear-gradient(180deg,#000,transparent)}
.hero .wrap{position:relative;z-index:2}
.hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:56px;align-items:center}
.hero h1{margin:18px 0 22px}
.hero .sub{font-size:clamp(18px,2.1vw,22px);color:var(--fg-soft);max-width:54ch}
.hero .sub strong{color:var(--ink);font-weight:600}
.hero .btnrow{margin-top:34px}
.hero .stats{display:flex;flex-wrap:wrap;gap:14px 34px;margin-top:44px;padding-top:28px;border-top:1px solid var(--line)}
.hero .stat .n{font-family:var(--display);font-weight:800;font-size:clamp(28px,3.6vw,40px);letter-spacing:-.03em;color:var(--ink);line-height:1}
.hero .stat .n .u{color:var(--red)}
.hero .stat .l{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--fg-mut);margin-top:8px}

.orbital{position:relative;aspect-ratio:1;max-width:520px;margin-inline:auto;width:100%}
.orbital canvas{position:absolute;inset:0;width:100%;height:100%}

/* dark hero variant + agent portrait */
.hero.sec-dark{padding-top:clamp(56px,8vw,104px)}

.hero.sec-dark .stat .n{color:#fff}
.hero.sec-dark .stat .n .u{color:var(--red-3)}
.hero.sec-dark .stats{border-top-color:var(--dk-line)}
.hero-agent{position:relative;max-width:520px;margin-inline:auto;width:100%;display:grid;place-items:center}
.hero-agent img{position:relative;z-index:2;width:100%;height:auto;filter:drop-shadow(0 30px 50px rgba(0,0,0,.6));animation:agentFloat 7s ease-in-out infinite}
.hero-agent::before{content:"";position:absolute;z-index:1;left:50%;top:46%;transform:translate(-50%,-50%);width:92%;padding-top:92%;border-radius:50%;background:radial-gradient(circle,rgba(47,107,255,.28),rgba(47,107,255,.05) 45%,transparent 68%);filter:blur(6px)}
.hero-agent::after{content:"";position:absolute;z-index:0;inset:0;background-image:linear-gradient(rgba(255,255,255,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.05) 1px,transparent 1px);background-size:34px 34px;border-radius:50%;-webkit-mask:radial-gradient(circle at 50% 50%,#000 40%,transparent 70%);mask:radial-gradient(circle at 50% 50%,#000 40%,transparent 70%)}
@keyframes agentFloat{50%{transform:translateY(-12px)}}
@media (prefers-reduced-motion:reduce){.hero-agent img{animation:none}}

/* featured agent showcase (real portraits) */
.featured{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-bottom:56px}
.feat{--c:var(--blue);position:relative;display:grid;grid-template-columns:200px 1fr;gap:6px;align-items:center;border:1px solid var(--dk-line);border-radius:var(--radius-lg);background:linear-gradient(180deg,var(--dk-panel),#0d1017);overflow:hidden;padding:18px}
.feat::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--c),transparent 80%)}
.feat .pic{position:relative;height:250px;display:grid;place-items:end center}
.feat .pic::after{content:"";position:absolute;inset:auto 0 6px 0;height:60%;background:radial-gradient(60% 60% at 50% 100%,color-mix(in srgb,var(--c) 30%,transparent),transparent 72%);pointer-events:none}
.feat .pic img{position:relative;z-index:1;height:250px;width:auto;filter:drop-shadow(0 18px 28px rgba(0,0,0,.55))}
.feat .meta{padding:8px 14px 8px 4px}
.feat .code{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.12em;color:var(--c);border:1px solid color-mix(in srgb,var(--c) 40%,transparent);border-radius:6px;padding:4px 8px;display:inline-block;margin-bottom:12px}
.feat h4{color:#fff;font-size:19px;margin-bottom:8px}
.feat p{color:var(--dk-mut);font-size:14px;margin:0}
@media (max-width:1080px){.featured{grid-template-columns:1fr}}
@media (max-width:520px){.feat{grid-template-columns:1fr}.feat .pic,.feat .pic img{height:220px}}

/* ---------- platform showcase hero (CardinalOps-style) ---------- */
.showcase{position:relative;overflow:hidden;padding:clamp(52px,7vw,96px) 0 clamp(48px,7vw,88px)}
.showcase .headline{max-width:900px;margin:0 auto;text-align:center}
.showcase .headline .btnrow{justify-content:center;margin-top:30px}
.showcase .headline .sub{margin-inline:auto}
.appstage{position:relative;max-width:1180px;margin:clamp(40px,6vw,60px) auto 0}
.appstage::before{content:"";position:absolute;z-index:1;left:50%;top:38%;transform:translate(-50%,-50%);width:78%;height:78%;background:radial-gradient(closest-side,rgba(195,0,23,.38),transparent 72%);filter:blur(34px);pointer-events:none}
.appframe{position:relative;z-index:2;border-radius:16px;border:1px solid var(--dk-line-2);overflow:hidden;box-shadow:0 44px 110px -34px rgba(0,0,0,.85),0 0 0 1px rgba(255,255,255,.04);background:#fff}
.appframe img{width:100%;display:block}
.appframe::after{content:"";position:absolute;inset:0;pointer-events:none;box-shadow:inset 0 -60px 60px -40px rgba(0,0,0,.25)}
.callout{position:absolute;z-index:3;display:flex;gap:12px;align-items:flex-start;background:rgba(255,255,255,.94);backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.7);border-radius:12px;padding:14px 16px;box-shadow:0 24px 54px -18px rgba(0,0,0,.55);max-width:264px;animation:calloutFloat 6s ease-in-out infinite}
.callout .ic{flex:none;width:36px;height:36px;border-radius:9px;display:grid;place-items:center;background:var(--red-wash);color:var(--red)}
.callout .ic svg{width:20px;height:20px}

/* Any .ic chip holds a 24x24 glyph. Without an explicit size an inline SVG
   stretches to fill its container - this is what blew the home-page icons up
   to full width. Keep this rule; more specific ones (.callout .ic svg) win. */
.ic svg{width:22px;height:22px;flex:none}

/* Anchor targets must clear the sticky masthead, or "See packages" /
   "Meet the agents" drop the heading behind the header. */
[id]{scroll-margin-top:100px}

.callout .k{font-family:var(--mono);font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--red);font-weight:700}
.callout h5{font-size:14px;color:var(--ink);margin:3px 0 3px;font-weight:700}
.callout p{font-size:12.5px;color:var(--fg-mut);margin:0;line-height:1.4}
.callout.tr{top:14%;right:-3%}
.callout.bl{bottom:16%;left:-3%;animation-delay:1.4s}
@keyframes calloutFloat{50%{transform:translateY(-9px)}}
@media (prefers-reduced-motion:reduce){.callout{animation:none}}
@media (prefers-reduced-motion:reduce){.logo .spark{animation:none;background:var(--red-3)}}
@media (max-width:900px){.callout{display:none}.appstage::before{width:90%}}

/* two-path cards: service vs platform */
.paths{display:grid;grid-template-columns:1fr 1fr;gap:24px}
.path{position:relative;display:flex;flex-direction:column;border:1px solid var(--line);border-radius:var(--radius-lg);background:var(--paper);padding:34px;box-shadow:var(--shadow-sm);transition:transform .3s var(--ease),box-shadow .3s,border-color .3s;overflow:hidden}
.path:hover{transform:translateY(-6px);box-shadow:var(--shadow);border-color:var(--line-2)}
.path::before{content:"";position:absolute;top:0;left:0;right:0;height:4px;background:linear-gradient(90deg,var(--red),transparent 75%)}
.path .k{font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--red);font-weight:700;margin-bottom:14px}
.path h3{font-size:24px;margin-bottom:12px}
.path p{color:var(--fg-mut);margin:0 0 18px}
.path ul{margin:0 0 24px;display:grid;gap:9px}
.path li{position:relative;padding-left:26px;font-size:14.5px;color:var(--fg-soft)}
.path li::before{content:"";position:absolute;left:0;top:7px;width:14px;height:14px;border-radius:50%;background:var(--red-wash);box-shadow:inset 0 0 0 1.5px var(--red)}
.path .btn{margin-top:auto;align-self:flex-start}
@media (max-width:820px){.paths{grid-template-columns:1fr}}

/* clean roster card (icon + text, no robot) */
.rost{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:14px}
.ag{--c:var(--blue);position:relative;display:flex;gap:14px;align-items:flex-start;border:1px solid var(--dk-line);border-radius:var(--radius);background:var(--dk-panel);padding:18px;transition:transform .25s var(--ease),border-color .25s}
.ag:hover{transform:translateY(-4px);border-color:color-mix(in srgb,var(--c) 50%,var(--dk-line-2))}
.ag .em{flex:none;width:42px;height:42px;border-radius:11px;display:grid;place-items:center;background:color-mix(in srgb,var(--c) 15%,transparent);border:1px solid color-mix(in srgb,var(--c) 34%,transparent);color:var(--c)}
.ag .em svg{width:22px;height:22px}
.ag h4{color:#fff;font-size:15px;margin-bottom:3px}
.ag .role{color:var(--dk-mut);font-size:12.5px;margin:0;line-height:1.45}
.ag .code{position:absolute;top:12px;right:12px;font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.1em;color:var(--c);opacity:.8}

/* ---------- 9. COMPONENTS ---------- */
.card{position:relative;background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);padding:28px;transition:transform .3s var(--ease),border-color .3s,box-shadow .3s;overflow:hidden;box-shadow:var(--shadow-sm)}
.card:hover{transform:translateY(-5px);border-color:var(--line-2);box-shadow:var(--shadow)}
/* -5px, not -6px. Every other card family on the site lifts 5 and this one
   lifted 6, which nobody would name but everybody would feel on a page that
   puts two families near each other. Aligned 24 Sep 2026 with the shared card
   rail — see THE CARD RAIL in home.css. */
.card .ico{width:46px;height:46px;border-radius:12px;display:grid;place-items:center;background:var(--red-wash);border:1px solid var(--line);margin-bottom:18px;color:var(--red)}
.card .ico svg{width:24px;height:24px}
.card h3{margin-bottom:10px}
.card p{color:var(--fg-mut);font-size:15.5px;margin:0}
.card .textlink{margin-top:16px}
.sec-dark .card{background:var(--dk-panel);border-color:var(--dk-line);box-shadow:none}
.sec-dark .card p{color:var(--dk-mut)}
.sec-dark .card .ico{background:rgba(195,0,23,.16);border-color:var(--dk-line);color:var(--red-3)}

.glass{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow-sm)}
.sec-dark .glass{background:rgba(255,255,255,.03);border-color:var(--dk-line);box-shadow:none}

.cards-3{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
.cards-2{display:grid;grid-template-columns:repeat(2,1fr);gap:24px}
.cards-4{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}

.statband{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--paper);box-shadow:var(--shadow-sm)}
.statband .cell{padding:34px 28px;border-left:1px solid var(--line)}
.statband .cell:first-child{border-left:0}
.statband .n{font-family:var(--display);font-weight:800;font-size:clamp(34px,4.2vw,52px);letter-spacing:-.04em;line-height:.9;color:var(--ink)}
.statband .n .u{color:var(--red)}
.statband .l{font-family:var(--mono);font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--fg-mut);margin-top:14px}
.statband .s{color:var(--fg-mut);font-size:13.5px;margin-top:8px}

.marquee{overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent)}
.marquee .track{display:flex;gap:56px;width:max-content;animation:scrollx 34s linear infinite}
.marquee:hover .track{animation-play-state:paused}
/* --fg-mut, not --fg-faint, from 22 Sep. These are 19px, so they need 3:1 and
   --fg-faint measured 2.39 on --bg. --fg-mut gives 4.52 there, and these are
   product names a reader is meant to recognise - Sentinel, Defender XDR, Okta
   - rather than decoration. --fg-faint stays as it is for what it is actually
   for: the small ordinal in the corner of a card, where barely-there is the
   point. */
.marquee .item{font-family:var(--display);font-weight:700;font-size:19px;color:var(--fg-mut);white-space:nowrap;letter-spacing:-.01em;display:flex;align-items:center;gap:10px}
.marquee .item::before{content:"";width:8px;height:8px;border-radius:2px;background:var(--red);opacity:.55}
@keyframes scrollx{to{transform:translateX(-50%)}}

.ctaband{position:relative;overflow:hidden;border-radius:var(--radius-lg);border:1px solid var(--dk-line-2);background:linear-gradient(120deg,var(--dk),var(--void));color:#fff;padding:clamp(40px,6vw,72px);text-align:center}
.ctaband::before{content:"";position:absolute;inset:0;background:radial-gradient(80% 120% at 50% -10%,rgba(195,0,23,.34),transparent 60%)}
.ctaband>*{position:relative}
.ctaband h2{margin-bottom:18px;color:#fff}
.ctaband .lede{max-width:60ch;margin:0 auto 30px;color:var(--dk-soft)}
.ctaband .eyebrow{color:var(--red-3)}
.ctaband .btnrow{justify-content:center}

.badge{display:inline-flex;align-items:center;gap:9px;font-family:var(--mono);font-size:11.5px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--fg-soft);background:var(--paper);border:1px solid var(--line);border-radius:100px;padding:9px 16px;margin-bottom:22px;box-shadow:var(--shadow-sm)}
.badge svg{width:15px;height:15px;color:var(--red)}

/* ---------- 10. AGENTS + FIGURE ---------- */
.squad{margin-bottom:44px}
.squad-head{display:flex;align-items:center;gap:14px;margin-bottom:22px}
.squad-tag{--c:var(--red);display:inline-flex;align-items:center;gap:9px;font-family:var(--mono);font-size:12px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--c);border:1px solid color-mix(in srgb,var(--c) 40%,transparent);background:color-mix(in srgb,var(--c) 12%,transparent);border-radius:100px;padding:8px 16px}
.squad-tag .sq{width:9px;height:9px;border-radius:50%;background:var(--c);box-shadow:0 0 12px var(--c)}
.squad-head .desc{color:var(--dk-mut);font-size:14.5px}
.squad-head .rule{flex:1;height:1px;background:var(--dk-line)}

.agents-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:18px}
.agent{--c:var(--red);position:relative;border:1px solid var(--dk-line);border-radius:var(--radius);padding:0 22px 22px;background:linear-gradient(180deg,var(--dk-panel),#0e1119);overflow:hidden;transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.agent::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--c),transparent 85%)}
.agent:hover{transform:translateY(-6px);border-color:color-mix(in srgb,var(--c) 55%,var(--dk-line-2));box-shadow:0 26px 60px -26px color-mix(in srgb,var(--c) 70%,transparent)}
.agent .figwrap{position:relative;height:190px;margin:0 -22px 4px;display:grid;place-items:end center}
.agent .figwrap::after{content:"";position:absolute;inset:0;background:radial-gradient(60% 50% at 50% 88%,color-mix(in srgb,var(--c) 22%,transparent),transparent 70%);pointer-events:none}
.agent .afig{height:190px;width:auto}
.agent .top{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.agent .num{font-family:var(--mono);font-size:12px;font-weight:700;letter-spacing:.1em;color:var(--dk-mut)}
.agent .code{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.12em;color:var(--c);border:1px solid color-mix(in srgb,var(--c) 40%,transparent);border-radius:6px;padding:4px 8px}
.agent h4{font-size:16.5px;margin-bottom:6px;color:#fff;letter-spacing:-.01em}
.agent .role{font-size:13.5px;color:var(--dk-mut);margin:0;line-height:1.5}

/* figure animations (all CSS, reduced-motion safe) */
.afig{overflow:visible}
.afig .fig{animation:figFloat 5.5s ease-in-out infinite;filter:drop-shadow(0 8px 18px rgba(0,0,0,.55))}
.afig .holo{animation:holoFloat 6.5s ease-in-out infinite;transform-origin:center}
.afig .core{transform-box:fill-box;transform-origin:center;animation:coreGlow 2.4s ease-in-out infinite}
.afig .visor{animation:visorGlow 3.2s ease-in-out infinite}
.afig .spark{animation:pulse 2s infinite}
.afig .ring{transform-box:fill-box;transform-origin:center;animation:ringBreath 4.2s ease-in-out infinite}
.afig .ring.r2{animation-delay:.7s}.afig .ring.r3{animation-delay:1.4s}
.afig .scan{animation:scanY 3.4s cubic-bezier(.4,0,.6,1) infinite}
@keyframes figFloat{50%{transform:translateY(-7px)}}
@keyframes holoFloat{50%{transform:translateY(-5px)}}
@keyframes coreGlow{0%,100%{opacity:.75}50%{opacity:1;filter:drop-shadow(0 0 7px var(--c))}}
@keyframes visorGlow{0%,100%{opacity:.6}50%{opacity:1}}
@keyframes ringBreath{0%,100%{opacity:.22}50%{opacity:.6}}
@keyframes scanY{0%{transform:translateY(0)}100%{transform:translateY(58px)}}

/* ---------- 11. ARTICLE / LIST ---------- */
/* ---------- article body ----------
   WAS max-width:760px, which made every case study, learning post and blog
   article a narrow column stranded in a very wide page while every other
   template on the site runs to var(--wrap). Adam, 16 Sep 2026: keep the same
   width as everything else.

   Widening the prose alone to 1240px would be unreadable - running text past
   roughly 80 characters a line is measurably harder to follow, which is why
   the narrow column existed in the first place. So the CONTAINER now matches
   the rest of the site and the contents list moves out of the flow into the
   space that buys, which is what the width is actually for. The prose column
   lands near 880px: wider than before, and still a sane measure.

   Explicit row placement rather than auto-flow: the contents list comes BEFORE
   the prose in the DOM (it has to, for keyboard and screen-reader order), and
   sparse auto-placement would push the prose down to row 3 and leave a hole. */
.article{max-width:var(--wrap);margin:0 auto;padding:clamp(40px,6vw,72px) 24px;padding-top:clamp(34px,4vw,52px);
  display:grid;grid-template-columns:minmax(0,1fr) 250px;column-gap:clamp(32px,4vw,60px);align-items:start}
/* Rows are pinned rather than auto-placed. The hero image is optional, and
   when it is absent row 1 is empty - sparse auto-placement then backfills the
   next item into it, which put the related-service block above the prose. An
   empty row collapses to zero height, so pinning costs nothing when there is
   no image. */
.article .arthero-img{grid-column:1 / -1;grid-row:1;min-width:0}
.article .artmain{grid-column:1;grid-row:2;min-width:0}
.article .arttoc{grid-column:2;grid-row:2;position:sticky;top:calc(var(--head-h) + 18px);margin:0;align-self:start}
@media (max-width:1000px){
  /* One column again, contents list back in normal flow above the prose. */
  .article{display:block;max-width:820px}
  .article .arttoc{position:static;margin:0 0 clamp(32px,4vw,44px)}
}
.crumbs{font-family:var(--mono);font-size:12px;letter-spacing:.06em;color:var(--fg-mut);margin-bottom:24px;display:flex;gap:8px;flex-wrap:wrap}
.crumbs a:hover{color:var(--red)}
.article .kicker{color:var(--red);font-family:var(--mono);font-size:12px;letter-spacing:.16em;text-transform:uppercase;font-weight:700}
.article h1{font-size:clamp(34px,5vw,54px);margin:14px 0 22px;line-height:1.04}
.article .meta{display:flex;flex-wrap:wrap;gap:8px 22px;align-items:center;color:var(--fg-mut);font-size:14px;padding-bottom:26px;border-bottom:1px solid var(--line);margin-bottom:36px}
.article .meta .av{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--red),var(--red-dk));display:grid;place-items:center;font-family:var(--display);font-weight:700;color:#fff;font-size:14px}
.prose{font-size:18px;line-height:1.75;color:var(--fg)}
.prose h2{font-size:clamp(26px,3.4vw,34px);margin:44px 0 16px}
.prose h3{margin:32px 0 12px}
.prose p{margin:0 0 22px}
.prose .lede{font-size:21px;color:var(--ink);font-weight:400;margin-bottom:28px}
.prose a{color:var(--red);text-decoration:underline;text-underline-offset:3px}
.prose ul,.prose ol{margin:0 0 22px;padding-left:22px}
.prose li{margin-bottom:10px}
.prose li::marker{color:var(--red)}
.prose blockquote{margin:28px 0;padding:20px 26px;border-left:3px solid var(--red);background:var(--red-wash);border-radius:0 var(--radius-sm) var(--radius-sm) 0;font-family:var(--display);font-size:20px;color:var(--ink);font-weight:500}
.prose code{font-family:var(--mono);font-size:.88em;background:var(--bg-2);padding:2px 6px;border-radius:5px;color:var(--red-dk)}
.prose img{border-radius:var(--radius);border:1px solid var(--line);margin:28px 0}
.share{display:flex;gap:10px;margin-top:40px;padding-top:28px;border-top:1px solid var(--line);align-items:center}
.share span{font-family:var(--mono);font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--fg-mut)}

/* THE `hidden` ATTRIBUTE HAS TO WIN, and on this element it did not.
   index-filter.js filters by setting `hidden` on a card, and `display:flex`
   below is an AUTHOR rule — it beats the UA stylesheet's
   [hidden]{display:none} every time. So the topic chips updated the count
   ("7 articles in XDR") while all 102 cards stayed on screen: the filter was
   working perfectly and nothing moved.

   Measured before the fix: 95 cards carrying the hidden attribute, 0 of them
   computing to display:none.

   admin.css hit the identical bug with .edsource and carries the same rule;
   this is the public stylesheet finally getting it. Global rather than scoped
   to .ixitem, because any element that sets `display` and is later toggled
   with `hidden` has this bug waiting, and there is no case on this site where
   a hidden element should still render. */
[hidden]{display:none !important}

.postcard{display:flex;flex-direction:column;height:100%}
.postcard .cat{font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--red);margin-bottom:12px}
.postcard h3{font-size:20px;margin-bottom:10px}
.postcard p{color:var(--fg-mut);font-size:15px;flex:1}
/* --fg-mut: 12px text, so --fg-faint's 2.59 on paper was not readable. */
.postcard .pmeta{margin-top:16px;font-family:var(--mono);font-size:12px;color:var(--fg-mut);display:flex;gap:14px}
.listgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}

/* ---------- 12. UTILITIES ---------- */
.center{text-align:center}
.mt-s{margin-top:16px}.mt{margin-top:28px}.mt-l{margin-top:48px}
.mb-0{margin-bottom:0}
.rule-red{width:64px;height:4px;background:var(--red);border-radius:2px}
.pill-row{display:flex;flex-wrap:wrap;gap:10px}
.err{min-height:70vh;display:grid;place-items:center;text-align:center;padding:40px}
.err .big{font-family:var(--display);font-weight:800;font-size:clamp(90px,18vw,200px);line-height:.9;color:var(--red)}

/* ---------- 13. MOTION ---------- */
.reveal{opacity:0;transform:translateY(26px);transition:opacity .7s var(--ease),transform .7s var(--ease)}
.reveal.in{opacity:1;transform:none}
.reveal.d1{transition-delay:.08s}.reveal.d2{transition-delay:.16s}.reveal.d3{transition-delay:.24s}
.reveal.d4{transition-delay:.32s}.reveal.d5{transition-delay:.40s}.reveal.d6{transition-delay:.48s}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}
  .reveal{opacity:1;transform:none}
}

/* ---------- 14. RESPONSIVE ---------- */
@media (max-width:1080px){
  .hero-grid{grid-template-columns:1fr;gap:40px}
  .orbital{max-width:440px}
  .cards-4{grid-template-columns:repeat(2,1fr)}
  .foot .top{grid-template-columns:1fr 1fr 1fr;gap:28px}
  .foot .brand{grid-column:1/-1}
}
@media (max-width:900px){
  .mainnav,.nav-cta.desktop{display:none}
  .navtoggle{display:inline-flex}
  .cards-3{grid-template-columns:1fr 1fr}
  .statband{grid-template-columns:1fr 1fr}
  .statband .cell:nth-child(odd){border-left:0}
  .statband .cell:nth-child(n+3){border-top:1px solid var(--line)}
  .listgrid{grid-template-columns:1fr 1fr}
}
@media (max-width:620px){
  body{font-size:16px}
  .utility .social{display:none}
  .cards-3,.cards-2,.cards-4,.listgrid{grid-template-columns:1fr}
  .statband{grid-template-columns:1fr}
  .statband .cell{border-left:0;border-top:1px solid var(--line)}
  .statband .cell:first-child{border-top:0}
  .foot .top{grid-template-columns:1fr 1fr}
  .squad-head{flex-wrap:wrap}
  .hero .stats{gap:14px 24px}
  .btn{width:100%;justify-content:center}
  .hero .btnrow .btn{width:auto;flex:1 1 auto}
}

/* the 404 shares the dark treatment, so its type needs the dark palette */
.err.sec-dark .big{color:#fff;opacity:.14}
.err.sec-dark h2{color:#fff}
.err.sec-dark .lede{color:var(--dk-soft)}

/* ---------- cookie consent bar ----------
   Bottom-left rather than a full-width strip across the foot of the window: it
   has to be dismissable without being the first thing anybody deals with, and
   a banner that covers the CTA on a phone costs more than the analytics are
   worth. Rendered hidden and revealed by main.js, so a returning visitor who
   already answered never sees it flash. */
.cbar{position:fixed;left:16px;bottom:16px;z-index:200;width:min(400px,calc(100vw - 32px));
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:0 24px 60px -18px rgba(15,20,35,.28);padding:20px 22px}
.cbar-t{margin:0 0 16px;font-size:13.5px;line-height:1.55;color:var(--fg-soft)}
.cbar-b{display:flex;align-items:center;gap:10px}
.cbar-b .btn{padding:9px 18px;font-size:13.5px}
.cbar-l{margin-left:auto;font-size:12.5px;color:var(--fg-mut);text-decoration:underline}
.cbar-l:hover{color:var(--ink)}
@media (max-width:520px){.cbar{left:12px;right:12px;bottom:12px;width:auto;padding:18px}}

/* ---------- "last reviewed" line ----------
   Sits between the last section and the footer. Deliberately understated: it
   is a maintenance signal, not a claim, and anyone who is not looking for it
   should not notice it. */
/* Visually hidden, still announced. Used for table captions and the text
   equivalent of a tick, so the matrix reads as sentences to a screen reader
   rather than as a grid of unlabelled symbols. */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* ---------- index cards ----------
   The cards were a title, a line of grey and a date. These are the front door
   to every piece of writing on the site, so they get an image where one exists,
   a visible affordance that they are links, and a hover that moves. */
.postcard{position:relative;overflow:hidden;transition:transform .3s var(--ease),
  border-color .3s,box-shadow .3s}
.postcard:hover{transform:translateY(-5px);border-color:var(--line-2);box-shadow:var(--shadow)}
.postcard::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--seam-grad);
  transform:scaleX(0);transform-origin:0 50%;transition:transform .35s var(--ease)}
.postcard:hover::before{transform:scaleX(1)}
.pc-img{display:block;position:relative;margin:-28px -28px 20px;aspect-ratio:16/9;
  overflow:hidden;background:var(--bg-2)}
.pc-img img{display:block;width:100%;height:100%;object-fit:cover;
  transition:transform .4s var(--ease)}

/* GENERATED BANNER, for an article with no hero_image.
   Drawn rather than sourced: it needs no asset, cannot 404, and is the same
   every time for a given category. The alternative was a grey rectangle, which
   reads as a missing file rather than as a design — and on an index page the
   image is most of what makes a card worth clicking. A real hero_image always
   wins; these disappear one by one as images land. */
.pc-img.is-gen{background:linear-gradient(135deg,var(--bd-1),var(--bd-2))}
.pc-gen{position:absolute;inset:0;display:flex;flex-direction:column;
  justify-content:space-between;padding:18px 20px;
  /* The same lattice the page uses, so the card belongs to the site rather
     than looking like stock artwork. */
  background-image:linear-gradient(rgba(255,255,255,.045) 1px,transparent 1px),
    linear-gradient(90deg,rgba(255,255,255,.045) 1px,transparent 1px),
    radial-gradient(60% 80% at 85% 0,rgba(195,0,23,.34),transparent 62%);
  background-size:34px 34px,34px 34px,100% 100%}
.pc-genmark{font-family:var(--display);font-weight:800;font-size:12px;letter-spacing:-.01em;
  color:#fff;opacity:.92}
.pc-genmark em{font-style:normal;font-weight:500;color:var(--red-3)}
.pc-gentext{font-family:var(--display);font-weight:700;
  font-size:clamp(17px,2.2vw,21px);line-height:1.15;letter-spacing:-.02em;color:#fff;
  max-width:14ch;text-wrap:balance}
.postcard:hover .pc-gen{transform:scale(1.02)}
.pc-gen{transition:transform .4s var(--ease)}
.postcard:hover .pc-img img{transform:scale(1.04)}
/* The arrow is pushed to the end of the meta row so every card has a consistent
   "this is a link" marker regardless of how long its date and read time are. */
.pc-go{margin-left:auto;display:grid;place-items:center;width:26px;height:26px;
  border-radius:50%;background:var(--red-wash);color:var(--red);
  transition:background .25s,transform .25s var(--ease)}
.pc-go svg{width:14px;height:14px}
.postcard:hover .pc-go{background:var(--red);color:#fff;transform:translateX(2px)}
@media (prefers-reduced-motion:reduce){
  .postcard,.postcard::before,.pc-img img,.pc-go{transition:none}
  .postcard:hover{transform:none}
  .postcard:hover .pc-img img{transform:none}
}

/* Draft preview banner. Sticky so it cannot be scrolled away and mistaken for
   a live page.

   IT USED TO SIT ON TOP OF THE MASTHEAD, and that was the bug: the banner pins
   at 0-39 and the masthead at 10-77, so they overlapped by 29px, and with the
   banner at z-index 401 against the masthead's 200 it painted over the nav -
   including over the mega menu whenever one was open.

   Raising the nav above it would only swap which one is covered. They should
   not overlap at all, so the masthead is offset by the banner's height while a
   banner is present. 49px is the banner (39px) plus the masthead's own 10px
   top. Only preview pages pay for it, and only reviewers see it. */
body:has(.previewbar) .masthead{top:49px}
.previewbar{position:sticky;top:0;z-index:401;padding:9px 16px;text-align:center;
  font-family:var(--mono);font-size:12px;letter-spacing:.04em;
  color:#3a2500;background:#ffc94d;border-bottom:1px solid #d9a52e}
.previewbar strong{font-weight:700}

/* ---------- legal pages ----------
   A reading column with a sticky contents rail. Legal text is skimmed for the
   one clause somebody came for, so the rail matters more than it would on an
   article, and the measure is kept tight because these are long. */
.legal{display:grid;grid-template-columns:230px minmax(0,1fr);gap:clamp(28px,4vw,56px);align-items:start}
.legal-toc{position:sticky;top:120px;font-size:13px;line-height:1.45}
.legal-toc-k{display:block;font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;color:var(--fg-mut);margin-bottom:12px}
.legal-toc ol{list-style:none;margin:0;padding:0;counter-reset:lg}
.legal-toc li{counter-increment:lg;margin-bottom:8px;padding-left:22px;position:relative}
.legal-toc li::before{content:counter(lg);position:absolute;left:0;top:1px;font-family:var(--mono);
  font-size:10px;font-weight:700;color:var(--fg-mut)}
.legal-toc a{color:var(--fg-mut);text-decoration:none}
.legal-toc a:hover{color:var(--red-2)}

.legal-body{max-width:70ch}
.legal-intro{font-size:17px;line-height:1.65;padding-bottom:22px;border-bottom:1px solid var(--line)}
.legal-s{margin-top:clamp(30px,4vw,44px);scroll-margin-top:120px}
.legal-s h2{font-size:clamp(20px,2.3vw,25px);letter-spacing:-.02em;margin:0 0 14px}
.legal-s h3{font-size:16px;margin:24px 0 10px}
.legal-s p,.legal-s li{font-size:15.5px;line-height:1.7}
.legal-s ul{padding-left:20px;margin:12px 0}
.legal-s li{margin-bottom:7px}

.legal-dl{margin:14px 0}
.legal-dl dt{font-weight:700;font-size:15px;margin-top:16px}
.legal-dl dd{margin:4px 0 0;padding-left:0;font-size:15.5px;line-height:1.7;color:var(--fg-soft)}

.legal-table{width:100%;border-collapse:collapse;margin:16px 0;font-size:14.5px}
.legal-table th,.legal-table td{text-align:left;vertical-align:top;padding:11px 14px;
  border:1px solid var(--line)}
/* --bg, NOT --tint. --tint has never been declared anywhere - not in CSS,
   not in JS, not in a template - so this said `background:` and then named
   nothing, and the header row of every table on the privacy statement and the
   terms has been rendering with no fill at all. A var() with no fallback
   resolves to nothing and fails silently, which is why it survived.
   Found by scripts/check-tokens.js, 22 Sep 2026. */
.legal-table th{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--fg-mut);font-weight:700;background:var(--bg)}

@media (max-width:900px){
  .legal{grid-template-columns:1fr}
  .legal-toc{position:static;padding-bottom:22px;border-bottom:1px solid var(--line)}
  .legal-toc ol{columns:2;column-gap:24px}
}
