/* ============================================================================
   HOMEPAGE v2 — hero console, browser frame, RGB squads, micro-motion.
   Loaded after main.css. Dark-section aware.
   ============================================================================ */

/* hero right: live SOC console */
.console{position:relative;background:linear-gradient(180deg,var(--dk-panel),var(--dk));border:1px solid var(--dk-line-2);border-radius:16px;overflow:hidden;box-shadow:0 44px 100px -36px rgba(0,0,0,.85)}
.console .chead{display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1px solid var(--dk-line);background:rgba(255,255,255,.02)}
.console .chead .brand{font-family:var(--display);font-weight:700;font-size:13.5px;color:#fff;letter-spacing:-.01em}
.console .chead .brand b{color:var(--red-3);font-weight:700}
.console .chead .live{margin-left:auto;font-family:var(--mono);font-size:10px;letter-spacing:.12em;color:#6ff0a6;display:flex;align-items:center;gap:6px}
.console .chead .live::before{content:"";width:7px;height:7px;border-radius:50%;background:#22c55e;box-shadow:0 0 10px #22c55e;animation:pulse 1.6s infinite}
.console .cstats{display:grid;grid-template-columns:repeat(3,1fr);border-bottom:1px solid var(--dk-line)}
.console .cstats .s{padding:14px 16px;border-left:1px solid var(--dk-line)}
.console .cstats .s:first-child{border-left:0}
.console .cstats .v{font-family:var(--display);font-weight:800;font-size:21px;color:#fff;line-height:1}
.console .cstats .v .u{color:var(--red-3)}
.console .cstats .k{font-family:var(--mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--dk-mut);margin-top:6px}
.console .feed{padding:8px 8px 12px;position:relative}
.console .row{display:flex;align-items:center;gap:11px;padding:10px 12px;border-radius:9px;font-size:12.5px;opacity:0;transform:translateY(8px);animation:rowIn .55s var(--ease) forwards}
.console .row:nth-child(1){animation-delay:.15s}
.console .row:nth-child(2){animation-delay:.35s}
.console .row:nth-child(3){animation-delay:.55s}
.console .row:nth-child(4){animation-delay:.75s}
.console .row:nth-child(5){animation-delay:.95s}
.console .row .dot{width:8px;height:8px;border-radius:50%;flex:none}
.console .row .code{font-family:var(--mono);font-size:10px;font-weight:700;color:#fff;min-width:52px}
.console .row .txt{color:var(--dk-soft);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.console .row .st{font-family:var(--mono);font-size:9.5px;color:var(--dk-mut)}
.console .row.blue .dot{background:#2f6bff;box-shadow:0 0 8px #2f6bff}
.console .row.red .dot{background:#e11d2a;box-shadow:0 0 8px #e11d2a}
.console .row.green .dot{background:#12a150;box-shadow:0 0 8px #12a150}
.console .row .st.hold{color:var(--amber)}
.console .row .st.ok{color:#6ff0a6}
.console .scan{position:absolute;left:0;right:0;top:0;height:64px;background:linear-gradient(180deg,rgba(195,0,23,.14),transparent);pointer-events:none;animation:scanDown 4.5s cubic-bezier(.4,0,.6,1) infinite}
@keyframes rowIn{to{opacity:1;transform:none}}
@keyframes scanDown{0%{transform:translateY(-64px);opacity:0}12%{opacity:1}100%{transform:translateY(340px);opacity:0}}
@media (prefers-reduced-motion:reduce){.console .row{opacity:1;transform:none;animation:none}.console .scan{display:none}}

/* browser window frame for real screenshots (no stretch) */
.browser{position:relative;border-radius:14px;overflow:hidden;background:#fff;max-width:1060px;margin:0 auto;border:1px solid var(--dk-line-2);box-shadow:0 44px 100px -34px rgba(0,0,0,.8)}
.browser .top{display:flex;align-items:center;gap:8px;padding:11px 14px;background:#0d0e13;border-bottom:1px solid var(--dk-line)}
.browser .dots{display:flex;gap:6px}
.browser .dots i{width:11px;height:11px;border-radius:50%;background:#33363f;display:block}
.browser .dots i:first-child{background:#ff5f57}
.browser .dots i:nth-child(2){background:#febc2e}
.browser .dots i:nth-child(3){background:#28c840}
.browser .url{margin-left:12px;font-family:var(--mono);font-size:11px;color:var(--dk-mut);background:rgba(255,255,255,.05);border:1px solid var(--dk-line);border-radius:6px;padding:5px 14px}
.browser img{width:100%;height:auto;display:block}
.platstage{position:relative;max-width:1060px;margin:0 auto}
.platstage .callout.tr{top:12%;right:-3%}
.platstage .callout.bl{bottom:14%;left:-3%}

/* RGB squads — org-chart style */
.coreband{position:relative;display:grid;grid-template-columns:64px 1fr auto;gap:22px;align-items:center;border:1px solid var(--dk-line);border-radius:var(--radius-lg);background:linear-gradient(120deg,var(--dk-panel),#0e1119);padding:22px 28px;margin-bottom:26px;overflow:hidden}
.coreband::before{content:"";position:absolute;inset:0;background:radial-gradient(60% 130% at 5% 50%,rgba(195,0,23,.2),transparent 60%);pointer-events:none}
.coreband .em{position:relative;width:64px;height:64px;border-radius:16px;display:grid;place-items:center;background:rgba(195,0,23,.16);border:1px solid rgba(195,0,23,.4);color:var(--red-3)}
.coreband .em svg{width:32px;height:32px;animation:spinSlow 20s linear infinite}
.coreband .m{position:relative}
.coreband .code{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.12em;color:var(--red-3);border:1px solid rgba(195,0,23,.4);border-radius:5px;padding:3px 8px}
.coreband h3{color:#fff;margin:10px 0 6px}
.coreband p{color:var(--dk-mut);font-size:14px;margin:0;max-width:64ch}
.coreband .badge2{position:relative;font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--dk-soft);white-space:nowrap;border:1px solid var(--dk-line-2);border-radius:100px;padding:8px 14px}
@keyframes spinSlow{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.coreband .em svg{animation:none}}

.squadcols{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.squadcol{--c:var(--blue);position:relative;border:1px solid var(--dk-line);border-radius:var(--radius-lg);background:var(--dk-panel);padding:6px 8px 10px;overflow:hidden;transition:border-color .3s,box-shadow .3s,transform .3s}
.squadcol:hover{border-color:color-mix(in srgb,var(--c) 45%,var(--dk-line-2));box-shadow:0 30px 70px -34px color-mix(in srgb,var(--c) 60%,transparent);transform:translateY(-4px)}
.squadcol .sqtop{height:3px;border-radius:3px;background:linear-gradient(90deg,var(--c),transparent 85%);margin:2px 4px 0}
.squadcol .sqhead{display:flex;align-items:center;gap:10px;padding:16px 14px 10px}
.squadcol .sqhead .sq{width:10px;height:10px;border-radius:50%;background:var(--c);box-shadow:0 0 12px var(--c);flex:none}
.squadcol .sqhead .lab{font-family:var(--display);font-weight:800;font-size:15px;color:#fff}
.squadcol .sqhead .sub{font-family:var(--mono);font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:var(--dk-mut);margin-left:auto;text-align:right}
.arow{display:flex;gap:12px;align-items:flex-start;padding:13px 12px;border-radius:12px;transition:background .22s,transform .22s}
.arow:hover{background:color-mix(in srgb,var(--c) 9%,transparent);transform:translateX(3px)}
.arow .em{flex:none;width:40px;height:40px;border-radius:11px;display:grid;place-items:center;background:color-mix(in srgb,var(--c) 13%,transparent);color:var(--c);border:1px solid color-mix(in srgb,var(--c) 28%,transparent);transition:transform .3s var(--ease)}
.arow:hover .em{transform:scale(1.09) rotate(-4deg)}
.arow .em svg{width:20px;height:20px}
.arow h4{font-size:14.5px;color:#fff;margin-bottom:4px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.arow h4 .code{font-family:var(--mono);font-size:9px;font-weight:700;color:var(--c);border:1px solid color-mix(in srgb,var(--c) 32%,transparent);border-radius:5px;padding:2px 6px}
.arow p{font-size:12.5px;color:var(--dk-mut);margin:0;line-height:1.45}

/* micro-motion */
.eyebrow::before{transition:width .35s var(--ease)}
.sec-head:hover .eyebrow::before{width:46px}

/* squad header online status */
.squadcol .sqhead{align-items:flex-start}
.squadcol .sqhead .htext{display:flex;flex-direction:column;gap:2px}
.squadcol .sqhead .sub{margin-left:0;text-align:left}
.squadcol .sqhead .status{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--dk-soft);border:1px solid var(--dk-line);border-radius:100px;padding:5px 9px;white-space:nowrap}
.squadcol .sqhead .status .d{width:7px;height:7px;border-radius:50%;background:var(--c);box-shadow:0 0 8px var(--c);animation:pulse 1.9s ease-in-out infinite}

/* agent accordion rows (descriptive) — clean separated cards */
.squadcol{padding:8px 10px 12px}
.squadcol .mission{padding:0 6px 12px;margin:0;color:var(--dk-mut);font-size:12.5px;line-height:1.5;border-bottom:1px solid var(--dk-line);margin-bottom:10px}
.arow{display:block;border:1px solid var(--dk-line);border-radius:12px;background:rgba(255,255,255,.02);margin:0 0 10px;transition:border-color .25s,background .25s,box-shadow .25s;transform:none}
.arow:last-child{margin-bottom:2px}
.arow:hover{border-color:color-mix(in srgb,var(--c) 38%,var(--dk-line-2))}
.arow.open{border-color:color-mix(in srgb,var(--c) 55%,transparent);background:color-mix(in srgb,var(--c) 9%,transparent);box-shadow:0 14px 32px -20px color-mix(in srgb,var(--c) 75%,transparent)}
.arow-head{display:flex;gap:12px;align-items:flex-start;width:100%;background:none;border:0;text-align:left;padding:14px;color:inherit;font:inherit;cursor:pointer}
.arow-head .em{flex:none;width:40px;height:40px;border-radius:11px;display:grid;place-items:center;background:color-mix(in srgb,var(--c) 13%,transparent);color:var(--c);border:1px solid color-mix(in srgb,var(--c) 28%,transparent);transition:transform .3s var(--ease)}
.arow-head:hover .em,.arow.open .em{transform:scale(1.08) rotate(-4deg)}
.arow-head .em svg{width:20px;height:20px}
.arow-head .lab{flex:1;min-width:0}
.arow-head h4{font-size:14.5px;color:#fff;margin:2px 0 4px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
/* MIXED TOWARD WHITE, NOT BLACK, and the first attempt at this got it exactly
   backwards. This looks like .ag-code on the agents page and takes the
   opposite treatment, because it sits on the opposite ground: .ag-code is on a
   white card, this is inside .squadcol, which is --dk-panel. Darkening it took
   the blue agents from 3.9 to 1.2.

   65% colour / 35% white clears 4.5 on #151925 for every agent hue - the
   worst of them is --red at 2.78 neat and 4.65 mixed - while keeping each
   agent recognisably its own colour. Which is the same thing --red-3 does for
   --red by hand, arrived at per agent instead of per token.

   The border keeps --c neat. A border is not text. */
.arow-head h4 .code{font-family:var(--mono);font-size:9px;font-weight:700;color:color-mix(in srgb,var(--c) 65%,#fff);border:1px solid color-mix(in srgb,var(--c) 32%,transparent);border-radius:5px;padding:2px 6px}
.arow-head p{font-size:12.5px;color:var(--dk-mut);margin:0;line-height:1.45}
.arow-head .chev{flex:none;width:16px;height:16px;color:var(--dk-mut);transition:transform .3s var(--ease);margin-top:6px}
.arow.open .arow-head .chev{transform:rotate(180deg);color:var(--c)}
.arow-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .34s var(--ease)}
.arow.open .arow-body{grid-template-rows:1fr}
.arow-body>div{overflow:hidden}
.arow-skills{padding:2px 14px 16px 66px;display:grid;gap:8px}
.arow-skills li{position:relative;padding-left:16px;font-size:12.5px;color:var(--dk-soft);line-height:1.5}
.arow-skills li::before{content:"";position:absolute;left:0;top:8px;width:6px;height:6px;border-radius:50%;background:var(--c);box-shadow:0 0 6px var(--c)}

/* Microsoft / integrations clarity strip */
.reqline{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:10px 14px;margin-bottom:26px}
.reqline .req{display:inline-flex;align-items:center;gap:8px;font-family:var(--display);font-weight:600;font-size:14px;color:var(--ink);background:var(--paper);border:1px solid var(--line);border-radius:100px;padding:9px 16px;box-shadow:var(--shadow-sm)}
.reqline .req .tick{width:18px;height:18px;border-radius:50%;background:var(--red);color:#fff;display:grid;place-items:center;flex:none}
.reqline .req .tick svg{width:12px;height:12px}
.reqline .plus{color:var(--fg-mut);font-size:13px}

/* With / Without CYBERSHIELD AI comparison */
.compare{max-width:1040px;margin:0 auto;text-align:center}
.cmp-toggle{position:relative;display:inline-flex;background:var(--bg-2);border:1px solid var(--line);border-radius:100px;padding:5px;margin-bottom:36px}
.cmp-toggle button{position:relative;z-index:1;border:0;background:none;font-family:var(--display);font-weight:600;font-size:14.5px;padding:11px 24px;border-radius:100px;color:var(--fg-mut);cursor:pointer;transition:color .25s;white-space:nowrap}
.cmp-toggle button.on{color:#fff}
.cmp-toggle .pill{position:absolute;top:5px;bottom:5px;left:5px;width:0;border-radius:100px;background:var(--green);z-index:0;transition:left .32s var(--ease),width .32s var(--ease),background .32s}
.compare.without .cmp-toggle .pill{background:var(--red)}
.cmp-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:18px}
.cmp-card{position:relative;border:1px solid var(--line);border-radius:var(--radius);background:var(--paper);padding:26px 22px;box-shadow:var(--shadow-sm);text-align:left;overflow:hidden}
.cmp-card::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:var(--green);transition:background .3s;opacity:.9}
.compare.without .cmp-card::before{background:var(--red)}
.cmp-card .k{color:var(--fg-mut);font-size:13px;font-family:var(--mono);letter-spacing:.06em;text-transform:uppercase}
.cmp-card .lab{font-weight:700;color:var(--ink);font-size:18px;margin:4px 0 20px;font-family:var(--display)}
.cmp-card .v{font-family:var(--display);font-weight:800;font-size:clamp(28px,3.2vw,40px);letter-spacing:-.03em;line-height:1;color:var(--green);transition:color .3s}
.compare.without .cmp-card .v{color:var(--red)}
.cmp-card .sub{font-size:12.5px;color:var(--fg-mut);margin-top:12px;min-height:2.6em}
@media (max-width:820px){.cmp-grid{grid-template-columns:1fr 1fr}}
@media (max-width:460px){.cmp-grid{grid-template-columns:1fr}}

@media (max-width:960px){
  .squadcols{grid-template-columns:1fr}
  .coreband{grid-template-columns:56px 1fr;gap:16px}
  .coreband .badge2{display:none}
}

/* ============================================================================
   Background depth - subtle triangular mesh + slow drifting aurora + parallax
   ============================================================================ */
.hero.sec-dark .sub{color:#ccd1dc}
.hero.sec-dark .sub strong{color:#fff;font-weight:600}

/* The mesh itself is defined once, in main.css section 1b/3. A hero carries it
   a touch heavier than a mid-page band, and above the aurora rather than under
   it — that is the whole of the difference. */
.hero.sec-dark::before{opacity:var(--bd-mesh-opacity-hero);z-index:1}
/* ::after still carries the travelling pulse, so it must stay visible; the
   :has(.aura) rule in main.css is what stops the lights being drawn twice. */

/* soft, slowly-morphing aurora behind the hero mesh */
.sec-dark .aura{position:absolute;inset:-12%;z-index:0;pointer-events:none;overflow:hidden;transition:transform .5s ease}
.sec-dark .aura span{position:absolute;border-radius:50%;filter:blur(74px);will-change:transform}
.sec-dark .aura .a1{width:44vw;height:44vw;left:46%;top:0;background:radial-gradient(circle,var(--bd-red),transparent 68%);animation:auraA 26s ease-in-out infinite alternate}
.sec-dark .aura .a2{width:36vw;height:36vw;left:70%;top:36%;background:radial-gradient(circle,var(--bd-blue-aura),transparent 70%);animation:auraB 32s ease-in-out infinite alternate}
.sec-dark .aura .a3{width:28vw;height:28vw;left:38%;top:56%;background:radial-gradient(circle,var(--bd-red-2),transparent 70%);animation:auraC 29s ease-in-out infinite alternate}
/* Fourth light, in the engineering green, held to the LEFT so it sits behind
   the headline column. Added on the pseudo-element rather than a fourth span
   so no page template has to change. */
.sec-dark .aura::before{content:"";position:absolute;left:-10%;top:16%;width:40vw;height:40vw;
  border-radius:50%;filter:blur(84px);will-change:transform;
  background:radial-gradient(circle,var(--bd-green-aura),transparent 68%);
  animation:auraG 24s ease-in-out infinite alternate}
@keyframes auraA{from{transform:translate3d(0,0,0) scale(1)}to{transform:translate3d(-5%,7%,0) scale(1.14)}}
@keyframes auraB{from{transform:translate3d(0,0,0) scale(1)}to{transform:translate3d(7%,-5%,0) scale(1.1)}}
@keyframes auraC{from{transform:translate3d(0,0,0) scale(1)}to{transform:translate3d(4%,-7%,0) scale(1.18)}}
@keyframes auraG{from{transform:translate3d(0,0,0) scale(1)}to{transform:translate3d(6%,5%,0) scale(1.12)}}

/* console reacts subtly to the cursor (JS-driven) */
.hero .console{transition:transform .35s ease}

/* SPECIFICITY: these must name .a1/.a2/.a3, not `span`.
   `.sec-dark .aura span` is (0,2,1) and `.sec-dark .aura .a1` above is
   (0,3,0) — three classes beat two classes and a type, so the span form was
   silently outranked and neither of the rules below did anything. The
   reduced-motion one had been dead since it was written, which means anyone
   who asks their phone or laptop to stop animating things was still getting
   four drifting 74px blurs. */
@media (prefers-reduced-motion:reduce){
  .sec-dark .aura .a1,.sec-dark .aura .a2,.sec-dark .aura .a3,.sec-dark .aura::before{animation:none}
}
/* SAME RULE FOR TOUCH DEVICES, and it is a performance fix, not a taste one.
   Reported after launch: on a phone the menu took one to two seconds to open
   while the hero was on screen, and opened instantly once you had scrolled
   past it. That is the tell — whatever was costing the time stopped costing
   it when it left the viewport.

   These four lights are 44vw, 36vw, 28vw and 40vw wide under blur(74px) and
   blur(84px), and the keyframes animate transform with a SCALE:

     from{translate3d(0,0,0) scale(1)} to{translate3d(-5%,7%,0) scale(1.14)}

   Translation alone is free — the compositor moves an already-blurred texture
   around. Scaling is not: the rasterised size changes every frame, so the
   blur has to be recomputed every frame, four times over, for as long as the
   section is visible. The home page renders two of these groups, eight lights
   in all. It saturates a phone GPU, and the drawer's first paint queues
   behind it. Closing was always instant because that layer was already drawn.

   Held still they cost nothing: rasterised once, then composited. The look is
   unchanged — a 26-second drift is imperceptible on a phone anyway — and
   desktop keeps the motion. */
@media (pointer:coarse){
  /* will-change goes with the animation. It promotes each light to its own
     compositor layer PERMANENTLY, and each of those layers holds a 44vw-wide
     texture rasterised through blur(74px). Two groups render per page, so
     that is eight large blurred textures resident in GPU memory for
     transforms that, here, never happen.

     will-change on an element that is not animating is a documented
     anti-pattern for exactly this reason: it is a promise of future motion,
     and the browser pays for it up front. Turning the animation off without
     this left the whole cost in place, which is why doing only that changed
     nothing. */
  .sec-dark .aura .a1,.sec-dark .aura .a2,.sec-dark .aura .a3,.sec-dark .aura::before{
    animation:none;will-change:auto}
}

/* ============================================================================
   CYBERSHIELD AI platform page — roster by function + screenshot gallery
   ============================================================================ */
.progroup{margin-bottom:clamp(36px,5vw,52px)}
.pg-head{display:flex;align-items:baseline;gap:8px 20px;border-top:2px solid var(--ink);padding-top:16px;margin-bottom:22px;flex-wrap:wrap}
.pg-head h3{font-size:clamp(21px,2.5vw,28px);letter-spacing:-.02em}
.pg-head .count{font-family:var(--mono);font-size:12px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--red)}
.pg-head p{color:var(--fg-mut);font-size:14.5px;margin:0;flex:1;min-width:280px;line-height:1.5}
.pgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:20px}
.pcard{--c:var(--red);position:relative;border:1px solid var(--line);border-left:3px solid var(--c);border-radius:var(--radius);background:var(--paper);padding:24px;box-shadow:var(--shadow-sm);transition:transform .25s var(--ease),box-shadow .25s}
.pcard:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.pcard .top{display:flex;align-items:center;gap:12px;margin-bottom:14px}
.pcard .em{width:44px;height:44px;border-radius:12px;display:grid;place-items:center;background:color-mix(in srgb,var(--c) 12%,transparent);border:1px solid color-mix(in srgb,var(--c) 26%,transparent);color:var(--c);flex:none}
.pcard .em svg{width:22px;height:22px}
.pcard .code{margin-left:auto;font-family:var(--mono);font-size:10px;font-weight:700;color:var(--c);border:1px solid color-mix(in srgb,var(--c) 32%,transparent);border-radius:5px;padding:3px 7px}
.pcard h4{font-size:17px;margin-bottom:5px;letter-spacing:-.01em}
.pcard .role{color:var(--fg-mut);font-size:13.5px;margin:0 0 14px;line-height:1.5}
.pcard .skills{display:grid;gap:8px;border-top:1px solid var(--line);padding-top:14px}
.pcard .skills li{position:relative;padding-left:18px;font-size:13px;color:var(--fg-soft);line-height:1.45}
.pcard .skills li::before{content:"";position:absolute;left:0;top:7px;width:6px;height:6px;border-radius:50%;background:var(--c)}

/* screenshot gallery (dark) */
.gallery .tabs{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin-bottom:26px}
.gallery .tabs button{font-family:var(--display);font-weight:600;font-size:13.5px;padding:10px 18px;border-radius:100px;border:1px solid var(--dk-line-2);background:rgba(255,255,255,.04);color:var(--dk-soft);cursor:pointer;transition:.2s}
.gallery .tabs button:hover{color:#fff;border-color:var(--dk-mut)}
.gallery .tabs button.on{background:var(--red);color:#fff;border-color:var(--red);box-shadow:0 8px 22px -8px var(--red-glow)}
.gallery .stage{position:relative;max-width:1080px;margin:0 auto}
.gallery .pane{display:none}
.gallery .pane.on{display:block;animation:galFade .45s var(--ease)}
.gallery .cap{text-align:center;color:var(--dk-mut);font-size:14px;margin-top:16px;max-width:70ch;margin-inline:auto}
@keyframes galFade{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

/* built-from: agents / skills / tools */
.triptych{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
.triptych .card h3{display:flex;align-items:center;gap:10px}
.triptych .num{font-family:var(--mono);font-size:12px;color:var(--red);border:1px solid color-mix(in srgb,var(--red) 30%,transparent);border-radius:6px;padding:3px 8px}
@media (max-width:820px){.triptych{grid-template-columns:1fr}}

/* ============================================================================
   Managed AI SOC page — comparison rows + pricing tiers
   ============================================================================ */
/* traditional vs SOC AI two-column table */
/* Horizontal scroll ONLY, and only when the table cannot fit.
   `overflow-x:auto` on its own is not enough: CSS computes the other axis to
   `auto` as soon as one axis is non-visible, and an 11px rounding overflow was
   then enough to put a full-height vertical scrollbar down the side of the
   comparison table. Pinning overflow-y and giving the table somewhere to go
   removes it. */
.tablewrap{border-radius:var(--radius-lg)}
/* Scroll ONLY where the table cannot fit. `overflow-x:auto` on its own is not
   enough: CSS computes the other axis to `auto` as soon as one is non-visible,
   and an 11px rounding overflow was then enough to put a full-height vertical
   scrollbar down the side of the table. Confining the scroll container to the
   widths that need it removes both the stray bar and the clipping. */
@media (max-width:860px){
  .tablewrap{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}
}

/* baseline grid */
.baseline{display:grid;grid-template-columns:repeat(4,1fr);gap:18px}
@media (max-width:1000px){.baseline{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){.baseline{grid-template-columns:1fr}}

/* pricing tiers */
.pricing{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;align-items:stretch}
.tier{position:relative;border:1px solid var(--line);border-radius:var(--radius-lg);background:var(--paper);padding:32px 28px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column}
.tier.is-featured{border:1.5px solid var(--red);box-shadow:0 34px 80px -34px var(--red-glow)}
/* THE RECOMMENDED-PACKAGE BADGE IS .pkg-flag, SHARED - see the rule itself
   further down this file. This page used to carry its own `.tier .rec`, and
   that class was the bug behind Adam's "Most popular seems a bit cut off" on
   22 Sep.

   `.rec` is two unrelated components. Here it was the badge; everywhere else
   it is a definition row, with `.rec:first-child{padding-top:0}` a thousand
   lines further down the file. Both selectors are (0,2,0), so source order
   decides and the definition row won - and the badge, being the first child
   of its card, was served padding-top:0 by a component it has nothing to do
   with. Computed padding was 0px 18px 7.5px: no space above the text at all,
   which is what a cut-off pill looks like.

   Renaming it to .pkg-flag fixes that and answers the alignment question in
   the same move - one badge on the site instead of two that had drifted in
   wording, padding, size and position. The centred placement this page wants
   is a modifier on it. */
.tier .pkg-flag{left:50%;right:auto;transform:translateX(-50%);--flag-ring:var(--paper)}
.tier .pk{font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--fg-mut);margin-bottom:8px}
.tier .tname{font-family:var(--display);font-weight:800;font-size:26px;margin-bottom:8px;color:var(--ink)}
.tier .tdesc{color:var(--fg-mut);font-size:14px;line-height:1.5;margin-bottom:14px;min-height:4.4em}
/* THE METERING LINE, 22 Sep. Same content and same shape as .pkg-unit on the
   pricing page - the unit in mono over one line of reason - rendered light
   rather than dark because this card is paper. It sits above the feature list
   and below the audience line, so the reader has the package, who it is for
   and how it is charged before a single bullet.

   Red for per-incident and blue for per-user, which is the pairing the pricing
   page uses in three places. A buyer who has seen one page recognises it on
   the other, which is the whole reason the unit lives on the tier in
   packages.js rather than being written twice. */
.tier .tunit{display:flex;flex-direction:column;gap:3px;
  margin:0 0 18px;padding-top:14px;border-top:1px solid var(--line)}
.tier .tu-k{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--red)}
.tier:nth-child(2) .tu-k,
.tier:nth-child(3) .tu-k{color:var(--blue)}
.tier .tu-w{color:var(--fg-mut);font-size:13px;line-height:1.5}
/* NO RULE ON THE LIST, from 22 Sep. The metering line added above it has
   one, and two hairlines 60px apart turn a card into a form. The pricing
   page's card has exactly one for the same reason. */
.tier ul{display:grid;gap:11px;margin:0 0 26px;padding:4px 0 0;flex:1}
.tier li{position:relative;padding-left:26px;font-size:13.5px;color:var(--fg-soft);line-height:1.45}
.tier li::before{content:"";position:absolute;left:0;top:6px;width:14px;height:14px;border-radius:50%;background:var(--red-wash);box-shadow:inset 0 0 0 1.5px var(--red)}
/* excluded items are de-emphasised but must stay legible: --fg-faint was 2.59:1 */
.tier li.no{color:var(--fg-mut)}
.tier li.no::before{background:transparent;box-shadow:inset 0 0 0 1.5px var(--line-2)}
.tier .btn{margin-top:auto;width:100%;justify-content:center}
@media (max-width:900px){.pricing{grid-template-columns:1fr;max-width:460px;margin:0 auto}.tier.is-featured{order:-1}}


/* ---------- "reaches a verdict" split (home) ----------
   These sections had no CSS at all; the icons rendered at container width. */
.vsplit{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(30px,4.5vw,56px);align-items:start;margin-top:clamp(28px,4vw,44px)}
.points{display:grid;gap:clamp(20px,2.6vw,30px)}
.vpoint{display:flex;gap:16px;align-items:flex-start}
.vpoint .ic{flex:none;width:46px;height:46px;border-radius:12px;display:grid;place-items:center;background:var(--red-wash);border:1px solid rgba(195,0,23,.16);color:var(--red)}
.vpoint h4{font-size:18px;margin-bottom:7px;letter-spacing:-.01em}
.vpoint p{margin:0;color:var(--fg-mut);font-size:14.5px;line-height:1.6}

.verdictcard{border:1px solid var(--line);border-radius:var(--radius-lg);background:var(--paper);box-shadow:var(--shadow);padding:clamp(20px,2.4vw,26px)}
.vc-head{display:flex;align-items:center;justify-content:space-between;gap:14px;padding-bottom:16px;border-bottom:1px solid var(--line)}
.vc-k{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--fg-mut)}
.vc-mal{display:inline-flex;align-items:center;gap:8px;font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.1em;color:var(--red);background:var(--red-wash);border:1px solid rgba(195,0,23,.2);border-radius:100px;padding:5px 12px;white-space:nowrap}
.vc-mal .d{width:7px;height:7px;border-radius:50%;background:var(--red);box-shadow:0 0 10px var(--red);animation:pulse 1.6s infinite}
.vc-rows>div{display:flex;align-items:baseline;justify-content:space-between;gap:18px;padding:13px 0;border-bottom:1px solid var(--line)}
.vc-rows>div:last-child{border-bottom:0}
.vc-rows span{color:var(--fg-mut);font-size:13.5px}
.vc-rows b{font-family:var(--display);font-weight:600;font-size:14px;color:var(--ink);text-align:right}
.vc-conf{padding:14px 0 2px;border-top:1px solid var(--line)}
.vc-conf-top{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:10px}
.vc-conf-top span{color:var(--fg-mut);font-size:13.5px}
.vc-conf-top b{font-family:var(--display);font-weight:800;font-size:19px;color:var(--ink);letter-spacing:-.02em}
.vc-bar{height:6px;border-radius:4px;background:var(--bg-2);overflow:hidden}
.vc-bar i{display:block;height:100%;width:93%;border-radius:4px;background:linear-gradient(90deg,var(--red),var(--red-2))}
.vc-action{display:flex;align-items:center;justify-content:space-between;gap:14px;margin-top:18px;padding-top:16px;border-top:1px solid var(--line);flex-wrap:wrap}
.vc-action span{color:var(--fg-mut);font-size:13px}
.vc-action b{display:inline-flex;align-items:center;gap:8px;font-family:var(--display);font-weight:600;font-size:14.5px;color:var(--red)}
.vc-action b svg{width:16px;height:16px;flex:none}

/* ---------- full-spectrum services grid (home) ---------- */
.svc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(272px,1fr));gap:18px}
.svc{position:relative;display:block;padding:26px 24px;border:1px solid var(--line);border-radius:var(--radius);background:var(--paper);box-shadow:var(--shadow-sm);color:inherit;text-decoration:none;transition:transform .25s var(--ease),box-shadow .25s,border-color .25s}
.svc:hover{transform:translateY(-4px);box-shadow:var(--shadow);border-color:var(--line-2)}
.svc .ic{width:44px;height:44px;border-radius:12px;display:grid;place-items:center;background:var(--red-wash);border:1px solid rgba(195,0,23,.16);color:var(--red);margin-bottom:16px}
.svc h4{font-size:17px;margin-bottom:8px;letter-spacing:-.01em}
.svc p{margin:0;color:var(--fg-mut);font-size:14px;line-height:1.55}
.soon{position:absolute;top:18px;right:18px;font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--fg-mut);background:var(--bg-2);border:1px solid var(--line);border-radius:100px;padding:4px 10px}

@media (max-width:900px){.vsplit{grid-template-columns:1fr}}

/* ---------- agent cards on dark ---------- */
.sec-dark .pg-head{border-top-color:var(--dk-line-2)}
.sec-dark .pg-head h3{color:#fff}
.sec-dark .pg-head p{color:var(--dk-mut)}
.sec-dark .pg-head .count{color:var(--red-3)}
.sec-dark .pcard{background:var(--dk-panel);border-color:var(--dk-line);box-shadow:none}
.sec-dark .pcard:hover{border-color:var(--dk-line-2);box-shadow:0 26px 60px -30px rgba(0,0,0,.9)}
.sec-dark .pcard h4{color:#fff}
.sec-dark .pcard .role{color:var(--dk-mut)}
.sec-dark .pcard .skills{border-top-color:var(--dk-line)}
.sec-dark .pcard .skills li{color:var(--dk-soft)}
.sec-dark .pcard .em{background:color-mix(in srgb,var(--c) 20%,transparent);border-color:color-mix(in srgb,var(--c) 36%,transparent)}

/* ---------- baseline grid: distinct icons, tighter cards ---------- */
.baseline .card{padding:26px 24px}
/* --fg-mut, 22 Sep. I argued the other way when the baseline rails were done
   - that the ordinal in a card's corner is exactly what --fg-faint is for and
   barely-there is the point - and Adam's answer was to fix it. He is right
   that it is still a number somebody reads: eight cards, and the number is
   how you refer to one. 2.46 was not quiet, it was unreadable. */
.baseline .card .n{position:absolute;top:20px;right:22px;font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.1em;color:var(--fg-mut)}
.baseline .card .ico{margin-bottom:16px;transition:background .3s var(--ease),color .3s,transform .35s var(--ease),border-color .3s}
.baseline .card h4{font-size:16.5px;letter-spacing:-.01em;margin-bottom:9px;padding-right:30px}
.baseline .card p{font-size:13.5px;color:var(--fg-mut);line-height:1.58;margin:0}
/* RED, BLUE, GREEN - 22 Sep, Adam: "these boxes have a red line, no RGB
   line". Eight identical red rails, where every other repeated set on the site
   cycles the three: the stat row, the step numbers, the capability cards, the
   package cards, the masthead dot. This was the last family still doing it in
   one colour.

   The cycle runs on a 3, not on the 8 cards, so the wrap is 01-02-03 /
   04-05-06 / 07-08. That is deliberate: locking it to the count would mean an
   Nth card changing every other card's colour, and the sequence is meant to be
   a rhythm rather than a code.

   The gradient shape is unchanged - solid at the left, gone by 85% - so the
   rail still wipes in from the left on hover and still fades out rather than
   stopping dead. Only the hue moves. */
/* THE TOP RAIL THAT USED TO BE HERE IS GONE, 24 Sep 2026. It was a red, blue
   or green bar across the top of each card, cycling on 3. The shared left rail
   below replaces it — see the card-rail block further down this file — so the
   baseline cards now carry the same line as the service grid, the generic
   cards and the pricing grid, which is what Adam asked for.

   THE ICON CYCLE STAYS. The rail is the brand mark and is the same everywhere;
   the icon is where a set says "these belong together and here is their
   order". Those are different jobs and the card has room for both. */
.baseline .card:nth-child(3n+2) .ico{background:color-mix(in srgb,var(--blue) 9%,transparent);color:var(--blue)}
.baseline .card:nth-child(3n) .ico{background:color-mix(in srgb,var(--green) 9%,transparent);color:var(--green)}
/* THE HOVER FILL WAS A BUG. Every card's icon turned RED on hover regardless
   of the colour it rested at, so a blue card flashed red under the cursor —
   the exact "blue line above a red icon" the note above this block was written
   to prevent. .deliv had it right and this did not. Fixed 24 Sep 2026. */
.baseline .card:hover .n{color:var(--red)}
.baseline .card:nth-child(3n+2):hover .n{color:var(--blue)}
.baseline .card:nth-child(3n):hover .n{color:var(--green)}

/* ---------- FAQ (visible, and mirrored by FAQPage schema) ---------- */
.faq{max-width:860px;margin:0 auto;display:grid;gap:12px}
.faq-item{border:1px solid var(--line);border-radius:var(--radius);background:var(--paper);box-shadow:var(--shadow-sm);overflow:hidden;transition:border-color .25s,box-shadow .25s}
.faq-item[open]{border-color:var(--line-2);box-shadow:var(--shadow)}
.faq-item summary{display:flex;align-items:center;gap:18px;padding:20px 24px;cursor:pointer;list-style:none;font-family:var(--display);font-weight:600;font-size:16.5px;color:var(--ink);letter-spacing:-.01em}
.faq-item summary::-webkit-details-marker{display:none}
.faq-item summary:hover{color:var(--red)}
.faq-item summary .q{flex:1}
.faq-item summary svg{width:18px;height:18px;flex:none;color:var(--red);transition:transform .28s var(--ease)}
.faq-item[open] summary svg{transform:rotate(180deg)}
.faq-a{padding:0 24px 22px;margin-top:-4px}
.faq-a p{margin:0;color:var(--fg-soft);font-size:15px;line-height:1.65;max-width:72ch}
.faq-item summary:focus-visible{outline:2px solid var(--red);outline-offset:-2px}
@media (max-width:560px){
  .faq-item summary{padding:17px 18px;font-size:15.5px;gap:12px}
  .faq-a{padding:0 18px 18px}
}

/* ---------- services grid ----------
   Was eight identical white boxes of unequal height. Now: a fixed two-row grid
   with the icon inline against the title, a red rail that fills on hover, and
   the whole card lifting as one object. */
/* The service grid moved to a dark section on the home page, so it needs the
   same treatment .pcard already has. Panel background, white headings, muted
   body - the red accent bar and icon carry over unchanged. */
.sec-dark .svc{background:var(--dk-panel);border-color:var(--dk-line);box-shadow:none}
.sec-dark .svc:hover,.sec-dark .svc:focus-visible{border-color:var(--dk-line-2);
  box-shadow:0 26px 60px -30px rgba(0,0,0,.9)}
.sec-dark .svc h4{color:#fff}
.sec-dark .svc p{color:var(--dk-mut)}
.sec-dark .svc .ic{background:color-mix(in srgb,var(--red) 18%,transparent);
  border-color:color-mix(in srgb,var(--red) 34%,transparent);color:var(--red-3)}

.svc-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
.svc{position:relative;display:flex;flex-direction:column;gap:10px;overflow:hidden;
  padding:26px 24px 24px;min-height:212px;border-radius:16px;background:var(--paper);
  border:1px solid var(--line);box-shadow:var(--shadow-sm);
  transition:transform .28s var(--ease),box-shadow .28s,border-color .28s}
.svc::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--seam-grad-v);
  transform:scaleY(0);transform-origin:top;transition:transform .38s var(--ease)}
.svc:hover,.svc:focus-visible{transform:translateY(-5px);box-shadow:var(--shadow);border-color:var(--line-2)}
.svc:hover::before,.svc:focus-visible::before{transform:scaleY(1)}
.svc .ic{width:38px;height:38px;border-radius:10px;display:grid;place-items:center;
  background:var(--red-wash);border:1px solid rgba(195,0,23,.16);color:var(--red);
  transition:background .3s var(--ease),color .3s,border-color .3s,transform .35s var(--ease)}
.svc:hover .ic{background:var(--red);border-color:var(--red);color:#fff;transform:rotate(-6deg)}
.svc h4{font-size:16px;letter-spacing:-.012em;line-height:1.3;margin:0}
.svc p{margin:0;color:var(--fg-soft);font-size:13.5px;line-height:1.55;flex:1}
.svc .go{display:inline-flex;align-items:center;gap:7px;margin-top:2px;
  font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--red);opacity:0;transform:translateY(4px);transition:opacity .28s var(--ease),transform .28s var(--ease)}
.svc .go svg{width:14px;height:14px}
.svc:hover .go,.svc:focus-visible .go{opacity:1;transform:translateY(0)}
.svc:focus-visible{outline:2px solid var(--red);outline-offset:3px}
@media (max-width:1100px){.svc-grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){.svc-grid{grid-template-columns:1fr}.svc{min-height:0}}
@media (prefers-reduced-motion:reduce){
  .svc,.svc .ic,.svc .go,.svc::before{transition:none}
  .svc:hover{transform:none}.svc:hover .ic{transform:none}
}

/* ================= MANAGED AI SOC PAGE ================= */

/* ---- platform scale ----
   Light section, dark CARDS. Each card carries the same two layers as the hero:
   the triangular mesh (::before) and a red/blue aura (::after), so they read as
   part of the same world rather than flat panels.
   Numerals are brand #c30017 at 46-74px. */
/* De-tinted with the rest. It was --paper on a --bg page, one more
   near-identical step doing the work a section break should do. */
.scaleband{}

/* These bands USED to re-draw the page lattice and the ambient wash, because
   each of them painted a background colour of its own and would otherwise have
   punched a blank hole in the page texture.

   REMOVED 15 Sep 2026. The tone pass took the background colours off all four,
   so the re-draw stopped being a repair and became a DUPLICATE: the body's own
   fixed layers were already showing through, and these painted the wash and the
   lattice a second time on top. Two coats of an 8% red radial is a visibly
   different white from one, which is exactly the banding the tone pass was
   meant to end - the Microsoft proof strip read pink next to the section below
   it. The body is now the only thing that paints page texture.

   If a light band ever needs its own background colour again, it needs this
   re-draw back with it. The two go together or neither does. */
.scalerow{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;max-width:1180px;margin:34px auto 0}
.scalerow .sb{position:relative;isolation:isolate;overflow:hidden;
  padding:clamp(30px,3.6vw,44px) clamp(18px,2.4vw,28px);
  text-align:center;display:flex;flex-direction:column;gap:10px;border-radius:var(--radius-lg);
  background:linear-gradient(180deg,var(--bd-1),var(--bd-2));
  border:1px solid rgba(255,255,255,.09);
  box-shadow:0 26px 60px -34px rgba(10,14,25,.7),inset 0 1px 0 rgba(255,255,255,.07);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease),border-color .3s}

/* the same three lights as a hero, off the same tokens */
.scalerow .sb::after{content:"";position:absolute;z-index:-2;inset:-40%;pointer-events:none;
  background:
    radial-gradient(circle at 26% 18%, var(--bd-red), transparent 58%),
    radial-gradient(circle at 8% 72%, var(--bd-green-aura), transparent 58%),
    radial-gradient(circle at 82% 74%, var(--bd-blue-aura), transparent 62%);
  filter:blur(26px);opacity:.9;transition:opacity .35s var(--ease)}
/* square grid, same tile and colour as .sec-dark::before. The card is far
   smaller than a section, so the lattice is scaled down to suit and takes a
   flat wash rather than the section-height fade. */
.scalerow .sb::before{content:"";position:absolute;z-index:-1;inset:0;pointer-events:none;opacity:.55;
  --bd-mesh-size:34px;
  background:var(--bd-mesh-ink);
  -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)}
.scalerow .sb:hover{transform:translateY(-4px);border-color:rgba(255,255,255,.16);
  box-shadow:0 34px 72px -32px rgba(10,14,25,.85),inset 0 1px 0 rgba(255,255,255,.09)}
.scalerow .sb:hover::after{opacity:1}

/* THE RAIL CARRIES THE COLOUR, one per card rather than three reds.

   Three identical red rails above three identical red numbers gave the row no
   internal rhythm: it read as one card repeated. Red, blue then green is the
   accent sequence the masthead dot cycles through and the dark bands already
   carry as auras, so the row now belongs to the same system instead of being
   a red block within it.

   The NUMBER stays red on all three, deliberately. Three giant numerals in
   three colours is a different design - louder, and it makes the figures look
   like categories rather than one set of facts. The rail and the icon carry
   the colour; the data stays in the brand. */
.scalerow .rail{position:absolute;top:0;left:50%;transform:translateX(-50%);z-index:1;
  width:60px;height:3px;border-radius:0 0 3px 3px;background:var(--red)}
.scalerow .sb:nth-child(2) .rail{background:var(--blue-dk)}
.scalerow .sb:nth-child(3) .rail{background:var(--green-dk)}
.scalerow .sb:nth-child(2) .sbi{color:var(--blue-dk)}
.scalerow .sb:nth-child(3) .sbi{color:var(--green-dk)}
/* The aura behind each card leans to its own colour too, so the tint under
   the number agrees with the rail above it rather than fighting it. */
.scalerow .sb:nth-child(2)::after{background:
  radial-gradient(circle at 26% 18%, var(--bd-blue-aura), transparent 58%),
  radial-gradient(circle at 8% 72%, var(--bd-red), transparent 58%),
  radial-gradient(circle at 82% 74%, var(--bd-green-aura), transparent 62%)}
.scalerow .sb:nth-child(3)::after{background:
  radial-gradient(circle at 26% 18%, var(--bd-green-aura), transparent 58%),
  radial-gradient(circle at 8% 72%, var(--bd-blue-aura), transparent 58%),
  radial-gradient(circle at 82% 74%, var(--bd-red), transparent 62%)}
.scalerow .v{position:relative;z-index:1;font-family:var(--display);font-weight:800;
  font-size:clamp(46px,7vw,74px);line-height:1;letter-spacing:-.045em;color:var(--red);
  font-variant-numeric:tabular-nums}
.scalerow .sbi{position:relative;z-index:1;width:26px;height:26px;color:var(--red-3);opacity:.9;margin:0 auto 4px}
.scalerow .n{position:relative;z-index:1;display:block;margin-top:9px;font-size:13.5px;line-height:1.5;
  color:var(--dk-mut);max-width:30ch;margin-inline:auto}
.scalerow .k{position:relative;z-index:1;font-family:var(--display);font-weight:600;font-size:15.5px;
  color:#fff;letter-spacing:-.01em}
.scalefoot{max-width:70ch;margin:32px auto 0;text-align:center;color:var(--fg-soft);
  font-size:clamp(15.5px,1.8vw,17.5px);line-height:1.65}
.scalefoot strong{color:var(--red);font-weight:600}
@media (max-width:820px){.scalerow{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.scalerow .sb:hover{transform:none}}

/* ---- MSSP vs AI SOC: paired rows, not a spreadsheet ---- */
.vs{max-width:1080px;margin:0 auto;display:grid;gap:10px}
.vs-head{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:2px}
.vs-lab{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;padding:0 4px}
.vs-lab.a{color:var(--fg-mut)}
.vs-lab.b{color:var(--red)}
.vs-row{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.vs-cell{display:flex;align-items:flex-start;gap:12px;padding:16px 18px;border-radius:var(--radius);font-size:14.5px;line-height:1.5}
.vs-cell svg{width:17px;height:17px;flex:none;margin-top:2px}
.vs-cell.a{background:var(--bg-2);border:1px solid var(--line);color:var(--fg-soft)}
.vs-cell.a svg{color:var(--fg-faint)}
.vs-cell.b{position:relative;overflow:hidden;background:var(--paper);border:1px solid var(--line);padding-left:21px;color:var(--ink);font-weight:500;
  box-shadow:var(--shadow-sm);transition:transform .25s var(--ease),box-shadow .25s}
.vs-cell.b::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--seam-grad-v)}
.vs-cell.b svg{color:var(--red)}
.vs-row:hover .vs-cell.b{transform:translateY(-2px);box-shadow:var(--shadow)}
@media (max-width:760px){
  .vs-head{display:none}
  .vs-row{grid-template-columns:1fr;gap:0;margin-bottom:8px}
  .vs-cell.a{border-radius:var(--radius) var(--radius) 0 0;border-bottom:0}
  .vs-cell.b{border-radius:0 0 var(--radius) var(--radius)}
}

/* ---- what lands on your desk ---- */
.delivgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(272px,1fr));grid-auto-rows:1fr;gap:18px}
.deliv{position:relative;background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);padding:26px 24px;
  box-shadow:var(--shadow-sm);transition:transform .25s var(--ease),box-shadow .25s,border-color .25s}
.deliv:hover{transform:translateY(-5px);box-shadow:var(--shadow);border-color:var(--line-2)}
.deliv .ic{width:44px;height:44px;border-radius:12px;display:grid;place-items:center;background:var(--red-wash);
  border:1px solid rgba(195,0,23,.16);color:var(--red);margin-bottom:16px;transition:background .3s,color .3s}
.deliv:hover .ic{background:var(--red);color:#fff;border-color:var(--red)}
/* THE ICONS CYCLE, 22 Sep, for the same reason the baseline cards' rails do
   and in the same three colours. These four sit directly above the two verdict
   cards, which now carry a red rail and a blue one - four identical red icons
   over a red-and-blue pair was an inconsistency inside a single screenful.

   The cycle is on 3 rather than on the card count, matching .baseline: with
   four cards it runs red, blue, green, red. Hover still fills the holder with
   its own colour, so the interaction is unchanged, just not always red. */
.deliv:nth-child(3n+2) .ic{background:color-mix(in srgb,var(--blue) 8%,transparent);
  border-color:color-mix(in srgb,var(--blue) 18%,transparent);color:var(--blue)}
.deliv:nth-child(3n) .ic{background:color-mix(in srgb,var(--green) 8%,transparent);
  border-color:color-mix(in srgb,var(--green) 18%,transparent);color:var(--green)}
.deliv:nth-child(3n+2):hover .ic{background:var(--blue);color:#fff;border-color:var(--blue)}
.deliv:nth-child(3n):hover .ic{background:var(--green);color:#fff;border-color:var(--green)}
.deliv h3{font-size:16.5px;letter-spacing:-.01em;margin-bottom:8px}
.deliv p{margin:0;color:var(--fg-soft);font-size:14px;line-height:1.55}

.speedrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:18px;margin-top:22px}
/* THE RED WASH IS GONE, 22 Sep - Adam: "i dont like the red/white fade, so
   bring that inline with our standards".

   It was linear-gradient(160deg, var(--red-wash), var(--paper)) - a pink
   diagonal, and the only one left on the site. Two things were wrong with it.
   It tinted the whole card, so the red label and the red rail had to compete
   with a red ground rather than sit on paper; and it was a one-off, invented
   for these two cards, when the site already has a settled treatment for a
   light card - the top-down paper gradient plus the graph-paper texture that
   .benefit-card and .estcard share.

   So these take that treatment by joining those selectors rather than by
   copying the rules, which is the point: one implementation, and the three
   families cannot drift. The colour now arrives where it does everywhere
   else - in the rail at the top and the mono label - rather than as a wash
   over everything. */
.sp{position:relative;isolation:isolate;overflow:hidden;
  border:1px solid var(--line);box-shadow:var(--shadow-sm);
  border-radius:var(--radius);padding:24px;display:flex;flex-direction:column;gap:5px;
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
/* The .note variant is a flat grey aside rather than a figure, so it keeps its
   own ground and opts out of the texture. Its background already wins on
   specificity; this stops the paper showing through the pseudo. */
.sp.note::before,.sp.note::after{display:none}
.sp .k{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--red)}
.sp .v{font-family:var(--display);font-weight:800;font-size:clamp(30px,3.6vw,42px);line-height:1;letter-spacing:-.03em;color:var(--ink)}
.sp .v.small{font-size:clamp(22px,2.4vw,28px)}
.sp .d{font-size:13.5px;color:var(--fg-soft);line-height:1.5;margin-top:4px}
.sp.note{background:var(--bg-2)}
.sp.note .k{color:var(--fg-mut)}

/* ---- guardrails ---- */
.ctrlgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px;margin-bottom:26px}
.ctrl{position:relative;overflow:hidden;background:var(--dk-panel);border:1px solid var(--dk-line);border-radius:var(--radius);padding:24px;
  transition:border-color .25s,transform .25s var(--ease)}
.ctrl:hover{border-color:var(--dk-line-2);transform:translateY(-3px)}
/* THE RAIL, RED THEN BLUE THEN GREEN - 22 Sep, Adam, extending the same ruling
   that put a cycle on the baseline cards. These had no rail at all, so this
   adds the one the dark package cards (.term) already carry rather than
   inventing a treatment: 2px, inset to match the card's own 24px padding, and
   running the full width on hover.

   Cycled on a 3 rather than on the card count, which is what lets one rule
   serve all three pages that use these - five cards here, three on how it
   works, four on the 24/7 SOC page - without any of them restyling the others.

   The dark colour variants, because this is a dark panel. Every other dark
   cycle on the site uses them. */
.ctrl::after{content:"";position:absolute;top:0;left:24px;right:24px;height:2px;
  /* --red-3, not --red. The blue and green here are the -dk variants and so is
     the number directly beneath, so a --red rail made card one the only one in
     the set whose rail and number were two different reds. */
  border-radius:0 0 2px 2px;background:var(--red-3);
  transition:left .3s var(--ease),right .3s var(--ease)}
.ctrl:nth-child(3n+2)::after{background:var(--blue-dk)}
.ctrl:nth-child(3n)::after{background:var(--green-dk)}
.ctrl:hover::after{left:0;right:0}
.ctrl .n{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.1em;color:var(--red-3);display:block;margin-bottom:12px}
/* The number follows its own rail, so a card is one colour rather than a green
   line over a red number. */
.ctrl:nth-child(3n+2) .n{color:var(--blue-dk)}
.ctrl:nth-child(3n) .n{color:var(--green-dk)}
@media (prefers-reduced-motion:reduce){.ctrl::after{transition:none}}
.ctrl h3{font-size:16.5px;color:#fff;letter-spacing:-.01em;margin-bottom:9px;line-height:1.35}
.ctrl p{margin:0;color:var(--dk-mut);font-size:14px;line-height:1.6}

.accessrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px;
  border-top:1px solid var(--dk-line);padding-top:26px}
.acc{display:flex;gap:14px;align-items:flex-start}
.acc .ic{width:42px;height:42px;flex:none;border-radius:11px;display:grid;place-items:center;
  background:rgba(195,0,23,.16);border:1px solid var(--dk-line);color:var(--red-3)}
/* THE HOLDER CARRIES THE CYCLE, 22 Sep. These rows have no card and no border
   to put a rail on, so the colour goes where there already is some - the icon
   holder - which is how the deliverable cards take it on the light half of
   this page.

   16% fill to match the red it replaces, and the dark colour variants for the
   glyph. Cycled on a 3, so the rule holds if a fourth row is ever added. */
.acc:nth-child(3n+2) .ic{background:color-mix(in srgb,var(--blue-dk) 16%,transparent);color:var(--blue-dk)}
.acc:nth-child(3n) .ic{background:color-mix(in srgb,var(--green-dk) 16%,transparent);color:var(--green-dk)}
.acc h4{color:#fff;font-size:15.5px;margin-bottom:5px}
.acc p{margin:0;color:var(--dk-mut);font-size:13.5px;line-height:1.55}

/* ---------- book a demo ---------- */
.demo-hero .hero-grid{align-items:start}
.demofacts{display:flex;flex-wrap:wrap;gap:14px 34px;margin-top:36px;padding-top:26px;border-top:1px solid var(--dk-line)}
.demofacts .df{display:flex;flex-direction:column;gap:3px}
.demofacts .v{font-family:var(--display);font-weight:700;font-size:19px;color:#fff;letter-spacing:-.02em}
.demofacts .k{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--dk-mut)}

/* A WHITE ISLAND INSIDE A DARK SECTION, which is the whole reason the reset
   below exists. Every page that carries this card - contact, demo, partners,
   book a meeting, the workshops - puts it in a `.sec-dark` hero, so everything
   in it inherits that section's dark-ground text colours and then sits on
   paper.

   The card was already resetting the colours it knew about: h2, .fld>span,
   inputs, .err all name --ink or --fg-mut explicitly. What was NOT reset was
   anything it did not anticipate - and `.sec-dark p{color:var(--dk-soft)}`
   caught exactly that. `.formnote` on the contact, book-a-meeting and partner
   pages was rendering #b9bfce on #ffffff: a contrast ratio of 1.84, which is
   effectively invisible. "We reply during working hours in your region" has
   been unreadable on three live pages.

   Found 22 Sep 2026 by walking every text node on twelve pages and comparing
   its computed colour against its computed ground.

   RESET AT THE CARD, NOT AT .formnote. Fixing the one element that was caught
   leaves the next one to be found by a customer: the card is a light surface
   and should declare that once, for everything inside it. */
.formcard{background:var(--paper);border-radius:var(--radius-lg);padding:clamp(24px,3vw,32px);
  box-shadow:0 40px 90px -40px rgba(0,0,0,.8);border:1px solid var(--line);
  color:var(--fg)}
.formcard p,.formcard li,.formcard .lede,.formcard .muted{color:var(--fg-soft)}
.formcard .formnote{color:var(--fg-mut)}
/* The rest of this reset lives at the END of this file. See the note there:
   section-level rules like `.sec-dark .sp-body p:not(.pullquote)` are (0,3,1)
   and reach inside the card, so a (0,2,0) rule here cannot win however it is
   written. */
.formcard h2{font-size:23px;letter-spacing:-.02em;margin-bottom:20px;color:var(--ink)}
.formcard .fld{display:block;margin-bottom:15px}
.formcard .fld>span{display:block;font-size:13px;font-weight:600;color:var(--ink);margin-bottom:6px}
.formcard .fld>span em{font-style:normal;font-weight:400;color:var(--fg-mut);font-size:11.5px;margin-left:5px}
.formcard input,.formcard select,.formcard textarea{width:100%;font:inherit;font-size:15px;color:var(--ink);
  background:var(--mist);border:1px solid var(--line-2);border-radius:10px;padding:11px 13px;transition:border-color .2s,background .2s}
.formcard input:focus,.formcard select:focus,.formcard textarea:focus{outline:2px solid var(--red);outline-offset:1px;border-color:transparent;background:var(--paper)}
.formcard textarea{resize:vertical;line-height:1.5}
.formcard .two{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.formcard .err{display:block;color:var(--red);font-size:12.5px;margin-top:5px;font-weight:500}
.formcard .btn{width:100%;justify-content:center;margin-top:6px}
.formcard .fine{color:var(--fg-mut);font-size:12px;line-height:1.5;margin:12px 0 0;text-align:center}
.formnote{border-radius:10px;padding:11px 14px;font-size:13.5px;margin:0 0 16px}
.formnote.bad{background:var(--red-wash);border:1px solid rgba(195,0,23,.28);color:var(--red-dk)}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.formcard .sent{text-align:center;padding:14px 0}
.formcard .sent .tick{width:56px;height:56px;border-radius:50%;display:grid;place-items:center;margin:0 auto 18px;
  background:rgba(18,161,80,.12);border:1px solid rgba(18,161,80,.35);color:var(--green)}
.formcard .sent .tick svg{width:26px;height:26px}
.formcard .sent h2{margin-bottom:10px}
.formcard .sent p{color:var(--fg-soft);font-size:14.5px;line-height:1.6;margin:0 0 10px}
.formcard .sent .fine{font-family:var(--mono);font-size:11.5px;color:var(--fg-mut);margin-bottom:18px}

.deliv .stepn{display:block;font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.12em;
  color:var(--red);margin-bottom:12px}
@media (max-width:560px){.formcard .two{grid-template-columns:1fr}}

/* ---- two doors: route the two buyers before the tile wall ---- */
.doors{display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:1060px;margin:0 auto}
.door{position:relative;display:flex;flex-direction:column;overflow:hidden;
  padding:clamp(26px,3vw,34px);border-radius:var(--radius-lg);background:var(--paper);
  border:1px solid var(--line);box-shadow:var(--shadow-sm);color:inherit;
  transition:transform .28s var(--ease),box-shadow .28s,border-color .28s}
/* The accent rule on a card is the signal band rather than flat red (Adam,
   16 Sep 2026). It only works because these cards are wide - see the note on
   --seam-grad in main.css; anything under ~200px paints a rainbow smear. */
.door::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:var(--seam-grad);
  transform:scaleX(0);transform-origin:left;transition:transform .4s var(--ease)}
.door:hover,.door:focus-visible{transform:translateY(-5px);box-shadow:var(--shadow);border-color:var(--line-2)}
.door:hover::before,.door:focus-visible::before{transform:scaleX(1)}
.door:focus-visible{outline:2px solid var(--red);outline-offset:3px}
.door .dk{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red);margin-bottom:12px}
.door h3{font-size:clamp(22px,2.6vw,28px);letter-spacing:-.025em;margin-bottom:12px}
.door .dl{color:var(--fg-soft);font-size:15px;line-height:1.6;margin:0 0 18px}
.door .dpts{display:grid;gap:9px;margin:0 0 20px;padding-top:18px;border-top:1px solid var(--line)}
.door .dpts li{position:relative;padding-left:20px;font-size:13.5px;color:var(--fg-soft);line-height:1.5}
.door .dpts li::before{content:"";position:absolute;left:0;top:7px;width:7px;height:7px;border-radius:2px;background:var(--red)}
.door .dgo{margin-top:auto;display:inline-flex;align-items:center;gap:8px;font-family:var(--display);
  font-weight:600;font-size:14.5px;color:var(--red)}
.door .dgo svg{width:16px;height:16px;transition:transform .25s var(--ease)}
.door:hover .dgo svg{transform:translateX(4px)}
@media (max-width:820px){.doors{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.door,.door::before,.door .dgo svg{transition:none}.door:hover{transform:none}}

/* ---------- hero credential ----------
   The flagship badge sits bottom-right of the hero, under the live console —
   the first credential a visitor sees, at the moment they are deciding whether
   this is a real company. */

/* ---------- homepage: proof strip ----------
   Larger than the first attempt: the badges are the proof, so they get room. */
/* NOTHING but padding. This band has now lost, in order: a --bg-2 background
   tint, a re-drawn lattice and ambient layer of its own, and a border-block
   hairline. All three were doing the same job - marking where the band starts
   and stops - and all three read as an unfinished join rather than a design.
   The badge and the sentence are the content; whitespace is the separator. */
.proof{padding:clamp(46px,6vw,72px) 0}
.proofrow{display:grid;grid-template-columns:auto 1fr;gap:clamp(34px,5vw,70px);align-items:center;max-width:1100px;margin:0 auto}
/* One badge, not four: the Security designation with all its specialisms is
   the claim worth making, and at this size the specialisms are legible. */
.pf-badge{display:block;width:clamp(210px,20vw,264px);border-radius:12px;
  box-shadow:var(--shadow-sm);transition:transform .25s ease,box-shadow .25s ease}
.pf-badge:hover{transform:translateY(-3px);box-shadow:var(--shadow)}
.pf-badge img{display:block;width:100%;height:auto;border-radius:12px;background:var(--paper)}
.pf-lead{font-family:var(--display);font-weight:700;font-size:clamp(22px,2.7vw,30px);line-height:1.24;
  letter-spacing:-.025em;color:var(--ink);margin:0 0 16px;max-width:26ch}
.pf-body{margin:0;color:var(--fg-soft);font-size:clamp(15.5px,1.7vw,17px);line-height:1.65;max-width:54ch}
.pf-body strong{color:var(--ink);font-weight:600}
@media (max-width:900px){
  .proofrow{grid-template-columns:1fr;gap:28px;text-align:center;justify-items:center}
  .pf-lead,.pf-body{max-width:none;margin-inline:auto}
}

/* the Microsoft door sits full width under the two primary doors */
.door-wide{grid-column:1 / -1}
@media (max-width:820px){.door-wide{grid-column:auto}}

/* ---------- autonomy dial ---------- */
.dialrow{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
.dialcard{position:relative;background:var(--dk-panel);border:1px solid var(--dk-line);
  border-radius:var(--radius);padding:26px 24px;transition:border-color .25s,transform .25s var(--ease)}
.dialcard:hover{border-color:var(--dk-line-2);transform:translateY(-3px)}
.dialcard .n{display:block;font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.12em;
  color:var(--red-3);margin-bottom:12px}
.dialcard h3{font-size:17px;color:#fff;letter-spacing:-.01em;margin-bottom:9px;line-height:1.35}
.dialcard p{margin:0;color:var(--dk-mut);font-size:14px;line-height:1.6}
.dialfoot{text-align:center;color:var(--dk-soft);font-size:14.5px;margin:26px 0 0}
@media (max-width:860px){.dialrow{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.dialcard:hover{transform:none}}

/* ---------- integrations: the Microsoft surface ----------
   A two-column list rather than a <table>. The direction badge is the point of
   the section - "what does this write into my tenant" is the question a
   security team actually asks - so it sits next to the name rather than in a
   third column a reader has to track across. */
.intlist{list-style:none;margin:0;padding:0;display:grid;gap:0;
  border-top:1px solid var(--line)}
.introw{display:grid;grid-template-columns:290px 1fr;gap:14px 28px;
  padding:16px 0;border-bottom:1px solid var(--line);align-items:start}
.int-k{display:flex;flex-direction:column;gap:7px;align-items:flex-start}
.int-n{font-family:var(--display);font-weight:600;font-size:15.5px;letter-spacing:-.01em;
  color:var(--ink);line-height:1.3}
.int-io{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;border-radius:4px;padding:3px 8px;white-space:nowrap}
.int-io.is-read{color:var(--green);background:color-mix(in srgb,var(--green) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--green) 26%,transparent)}
.int-io.is-both{color:var(--red);background:var(--red-wash);
  border:1px solid color-mix(in srgb,var(--red) 26%,transparent)}
.int-io.is-access{color:var(--fg-mut);background:var(--mist);border:1px solid var(--line-2)}
.int-d{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.6;padding-top:2px}
@media (max-width:760px){
  .introw{grid-template-columns:1fr;gap:8px;padding:18px 0}
  .int-k{flex-direction:row;align-items:center;gap:10px;flex-wrap:wrap}
}

/* ---------- contact: the three ways to reach us ---------- */
.contactlist{list-style:none;margin:30px 0 0;padding:0;display:grid;gap:14px;max-width:400px}
.contactlist li{display:grid;grid-template-columns:96px 1fr;gap:16px;align-items:baseline;
  padding-bottom:13px;border-bottom:1px solid var(--dk-line)}
.contactlist li:last-child{border-bottom:0;padding-bottom:0}
.cl-k{font-family:var(--mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--dk-mut)}
.cl-v{font-family:var(--display);font-weight:600;font-size:17px;letter-spacing:-.01em;color:#fff;
  transition:color .2s}
.cl-v:hover{color:var(--red-3)}
@media (max-width:560px){.contactlist li{grid-template-columns:1fr;gap:3px}}

/* the confirmation tick on a submitted form */
.fc-ok{display:grid;place-items:center;width:46px;height:46px;border-radius:50%;
  background:color-mix(in srgb,var(--green) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--green) 30%,transparent);
  color:var(--green);margin-bottom:18px}
.fc-ok svg{width:22px;height:22px}

/* ---------- contact: where we are ----------
   SOCs get the accent and a status line; presence does not. The visual
   difference carries the distinction the copy makes. */
/* FLEX, NOT A FIXED THREE COLUMNS. Seven locations in a 3-wide grid leaves one
   card stranded on its own row, which reads as an afterthought rather than a
   location. Wrapping and centring puts the remainder in the middle, so the
   block stays balanced at any count — and the count is the thing that keeps
   changing. */
.locrow{display:flex;flex-wrap:wrap;justify-content:center;gap:16px;margin-top:clamp(24px,3vw,32px)}
.locrow > .loc{flex:1 1 240px;max-width:340px}
.loc{background:var(--dk-panel);border:1px solid var(--dk-line);border-radius:var(--radius);
  padding:22px 24px}
/* Signal band, not flat red (Adam, 16 Sep 2026). overflow:hidden clips the
   bar to the corner radius; the border's width moved into the padding. */
/* The strip runs across BOTH rows now (Adam, 19 Sep 2026). The SOC and office
   distinction was never carried by it - it is carried by the label and by the
   presence or absence of the 24/7 chip - so matching them reads as one set of
   locations rather than two grades of card. */
.loc{position:relative;overflow:hidden;padding-top:24px}
.loc::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;background:var(--seam-grad)}
.loc-k{display:block;font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;color:var(--dk-mut);margin-bottom:9px}
.loc h3{font-size:19px;letter-spacing:-.015em;color:#fff;margin:0}
.loc .loc-k{color:var(--red-3)}
/* One line under the name, for a location that has something worth adding. */
.loc-note{display:block;margin-top:7px;font-size:12.5px;color:var(--dk-mut);line-height:1.35}
.loc-st{display:inline-block;margin-top:11px;font-family:var(--mono);font-size:9.5px;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--green);
  background:color-mix(in srgb,var(--green) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--green) 26%,transparent);border-radius:4px;padding:3px 8px}
@media (max-width:720px){.locrow > .loc{flex:1 1 100%;max-width:none}}

/* ---------- about: the people ---------- */
.peoplerow{display:grid;grid-template-columns:repeat(2,1fr);gap:18px;margin-top:clamp(26px,3vw,36px)}
.person{display:grid;grid-template-columns:132px 1fr;gap:20px;align-items:start;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:22px;box-shadow:var(--shadow-sm)}
.person img{width:132px;height:auto;border-radius:12px;display:block;background:var(--mist)}
.pe-body{min-width:0}
.person h3{font-size:19px;letter-spacing:-.015em;margin:0 0 3px}
.pe-role{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--red);font-weight:700;margin-bottom:11px}
.person p{margin:0;color:var(--fg-soft);font-size:13.5px;line-height:1.6}
@media (max-width:900px){.peoplerow{grid-template-columns:1fr}}
@media (max-width:520px){
  .person{grid-template-columns:1fr;gap:14px}
  .person img{width:100px}
}

/* ---------- D&R: MDR vs MSSP vs SOC as a service ----------
   The middle card is ours and is flagged, but the other two are described
   straight rather than knocked - we sell "SOC as a service" on another page. */
.termrow{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;
  margin-top:clamp(26px,3vw,36px);align-items:stretch}
.term{display:flex;flex-direction:column;background:var(--dk-panel);
  border:1px solid var(--dk-line);border-radius:var(--radius-lg);padding:26px 24px;
  transition:border-color .25s,transform .25s var(--ease)}
.term:hover{border-color:var(--dk-line-2);transform:translateY(-3px)}
/* A RAIL PER CARD, red then blue then green - the masthead dot's sequence and
   the stat row's. Three identical dark boxes in a row had nothing to tell them
   apart until you read them, which is a lot to ask of somebody deciding which
   package they need. */
.term{position:relative;overflow:hidden}
.term::after{content:"";position:absolute;top:0;left:24px;right:24px;height:2px;
  /* --red-3 from 22 Sep, matching .term-n directly beneath it and the -dk blue
     and green on the other two cards. Same fix as .ctrl - see the note there. */
  border-radius:0 0 2px 2px;background:var(--red-3);
  transition:left .3s var(--ease),right .3s var(--ease)}
.term:nth-child(2)::after{background:var(--blue-dk)}
.term:nth-child(3)::after{background:var(--green-dk)}
/* The rail runs the full width on hover. Small, and it is the difference
   between a card that responds and one that only lifts. */
.term:hover::after{left:0;right:0}
.term:nth-child(2) .term-n{color:var(--blue-dk)}
.term:nth-child(3) .term-n{color:var(--green-dk)}

/* The featured card takes the SAME gradient edge as the recommended card on
   the Managed SIEM page. "This is the one" should not look different
   depending which page you are on. The border is transparent and the gradient
   is clipped to the border box behind it; anywhere background-clip is ignored
   it falls back to a plainly bordered card. */
.term.is-feat{
  border-color:transparent;
  background:
    linear-gradient(var(--dk-panel),var(--dk-panel)) padding-box,
    linear-gradient(115deg,var(--red) 0%,var(--red-2) 28%,var(--blue-dk) 62%,var(--green-dk) 100%) border-box;
  box-shadow:0 30px 70px -36px var(--red-glow)}
.term-n{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red-3)}
.term h3{font-size:20px;letter-spacing:-.018em;color:#fff;margin:10px 0 11px}
.term p{margin:0;color:var(--dk-mut);font-size:14px;line-height:1.6}
.term-v{margin:auto 0 0 !important;padding-top:14px;border-top:1px solid var(--dk-line);
  color:var(--dk-soft) !important;font-size:13px !important}
.term.is-feat .term-v{color:#fff !important}
/* Optional bullets inside a term card, used by the tier table on the
   vulnerability page.
   EVERY SELECTOR HERE IS PREFIXED WITH .term ON PURPOSE. These cards carry
   .ticks, which is a LIGHT-section component declared further down this file
   at ~line 1290 - `.ticks li{color:var(--ink)}`. Equal specificity plus a
   later source position meant near-black text on a dark panel, which is
   exactly what shipped and had to be fixed. The .sec-dark override does not
   rescue it either: the tier row lives in a light .sec. */
.term .term-pts{margin:14px 0 0;display:grid;gap:9px}
.term .term-pts li{color:var(--dk-soft);font-size:13.5px;line-height:1.55;align-items:flex-start}
.term .term-pts li svg{width:15px;height:15px;margin-top:3px;color:var(--green)}
@media (max-width:860px){.termrow{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.term:hover{transform:none}}

/* ---------- D&R: what the agents do vs what people do ---------- */
.splitcols{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-top:clamp(26px,3vw,36px)}
.splitcol{background:var(--dk-panel);border:1px solid var(--dk-line);
  border-radius:var(--radius-lg);padding:26px 28px;border-top:2px solid var(--blue)}
.splitcol.is-human{border-top-color:var(--red)}
.splitcol h3{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--blue);margin:0 0 16px}
.splitcol.is-human h3{color:var(--red-3)}
.splitcol ul{list-style:none;margin:0;padding:0;display:grid;gap:11px}
.splitcol li{position:relative;padding-left:20px;color:var(--dk-soft);font-size:14px;line-height:1.5}
.splitcol li::before{content:"";position:absolute;left:0;top:8px;width:6px;height:6px;
  border-radius:50%;background:var(--blue)}
.splitcol.is-human li::before{background:var(--red)}
@media (max-width:760px){.splitcols{grid-template-columns:1fr}}
/* Three-across variant, used by the MXDR response bands. The honest answer to
   "what will you do without asking" has three parts - runs alone, proposed then
   carried out, always a person - and forcing it into the two-column .splitcols
   would drop the middle band, which is the one a buyer actually wants. */
.splitcols.is-3{grid-template-columns:repeat(3,1fr)}
.splitcol-n{margin:0 0 14px;color:var(--dk-mut);font-size:12.5px;line-height:1.55}
@media (max-width:1040px){.splitcols.is-3{grid-template-columns:1fr}}

/* ---------- D&R: the rest of the practice ----------
   Chips rather than cards: these are signposts to child pages, and eight cards
   would out-weigh the sections above them that carry the actual argument. */
.childgrid{list-style:none;margin:clamp(24px,3vw,32px) 0 0;padding:0;
  display:flex;flex-wrap:wrap;gap:10px}
.childcard{display:inline-flex;align-items:center;gap:10px;
  font-family:var(--display);font-weight:600;font-size:14.5px;letter-spacing:-.008em;
  background:var(--paper);border:1px solid var(--line);border-radius:100px;
  padding:11px 18px;color:var(--fg-mut);
  transition:border-color .2s,color .2s,transform .2s var(--ease),box-shadow .2s}
.childcard svg{width:14px;height:14px;flex:none;transition:transform .25s var(--ease)}
a.childcard.is-live{color:var(--ink);box-shadow:var(--shadow-sm)}
a.childcard.is-live:hover{border-color:var(--red);color:var(--red);transform:translateY(-2px);
  box-shadow:var(--shadow)}
a.childcard.is-live:hover svg{transform:translateX(3px)}
.cc-wip{font-family:var(--mono);font-size:9px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-mut);border:1px solid var(--line);
  border-radius:4px;padding:2px 6px}
@media (prefers-reduced-motion:reduce){a.childcard.is-live:hover{transform:none}}

/* ---------- 24/7 SOC: the SOC-CMM domains ----------
   Five domains, so a three-column grid leaves two on the second row. That reads
   fine here because they are a list of equals rather than a sequence - nothing
   is stranded, the row just ends. */
.cmmrow{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;
  margin-top:clamp(26px,3vw,36px)}
.cmm{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:22px 24px;box-shadow:var(--shadow-sm)}
.cmm-n{display:block;font-family:var(--mono);font-size:10.5px;font-weight:700;
  letter-spacing:.12em;color:var(--red);margin-bottom:10px}
.cmm h3{font-size:16.5px;letter-spacing:-.012em;margin:0 0 7px;line-height:1.3}
.cmm p{margin:0;color:var(--fg-soft);font-size:13.5px;line-height:1.55}
@media (max-width:900px){.cmmrow{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){.cmmrow{grid-template-columns:1fr}}

/* ---------- pricing: what moves the number ---------- */
.varlist{list-style:none;margin:0;padding:0;max-width:880px;margin-inline:auto;
  border-top:1px solid var(--line)}
.varrow{display:grid;grid-template-columns:56px 1fr;gap:20px;padding:20px 0;
  border-bottom:1px solid var(--line);align-items:start}
.var-n{font-family:var(--mono);font-size:13px;font-weight:700;color:var(--red);padding-top:3px}
.varrow h4{font-family:var(--display);font-weight:600;font-size:17px;letter-spacing:-.012em;
  margin:0 0 6px;color:var(--ink);text-transform:none}
.varrow p{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.6}
/* The rows arrive in order, because the copy above them says they are IN
   order — "in roughly the order they matter". A list that claims an ordering
   and then appears all at once has thrown away the one thing it was telling
   you. Cheap to do, and it is the ordering that is doing the work, not the
   motion. */
.varlist .varrow{opacity:0}
.varlist.in .varrow{opacity:1;
  animation:mtrRise .45s var(--ease) backwards;animation-delay:calc(var(--i) * 70ms)}
@media (max-width:620px){
  .varrow{grid-template-columns:1fr;gap:6px}
  .var-n{padding-top:0}
}
@media (prefers-reduced-motion:reduce){
  .varlist .varrow{opacity:1}
  .varlist.in .varrow{animation:none}
}

/* ---------- pricing: the three packages ----------
   Summary cards, not price cards - there are no prices to put on them. The full
   comparison is on the managed SOC page and is linked to underneath. */
.pkgrow{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;
  margin-top:clamp(26px,3vw,36px);align-items:stretch}
.pkg{position:relative;display:flex;flex-direction:column;
  background:var(--dk-panel);border:1px solid var(--dk-line);border-radius:var(--radius-lg);
  padding:28px 26px;transition:border-color .25s,transform .25s var(--ease)}
.pkg:hover{border-color:var(--dk-line-2);transform:translateY(-3px)}
.pkg.is-feat{border-color:color-mix(in srgb,var(--red) 45%,var(--dk-line));
  box-shadow:0 30px 70px -36px var(--red-glow)}
/* THE RECOMMENDED-PACKAGE BADGE, and it is now the only one on the site.
   Used by the dark .pkg cards here and by the light .tier cards on the managed
   SOC page, which was carrying a near-identical .rec until 22 Sep - see the
   note there for the collision that made it render cut off.

   THE RING IS WHAT STOPS IT READING AS A SLICED TAB. A 4px spread shadow in
   the colour of whatever it overlaps puts a clean gap between badge and card
   edge, so the two stop touching. --flag-ring defaults to paper because most
   cards are light; the dark package cards set their own below.

   Left-aligned by default, centred by the modifier the managed SOC page adds.
   These cards are the narrower pair and a centred badge would land directly
   over the package number. */
.pkg-flag{position:absolute;top:-13px;left:26px;display:inline-block;font-family:var(--mono);
  font-size:10px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:#fff;
  line-height:1;white-space:nowrap;
  background:var(--red);border-radius:100px;padding:7.5px 16px;
  box-shadow:0 0 0 4px var(--flag-ring,var(--paper)),0 6px 16px -6px var(--red-glow)}
.pkg .pkg-flag{--flag-ring:var(--dk-panel)}
.pkg-k{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--dk-mut)}
.pkg h3{font-size:24px;letter-spacing:-.02em;color:#fff;margin:10px 0 12px}
.pkg-who{margin:0 0 14px;color:var(--dk-soft);font-size:14.5px;line-height:1.55}
.pkg-step{margin:auto 0 0;padding-top:14px;border-top:1px solid var(--dk-line);
  color:var(--dk-mut);font-size:13.5px;line-height:1.55}
@media (max-width:860px){.pkgrow{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.pkg:hover{transform:none}}

/* ---------- pricing: the metering line on a package card ---------- */
/* Sits on the dark .pkg card between the name and the audience line. A rule
   above it rather than a fill: the card already carries a filled pill for
   "Most chosen", and a second filled thing would compete with the one that is
   actually a recommendation. */
.pkg-unit{
  display:flex;flex-direction:column;gap:3px;
  margin:0 0 14px;padding-top:12px;border-top:1px solid var(--dk-line);
}
.pu-k{
  font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--red-3);
}
/* Per-user is the blue rail, per-incident the red, matching the hero console
   rows above - the same two packages, the same two colours, so the mapping is
   carried by colour as well as by text on both. */
.pkg:nth-child(2) .pu-k,
.pkg:nth-child(3) .pu-k{color:var(--blue-dk)}
.pu-w{color:var(--dk-mut);font-size:13px;line-height:1.5}

/* ---------- pricing: how each package is metered ---------- */
/* TWO PANELS, EQUAL WIDTH. Not a table and not a three-column grid: there are
   two models, and a two-up on a light section is the plainest way to say
   "these are both normal" rather than "here is the better one". */
.meterrow{
  display:grid;grid-template-columns:repeat(2,1fr);gap:18px;
  margin-top:clamp(26px,3vw,36px);align-items:stretch;
}
.meter{
  position:relative;display:flex;flex-direction:column;
  background:linear-gradient(180deg,var(--paper) 0%,var(--mist) 100%);
  border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:26px 26px 24px;box-shadow:var(--shadow-sm);
  isolation:isolate;overflow:hidden;
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s;
}
.meter:hover{transform:translateY(-4px);border-color:var(--line-2);box-shadow:var(--shadow)}
/* The rail, red then blue, in the sequence the rest of the site uses and the
   same two colours the package cards give these two models. */
.meter::before{
  content:"";position:absolute;top:0;left:0;right:0;height:2px;z-index:1;
  background:var(--red);
}
.meter:nth-child(2)::before{background:var(--blue)}

.meter-top{
  display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;
  gap:10px;margin-bottom:14px;
}
.meter-top h3{margin:0;font-size:clamp(21px,2.1vw,25px);letter-spacing:-.02em}
.meter-on{display:flex;gap:6px;flex-wrap:wrap}
/* The package names as tags, so the mapping from model to package is legible
   without reading a sentence. Outlined rather than filled - they are a label
   for the panel, not a call to action. */
.mtag{
  font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--red-2);
  border:1px solid color-mix(in srgb,var(--red) 32%,transparent);
  border-radius:100px;padding:4px 10px;white-space:nowrap;
}
.meter:nth-child(2) .mtag{
  /* --blue-ink rather than --blue: this is 10px mono on paper, which needs
     4.5, and --blue measures 4.39 there. The border keeps --blue - a border
     is not text and has no contrast requirement to meet. */
  color:var(--blue-ink);border-color:color-mix(in srgb,var(--blue) 32%,transparent);
}
/* The lede carries the claim and the body carries the reasoning, so the panel
   can be read at two depths - the first paragraph alone is a complete answer. */
.meter-lead{margin:0 0 12px;color:var(--fg);font-size:15.5px;line-height:1.6}
.meter-body{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.65}

/* The line that stops the two panels reading as a comparison. Centred and
   quiet, under both of them, because it is about the pair rather than either. */
.meter-close{
  max-width:64ch;margin:clamp(24px,3vw,32px) auto 0;text-align:center;
  color:var(--fg-soft);font-size:15px;line-height:1.65;
}
@media (max-width:820px){.meterrow{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.meter:hover{transform:none}}

/* ---------- pricing: included on every package ---------- */
.basegrid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;
  margin-top:clamp(26px,3vw,36px)}
.basecard{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:22px;box-shadow:var(--shadow-sm);
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
/* These are deliberately the quiet part of the page - "none of this is an
   upgrade, it is the floor" - so they get the hover the rest of the site's
   cards have and nothing more. Eight cards all shouting would compete with
   the metering stage, which is the section that should win. */
.basecard:hover{transform:translateY(-5px);border-color:var(--line-2);box-shadow:var(--shadow)}
.basecard .ic{display:grid;place-items:center;width:38px;height:38px;border-radius:10px;
  background:var(--red-wash);border:1px solid rgba(195,0,23,.16);color:var(--red);margin-bottom:14px}
.basecard .ic svg{width:19px;height:19px}
.basecard h3{font-size:15.5px;letter-spacing:-.01em;margin:0 0 7px;line-height:1.3}
.basecard p{margin:0;color:var(--fg-soft);font-size:13.5px;line-height:1.55}
@media (max-width:1040px){.basegrid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){.basegrid{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.basecard:hover{transform:none}}

/* ---------- integrations: data engineering ---------- */
.dgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-top:clamp(26px,3vw,36px)}
.dcard{position:relative;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius);padding:24px 26px;box-shadow:var(--shadow-sm)}
.dn{display:inline-block;font-family:var(--mono);font-size:10.5px;font-weight:700;
  letter-spacing:.12em;color:var(--red);margin-bottom:11px}
.dcard h3{font-size:17px;letter-spacing:-.012em;margin:0 0 8px;line-height:1.3}
.dcard p{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.6}

/* The Content Hub breadth line. A stat band rather than another card, because
   it is the one number on the page and it earns the emphasis. */
.hubstat{position:relative;overflow:hidden;display:grid;grid-template-columns:auto 1fr;gap:26px;align-items:center;
  margin-top:18px;padding:24px 28px 24px 31px;border-radius:var(--radius);
  background:var(--mist);border:1px solid var(--line)}
.hubstat::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--seam-grad-v)}
.hs-n{display:flex;flex-direction:column;gap:4px;min-width:0}
.hs-n span{font-family:var(--display);font-weight:800;font-size:clamp(26px,3.4vw,36px);
  line-height:1;letter-spacing:-.03em;color:var(--ink)}
.hs-n em{font-style:normal;font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-mut);max-width:22ch;line-height:1.45}
.hubstat p{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.6}

@media (max-width:760px){
  .dgrid{grid-template-columns:1fr}
  .hubstat{grid-template-columns:1fr;gap:14px;padding:22px}
}

/* ---------- integrations: the wider estate ----------
   Wordmarks as chips rather than vendor logos. A wall of logos reads as
   "certified partner" or "shipped integration", which is precisely the claim
   the briefing forbids for third-party vendors - and it would need asset
   licensing we do not have. Plain type carries the variety without the claim. */
/* Three across rather than auto-fit. Nine categories divide into 3x3 exactly,
   where auto-fit stranded the last card in a row of its own with gaps beside
   it. Revisit the column count if the number of categories changes. */
.estgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;
  margin-top:clamp(26px,3vw,36px)}
@media (max-width:980px){.estgrid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:620px){.estgrid{grid-template-columns:1fr}}
/* position:relative IS LOad-BEARING and was missing until 24 Sep 2026.
   .estcard shares the graph-paper texture block further down this file with
   .benefit-card and .sp. That texture is an absolutely positioned ::before
   with inset:0, so its containing block is the nearest POSITIONED ancestor —
   and .estcard had none. .benefit-card gets one from .card and .sp declares
   its own; this was the one card in the set with neither, so its texture
   resolved against SECTION.sec instead and painted the graph paper, the grid
   lines and the coloured cells across the entire section, nine times over.

   isolation:isolate was there and reads like it should have covered it. It
   does not: it creates a stacking context, which decides PAINT ORDER. It does
   not create a containing block, which is what an absolutely positioned child
   resolves its offsets against. Two different things, and only one of them was
   present.

   overflow:hidden as well, so the texture cannot bleed past the corner radius. */
.estcard{position:relative;overflow:hidden;
  border:1px solid var(--line);border-radius:var(--radius);
  padding:22px 22px 20px;box-shadow:var(--shadow-sm);isolation:isolate;
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.estcard:hover{transform:translateY(-4px);border-color:var(--line-2);box-shadow:var(--shadow)}
/* Its own spread, for the same reason the benefit cards have three: these are
   ~390px rather than 584px, and one set of offsets does not suit both. */
.estcard:nth-child(1)::before{background-position:right 40px top 22px,right 110px top 66px,right 22px top 110px,right 88px top 22px,0 0,0 0}
.estcard:nth-child(2)::before{background-position:right 88px top 44px,right 22px top 88px,right 132px top 22px,right 44px top 110px,0 0,0 0}
.estcard:nth-child(3)::before{background-position:right 22px top 66px,right 110px top 22px,right 66px top 110px,right 132px top 66px,0 0,0 0}

/* THE TWO VERDICT CARDS, 22 Sep. They take the shared paper and texture above;
   what is left is the part that is theirs.

   A RAIL RATHER THAN A WASH, red then blue, which is the site's sequence and
   the same pair the pricing page gives per-incident and per-user. Two cards,
   so the sequence stops at two rather than forcing a green in to complete a
   set that is not there.

   The texture sits behind a 42px number, so it is pulled back to .58 - at full
   strength the filled cells read as marks ON the figure rather than paper
   behind it. Their own positions, because these cards are wider and shorter
   than either of the other two families. */
.sp::after{content:"";position:absolute;top:0;left:0;right:0;height:2px;z-index:1;
  background:var(--red);
  transition:left .3s var(--ease),right .3s var(--ease)}
.sp:nth-child(2)::after{background:var(--blue)}
/* --blue-ink: 10.5px mono on paper needs 4.5 and --blue gives 4.39. The rail
   above it stays --blue, being a rail. */
.sp:nth-child(2) .k{color:var(--blue-ink)}
.sp:hover{transform:translateY(-4px);border-color:var(--line-2);box-shadow:var(--shadow)}
.sp::before{opacity:.58}
.sp:nth-child(1)::before{background-position:right 44px top 22px,right 132px top 66px,right 88px top 110px,right 198px top 44px,0 0,0 0}
.sp:nth-child(2)::before{background-position:right 110px top 44px,right 22px top 88px,right 176px top 22px,right 66px top 110px,0 0,0 0}
@media (prefers-reduced-motion:reduce){.sp,.sp:hover{transition:none;transform:none}}
.estcard h3{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;color:var(--fg-mut);margin:0 0 14px}
.estcard ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:7px}
.estcard li{font-family:var(--display);font-weight:600;font-size:13.5px;letter-spacing:-.005em;
  color:var(--ink);background:var(--mist);border:1px solid var(--line);
  border-radius:100px;padding:6px 13px;line-height:1.3}

/* ---------- integrations: external sources ----------
   Sources the platform READS, not connections into the customer estate, which
   is why they are small cards set apart from the Microsoft list above rather
   than more rows on it. */
.srcgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:16px;
  margin-top:clamp(26px,3vw,36px)}
.src{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:20px 22px;box-shadow:var(--shadow-sm);
  transition:transform .25s var(--ease),border-color .25s,box-shadow .25s}
.src:hover{transform:translateY(-3px);border-color:var(--line-2);box-shadow:var(--shadow)}
.src h3{font-size:15.5px;letter-spacing:-.01em;margin:0 0 7px;line-height:1.3}
.src p{margin:0;color:var(--fg-soft);font-size:13.5px;line-height:1.55}
@media (prefers-reduced-motion:reduce){.src:hover{transform:none}}

/* ---------- the agent roster page ----------
   Fourteen agents is inherently a long page, so each one is a two-column row
   rather than a stack: identity and the quotable line on the left, what it
   does and what it removes on the right. Stacked, the same content ran to
   roughly twice the height.

   `--c` is the squad colour, set per row from the roster. Everything that
   identifies the agent inherits it, so a new squad colour needs no CSS. */
/* The five function groups live in ONE section rather than one section each,
   so the spacing between them is set here instead of by section padding. A
   rule across the top of each group gives the break the tone change used to
   give it, without the seam. */
.agroup + .agroup{margin-top:clamp(54px,7vw,88px);padding-top:clamp(44px,5vw,64px);
  border-top:1px solid var(--line)}

.agentlist{display:grid;gap:18px;margin-top:clamp(26px,3vw,36px)}

.agrow{--c:var(--red);display:grid;grid-template-columns:300px 1fr;gap:32px;
  background:var(--paper);border:1px solid var(--line);border-left:3px solid var(--c);
  border-radius:var(--radius);padding:26px 28px;box-shadow:var(--shadow-sm);
  transition:border-color .25s,box-shadow .25s,transform .25s var(--ease)}
.agrow:hover{transform:translateY(-3px);box-shadow:var(--shadow);border-color:var(--line-2);
  border-left-color:var(--c)}

.ag-id{min-width:0}
.ag-ico{display:grid;place-items:center;width:42px;height:42px;border-radius:11px;
  background:color-mix(in srgb,var(--c) 11%,transparent);
  border:1px solid color-mix(in srgb,var(--c) 26%,transparent);
  color:var(--c);margin-bottom:14px}
.ag-ico svg{width:21px;height:21px}
/* THE CODE BADGE TAKES AN INK VERSION OF ITS AGENT COLOUR, 22 Sep 2026.

   It is 10px mono on a white card and it was painting var(--c) neat - the
   agent's own accent, chosen to read on the dark roster and the console. On
   paper that fails for every light agent colour and badly for the yellow one:
   the SOC Manager's #e5a13a measured 2.21 against a requirement of 4.5, which
   is the least legible text on the site.

   color-mix toward black at 62% keeps each agent's hue exactly - so the badge
   is still recognisably that agent's colour - and takes the lightest of them
   past 4.5. It lands the amber within a shade of --amber-ink, which is the
   value that was hand-picked for this exact problem, so the arithmetic agrees
   with the judgement already recorded in main.css.

   Only the TEXT changes. The rails, fills and icons that use --c neat are not
   text and keep it. */
.ag-code{display:inline-block;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.12em;color:color-mix(in srgb,var(--c) 62%,#000);border:1px solid color-mix(in srgb,var(--c) 32%,transparent);
  border-radius:5px;padding:3px 8px;margin-bottom:10px}
.ag-id h3{font-size:19px;letter-spacing:-.015em;margin:0 0 6px;line-height:1.25}
.ag-role{margin:0;color:var(--fg-mut);font-size:14px;line-height:1.5}
.ag-say{margin:16px 0 0;padding:14px 0 0;border-top:2px solid color-mix(in srgb,var(--c) 30%,transparent);
  font-family:var(--display);font-weight:600;font-size:15px;line-height:1.45;
  letter-spacing:-.01em;color:var(--ink);text-wrap:pretty}

.ag-body{min-width:0}
.ag-does{margin:0;color:var(--fg-soft);font-size:15px;line-height:1.65}
.ag-skills{margin:0;padding:0;display:grid;gap:8px}
.ag-skills li{position:relative;padding-left:18px;font-size:14.5px;color:var(--fg-soft);line-height:1.5}
.ag-skills li::before{content:"";position:absolute;left:0;top:8px;width:6px;height:6px;
  border-radius:50%;background:var(--c)}

/* auto-fit rather than 1fr 1fr: several agents have a "removes" and no
   "costs today", and a fixed two-column grid left those hanging in half a row
   with a gap beside them. One cell now fills the width. */
.ag-pair{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:18px;
  margin-top:18px;padding-top:16px;border-top:1px solid var(--line)}
/* --green-ink: these are 9.5px labels on a white card - "What it takes off
   your desk" and its siblings - and --green measures 3.37 there against the
   4.5 small text needs. The ink variant is the same hue at 4.77. Its cost
   sibling below keeps --red, which is 6.3 on paper and always passed. */
.ag-cell .k{display:block;font-family:var(--mono);font-size:9.5px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--green-ink);font-weight:700;margin-bottom:6px}
.ag-cell.is-cost .k{color:var(--red)}
.ag-cell p{margin:0;color:var(--fg-mut);font-size:13.5px;line-height:1.55}

.ag-worth{margin:16px 0 0;padding:13px 15px;background:var(--mist);border-radius:9px;
  color:var(--fg-mut);font-size:13.5px;line-height:1.6}
.ag-worth .k{display:block;font-family:var(--mono);font-size:9.5px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--fg-mut);font-weight:700;margin-bottom:5px}


@media (max-width:960px){
  .agrow{grid-template-columns:1fr;gap:20px;padding:24px}
  .ag-say{margin-top:14px}
}
@media (max-width:560px){
  .ag-pair{grid-template-columns:1fr;gap:14px}
}
@media (prefers-reduced-motion:reduce){.agrow:hover{transform:none}}

/* ---------- the agent roster page: escalation ---------- */
.esc{list-style:none;margin:clamp(26px,3vw,34px) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
.esc-step{position:relative;overflow:hidden;background:var(--dk-panel);border:1px solid var(--dk-line);
  border-radius:var(--radius);padding:26px 24px 24px}
.esc-step::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;background:var(--seam-grad)}
.esc-k{display:inline-block;font-family:var(--mono);font-size:12px;font-weight:700;
  letter-spacing:.14em;color:var(--red-3);margin-bottom:12px}
.esc-step h3{font-size:17px;color:#fff;letter-spacing:-.01em;margin:0 0 8px;line-height:1.3}
.esc-step p{margin:0;color:var(--dk-mut);font-size:14px;line-height:1.6}
@media (max-width:860px){.esc{grid-template-columns:1fr}}

/* ---------- how it works: the pipeline flow ----------
   Two earlier attempts, and why this is the third. A 3x3 grid of identical
   cards said "nine things" rather than "a sequence". Running all nine down one
   vertical rail said "sequence" but made this one section taller than the rest
   of the page put together.

   This is three phases ACROSS and three stages DOWN each: the macro flow reads
   left to right, the micro flow top to bottom, and the whole thing is about a
   third of the height. The steps are text on a rail rather than cards, because
   nine cards at this density is a wall. */
.flow-head{text-align:center;max-width:60ch;margin:clamp(40px,5vw,60px) auto clamp(22px,2.6vw,30px)}
.flow-head h3{font-size:clamp(20px,2.4vw,25px);letter-spacing:-.018em;margin:0 0 5px}
.flow-head p{margin:0;color:var(--fg-mut);font-size:13.5px}

.flow{display:grid;grid-template-columns:repeat(3,1fr);gap:30px;align-items:stretch;
  max-width:1140px;margin:0 auto}

/* Equal-height columns with the steps distributed evenly down each. Left to
   its own devices the three phases end at three different heights (the copy is
   not the same length), which reads as three lists that happen to sit side by
   side rather than one process in three movements. Grid stretch equalises the
   columns; `flex:1` on each step shares the height between them. */
.fphase{position:relative;min-width:0;display:flex;flex-direction:column}
.fphase-head{padding-bottom:12px;margin-bottom:16px;border-bottom:2px solid var(--red)}
.fphase-head h4{font-family:var(--display);font-weight:600;font-size:15.5px;letter-spacing:-.01em;
  margin:0 0 3px;color:var(--ink);text-transform:none}
.fphase-head p{margin:0;color:var(--fg-mut);font-size:12.5px;line-height:1.45}

/* the chevron in the gutter, carrying the eye from one phase to the next */
@media (min-width:861px){
  .fphase:not(:last-child)::after{content:"";position:absolute;top:9px;right:-20px;
    width:9px;height:9px;border-top:2px solid var(--line-2);border-right:2px solid var(--line-2);
    transform:rotate(45deg)}
}

/* `grid-auto-rows:1fr` rather than flex on the children. Flex `flex:1` shares
   the FREE space, so a step with an extra line (the gate carries a loop
   annotation) still ends up taller than its siblings and the nodes drift out of
   step. Equal grid rows level to the tallest row instead, so every node sits
   the same distance from the last one no matter how much copy a step carries.
   `auto-rows` rather than `template-rows:repeat(3,...)` so a phase with a
   different number of steps still levels correctly. */
.fsteps{list-style:none;margin:0;padding:0;position:relative;flex:1;
  display:grid;grid-auto-rows:1fr}

.fstep{position:relative;display:grid;grid-template-columns:40px 1fr;gap:15px;
  padding-bottom:16px;align-items:start}
.fstep:last-child{padding-bottom:0}
/* The rail is drawn per step, from under one node to the top of the next, so
   it stretches with the even spacing and stops at the last node instead of
   trailing off past it. */
.fstep:not(:last-child)::before{content:"";position:absolute;left:19px;top:42px;bottom:0;
  width:2px;background:var(--line-2);border-radius:2px}

/* The stage markers. They were 32px outlines in muted grey, which read as
   incidental rather than as the spine of the section. Bigger, darker type, and
   a halo in the section colour so the rail is cut cleanly rather than just
   covered. The ring tracks the section background, which is now `--bg`. */
.fs-n{position:relative;z-index:1;display:grid;place-items:center;width:40px;height:40px;
  border-radius:50%;background:var(--paper);border:1.5px solid var(--line-2);
  font-family:var(--mono);font-size:13.5px;font-weight:700;letter-spacing:.01em;
  color:var(--ink);
  box-shadow:0 0 0 4px var(--bg), 0 2px 5px -2px rgba(15,20,35,.18);
  transition:border-color .2s var(--ease),color .2s,box-shadow .2s}
.fstep:hover .fs-n{border-color:var(--red);color:var(--red);
  box-shadow:0 0 0 4px var(--bg), 0 4px 10px -3px var(--red-glow)}

.fs-body{min-width:0;padding-top:3px}
.fs-body h5{font-family:var(--display);font-weight:600;font-size:15.5px;letter-spacing:-.01em;
  margin:0 0 4px;color:var(--ink);text-transform:none}
.fs-body p{margin:0;color:var(--fg-soft);font-size:13.5px;line-height:1.55}

/* the completeness gate: the only stage that can send the work backwards, and
   the one with its own section further down. It earns the accent. */
.fstep.is-gate .fs-n{background:var(--red);border-color:var(--red);color:#fff;
  box-shadow:0 0 0 4px var(--red-wash), 0 3px 10px -3px var(--red-glow)}
.fstep.is-gate:hover .fs-n{color:#fff;border-color:var(--red);
  box-shadow:0 0 0 5px var(--red-wash), 0 5px 14px -3px var(--red-glow)}
.fstep.is-gate .fs-body h5{color:var(--red)}
.fs-loop{display:flex;align-items:flex-start;gap:7px;margin:8px 0 0 !important;
  font-family:var(--mono);font-size:10.5px;letter-spacing:.02em;line-height:1.4;
  color:var(--red) !important}
.fs-loop svg{width:13px;height:13px;flex:none;margin-top:1px}

.flownote{max-width:74ch;margin:clamp(26px,3vw,34px) auto 0;text-align:center;
  color:var(--fg-mut);font-size:13px;line-height:1.6}
.flownote b{color:var(--ink);font-weight:600}

@media (max-width:860px){
  .flow{grid-template-columns:1fr;gap:26px}
  /* Levelling the rows only earns its keep when the phases sit side by side
     and the nodes have to line up across them. Stacked in one column it just
     pads every step out to the height of the tallest, which on a phone is the
     gate with its loop annotation wrapped over several lines. */
  .fsteps{grid-auto-rows:auto}
}
@media (max-width:620px){
  .fstep{grid-template-columns:34px 1fr;gap:12px}
  .fs-n{width:34px;height:34px;font-size:12px}
  .fstep:not(:last-child)::before{left:16px;top:36px}
}

/* ---------- how it works: procedure resolution note ---------- */
.prenote{position:relative;overflow:hidden;max-width:74ch;margin:clamp(28px,4vw,40px) auto 0;background:var(--paper);
  border:1px solid var(--line);border-radius:0 var(--radius) var(--radius) 0;
  padding:20px 24px 20px 27px}
.prenote::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--seam-grad-v)}
.prenote .k{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red);font-weight:700;margin-bottom:8px}
.prenote p{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.65}

/* ---------- how it works: the gate's two design choices ----------
   Dark-section pair, sitting under the three gate steps. Two rather than a
   grid of four: these are the two questions a technical buyer actually probes
   for, and padding the row out would bury them. */
.gatechoices{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px;margin-top:26px}
.gc{position:relative;overflow:hidden;background:rgba(255,255,255,.04);border:1px solid var(--dk-line);
  border-radius:var(--radius);padding:24px 24px 22px}
.gc::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;background:var(--seam-grad)}
.gc h3{font-size:16px;color:#fff;letter-spacing:-.01em;margin:0 0 9px;line-height:1.35}
.gc p{margin:0;color:var(--dk-mut);font-size:14px;line-height:1.6}

/* ---------- how it works: the one pull quote on the page ---------- */
/* No max-width. The red rule is a border on this element so it spans the full
   column; capping the text at 46ch left a full-width rule over a short line,
   which reads as a mistake rather than as a measure. */
/* The rule is a divider between two blocks of text, so the space above it and
   the space below it have to match. They were 24px above and 18px below, which
   is close enough to look like a mistake rather than a choice: the line read as
   belonging to the list above it instead of sitting between the two. */
.pullquote{font-family:var(--display);font-weight:600;font-size:19px;line-height:1.4;
  letter-spacing:-.015em;color:var(--ink);border-top:2px solid var(--red);
  border-image:var(--seam-grad) 1;
  padding-top:28px;margin:28px 0 0;text-wrap:pretty}

/* ---------- how it works: the audit record ----------
   A key/description pair list. Deliberately not cards: these are four facets
   of one record, and four boxes would read as four separate things. */
.reclist{margin:0;display:grid}
.rec{display:grid;grid-template-columns:148px 1fr;gap:6px 20px;padding:15px 0;border-top:1px solid var(--line)}
.rec:first-child{border-top:0;padding-top:0}
.rec dt{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--red);padding-top:5px}
/* Sized to the body copy it sits beside. It was 14.5px in a two-column layout
   whose other column runs at 16-17.5px, so the record list read as small print
   next to the prose it is supposed to be equal to - on every page that uses it,
   not just the dark one where it was first noticed. */
.rec dd{margin:0;color:var(--fg-soft);font-size:15.5px;line-height:1.65}
@media (max-width:640px){.rec{grid-template-columns:1fr;gap:3px}.rec dt{padding-top:0}}
/* Dark variant. The record list was built for light sections and had never been
   used on a dark one until the AI monitoring page: --red on near-black is close
   to unreadable for the labels, --fg-soft is a dark grey on a dark ground, and
   --line borders vanish entirely. Every colour in it is a light-page token, so
   the whole component needs restating rather than nudging. */
.sec-dark .rec{border-top-color:rgba(255,255,255,.10)}
.sec-dark .rec dt{color:var(--red-3)}
/* A touch larger again on dark. Light text on a dark ground renders optically
   thinner than the same size dark-on-light, so matching the number does not
   match the readability. */
.sec-dark .rec dd{color:var(--dk-soft);font-size:16.5px;line-height:1.66}
.sec-dark .rec dt{color:var(--red-3);font-size:11px}

/* ---------- homepage: the two verified clocks ---------- */
.clockrow{display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:820px;margin:0 auto}
/* A diagonal red-to-white wash used to fill these. It read as a faded sticker
   rather than a measurement, and the tint fought the numeral, which is the only
   thing on the card worth looking at. Same treatment as `.conv-hub` instead: a
   white card with a crisp red rule across the top. The colour marks the card,
   it does not wash it. */
/* overflow:hidden is load-bearing: without it the ::before rule below is a
   square 2px bar sitting across a 22px corner curve, and it visibly overhangs
   both ends of the card. */
.clock{position:relative;overflow:hidden;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius-lg);padding:clamp(30px,3vw,38px) clamp(26px,3vw,34px);
  text-align:center;display:flex;flex-direction:column;gap:6px;
  box-shadow:var(--shadow-sm)}
.clock::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--seam-grad)}
.clock .k{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--red)}
.clock .v{font-family:var(--display);font-weight:800;font-size:clamp(38px,5vw,56px);line-height:1;
  letter-spacing:-.035em;color:var(--ink);font-variant-numeric:tabular-nums}
.clock .d{font-size:13.5px;color:var(--fg-soft);line-height:1.5;margin-top:6px}
@media (max-width:640px){.clockrow{grid-template-columns:1fr}}

/* ---------- /microsoft-mssp/ ----------
   The Microsoft hub. Reuses .path for the three routes, so only the pieces
   that do not already exist are defined here. */
.paths-3{grid-template-columns:repeat(3,1fr)}
@media (max-width:900px){.paths-3{grid-template-columns:1fr}}
.path .dk{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red);display:block;margin-bottom:12px}
.path .dl{color:var(--fg-soft);font-size:15px;line-height:1.6;margin:0 0 18px}
.path .soon{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--fg-mut);border:1px dashed var(--line-2);border-radius:6px;padding:5px 10px;align-self:flex-start}

/* the partner badge, sitting on the dark hero */
.mspartner{display:block;width:clamp(200px,22vw,268px);margin-left:auto;border-radius:12px;
  box-shadow:var(--shadow);transition:transform .25s ease}
.mspartner:hover{transform:translateY(-3px)}
.mspartner img{display:block;width:100%;height:auto;border-radius:12px;background:var(--paper)}
/* The badge is right-aligned in a hero, where it sits in the second column.
   In a left-hand copy column it has to start at the text edge instead. */
.mspartner.is-left{margin-left:0}
@media (max-width:900px){.mspartner{margin:0 auto}.mspartner.is-left{margin:0 auto}}

/* The definition. Previously a grey box floating in a wide empty row; now a
   full-width statement with the question set as a label above it. */
/* No tint and no border, same call as .proof: the gradient was a third light
   tone, and a rule across the page just draws the join instead of shading it.
   The mono label above the statement is what marks this as a deliberate band. */
.defband{}
.def{max-width:1000px;margin:0 auto;text-align:center}
.def-q{font-family:var(--mono);font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--red);font-weight:700;margin:0 0 18px}
.def-a{margin:0 auto;font-family:var(--display);font-weight:500;
  font-size:clamp(19px,2.4vw,26px);line-height:1.45;letter-spacing:-.014em;color:var(--ink);
  max-width:46ch}
.def-a strong{font-weight:700}
@media (max-width:640px){.def{text-align:left}}

/* The four specialisms. A row of ticks read as a shopping list; these are
   credentials, so they get numbered cards and the product area each covers. */
.specs{list-style:none;margin:30px 0 0;padding:0;display:grid;
  grid-template-columns:repeat(4,1fr);gap:14px}
@media (max-width:1000px){.specs{grid-template-columns:1fr 1fr}}
@media (max-width:560px){.specs{grid-template-columns:1fr}}
.spec{position:relative;background:var(--paper);border:1px solid var(--line);border-radius:14px;
  padding:22px 20px 20px;box-shadow:var(--shadow-sm);overflow:hidden;
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.spec::before{content:"";position:absolute;inset:0 0 auto 0;height:3px;
  background:linear-gradient(90deg,var(--red),rgba(195,0,23,0))}
.spec:hover{transform:translateY(-4px);border-color:var(--line-2);box-shadow:var(--shadow)}
.sp-n{display:block;font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.12em;
  color:var(--red);margin-bottom:12px}
.sp-name{display:block;font-family:var(--display);font-weight:600;font-size:16.5px;
  line-height:1.3;letter-spacing:-.012em;color:var(--ink);margin-bottom:9px}
.sp-area{display:block;font-size:13.5px;line-height:1.5;color:var(--fg-mut)}

.fineprint{margin:26px 0 0;color:var(--fg-mut);font-size:14.5px;max-width:68ch}

.workshops{display:grid;grid-template-columns:repeat(3,1fr);grid-auto-rows:1fr;gap:16px}
@media (max-width:900px){.workshops{grid-template-columns:1fr 1fr}}
@media (max-width:600px){.workshops{grid-template-columns:1fr}}
.ws{position:relative;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius);padding:24px 24px 20px;box-shadow:var(--shadow-sm);
  display:flex;flex-direction:column;overflow:hidden;
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.ws::after{content:"";position:absolute;left:0;right:0;bottom:0;height:3px;
  background:linear-gradient(90deg,var(--red),rgba(195,0,23,0));
  transform:scaleX(0);transform-origin:left;transition:transform .35s var(--ease)}
.ws:hover{transform:translateY(-5px);border-color:var(--line-2);box-shadow:var(--shadow)}
.ws:hover::after{transform:scaleX(1)}
.ws-n{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.12em;
  color:var(--red);margin-bottom:14px}
.ws h3{font-size:17px;line-height:1.3;letter-spacing:-.014em;margin:0 0 9px}
.ws p{margin:0 0 18px;color:var(--fg-mut);font-size:14.5px;line-height:1.55}
.ws-foot{margin-top:auto;font-family:var(--mono);font-size:10px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--fg-mut);padding-top:14px;border-top:1px solid var(--line)}

.covgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:28px}
@media (max-width:900px){.covgrid{grid-template-columns:1fr 1fr}}
@media (max-width:560px){.covgrid{grid-template-columns:1fr}}
.cov h3{font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--red);margin:0 0 14px;font-weight:700}
.cov ul{list-style:none;margin:0;padding:0;display:grid;gap:9px}
.cov li{font-size:15px;color:var(--fg-soft);line-height:1.45}
.cov li a{color:var(--ink);border-bottom:1px solid var(--line-2)}
.cov li a:hover{color:var(--red);border-bottom-color:currentColor}

/* Long-form sections.
   The heading sits in its own column beside the body rather than centred above
   a narrow one, so the two share a left axis and the section fills its width. */
.splitrow{display:grid;grid-template-columns:minmax(300px,.85fr) minmax(0,1.15fr);
  gap:clamp(36px,6vw,84px);align-items:start;max-width:1180px;margin:0 auto}
@media (max-width:940px){.splitrow{grid-template-columns:1fr;gap:26px}}
.sp-head{position:sticky;top:96px}
@media (max-width:940px){.sp-head{position:static}}
.sp-head h2{font-size:clamp(28px,3.4vw,40px);line-height:1.1;letter-spacing:-.025em;margin:16px 0 16px}
.sp-head .lede{margin:0;font-size:clamp(16px,1.8vw,18px);max-width:42ch}
/* Body copy in the right-hand column. The pull quote is excluded: it is a
   separate component with its own type, its own spacing and a red rule that is
   meant to span the FULL column, and `.sp-body p` outranks `.pullquote` on
   specificity - so without this it was silently overriding the quote's top
   margin (pulling the rule up against the text above) and capping it at 68ch
   (shortening the rule), both of which the quote's own rule had tried to set. */
.sp-body p:not(.pullquote){color:var(--fg-soft);font-size:clamp(16px,1.75vw,17.5px);line-height:1.75;margin:0 0 20px;max-width:68ch}
/* Trailing space at the foot of the column, removed - but NOT from the pull
   quote. That rule zeroes `margin` outright, which beat .pullquote on
   specificity and killed its TOP margin too, pulling the red rule up tight
   against whatever sat above it. The quote sets its own bottom margin to 0, so
   excluding it here costs nothing. */
.sp-body p:last-child:not(.pullquote){margin:0}
/* Paragraphs here are uniform. A first-paragraph size bump was tried and read
   as an accident rather than an editorial lead: too small a jump to look
   deliberate, too large to go unnoticed. */
.sec-dark .sp-body p:not(.pullquote){color:var(--dk-soft)}

/* the two published clocks: the page needed one thing to look at */
.clocks{display:flex;gap:14px;margin:28px 0 0;flex-wrap:wrap}
.clk{background:rgba(255,255,255,.05);border:1px solid var(--dk-line-2);border-radius:12px;padding:16px 20px;flex:1 1 150px}
.clk .v{display:block;font-family:var(--display);font-weight:700;font-size:clamp(24px,3vw,32px);
  letter-spacing:-.02em;color:#fff;line-height:1}
.clk .k{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--dk-mut);margin-top:8px}

.socshot{display:block;width:100%;height:auto;border-radius:14px;margin:0 0 26px;
  border:1px solid var(--dk-line-2)}

.ticks{list-style:none;margin:26px 0 0;padding:0;display:grid;gap:11px}
.ticks li{display:flex;gap:12px;align-items:flex-start;font-size:15.5px;line-height:1.5;color:var(--ink)}
.ticks svg{width:18px;height:18px;flex:none;color:var(--red);margin-top:3px}

/* build or buy: a comparison reads faster than three more paragraphs */
.tradeoff{display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:1000px;margin:0 auto}
@media (max-width:760px){.tradeoff{grid-template-columns:1fr}}
.tr-col{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);padding:26px 28px}
.tr-col h3{font-size:18px;margin:0 0 18px}
.tr-col ul{list-style:none;margin:0;padding:0;display:grid;gap:12px}
.tr-col li{position:relative;padding-left:26px;font-size:15px;line-height:1.55;color:var(--fg-soft)}
.tr-col li::before{position:absolute;left:0;top:-1px;font-size:16px;line-height:1.5}
.tr-a li::before{content:"✕";color:var(--fg-mut)}
.tr-b li::before{content:"✓";color:var(--red);font-weight:700}
.tr-win{border-color:rgba(195,0,23,.32);box-shadow:var(--shadow-sm)}
.tr-win li{color:var(--ink)}

.longform{max-width:72ch}
.longform p{color:var(--fg-soft);font-size:16.5px;line-height:1.72;margin:0 0 18px}
.longform p:last-child{margin:0}

/* Prose that follows the comparison shares its 1000px container so the two
   line up on the same left edge. Set in two columns: a single column either
   left half the row empty or ran to ~110 characters a line, which is past the
   point where the eye reliably finds the start of the next one. */
.longform-tr{max-width:1000px;margin:34px auto 0;columns:2;column-gap:clamp(32px,4vw,60px)}
.longform-tr p{max-width:none;margin:0 0 18px;orphans:2;widows:2}
.longform-tr p:last-child{margin-bottom:0}
@media (max-width:760px){.longform-tr{columns:1}.longform-tr p{max-width:68ch}}

/* the five Solutions Partner designations; Security leads at a larger size */
.deshead{font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--fg-mut);font-weight:700;margin:38px 0 16px}
.desrow{display:flex;flex-wrap:wrap;align-items:flex-end;gap:16px}
.des{background:var(--paper);border:1px solid var(--line);border-radius:10px;padding:10px;
  box-shadow:var(--shadow-sm);flex:0 0 auto}
.des img{display:block;width:118px;height:auto;border-radius:5px}
.des.lead img{width:158px}
@media (max-width:560px){.des img{width:96px}.des.lead img{width:124px}}

/* Index and article mastheads: the same dark treatment as every other hero,
   sized for a heading rather than a full hero layout. */
.listhero,.arthero{padding:clamp(48px,7vw,86px) 0 clamp(40px,6vw,68px)}
.listhero .sec-head{margin-bottom:0}
.arthero .crumbs{margin:0 0 18px}
.arthero .crumbs a{color:var(--dk-soft)}
.arthero .crumbs a:hover{color:#fff}
.arthero .crumbs .muted{color:var(--dk-mut)}
.arthero .kicker{color:var(--red-3)}
.arthero h1{color:#fff;max-width:22ch;margin:10px 0 20px}
.arthero .meta{display:flex;flex-wrap:wrap;align-items:center;gap:10px 18px;color:var(--dk-mut);font-size:14px}
.arthero .meta strong{color:#fff}

/* ---------- /services/ot-security/ ---------- */
.domgrid{display:grid;grid-template-columns:repeat(3,1fr);grid-auto-rows:1fr;gap:20px}
@media (max-width:900px){.domgrid{grid-template-columns:1fr}}
.dom{position:relative;background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:28px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.dom:hover{transform:translateY(-5px);border-color:var(--line-2);box-shadow:var(--shadow)}
.dom .di{width:26px;height:26px;color:var(--red);margin-bottom:16px}
.dom h3{font-size:19px;margin:0 0 6px}
.dom .who{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--fg-mut);margin:0 0 14px}
.dom p{color:var(--fg-soft);font-size:15px;line-height:1.6;margin:0 0 18px}
.dom .fw{list-style:none;margin:auto 0 0;padding:14px 0 0;border-top:1px solid var(--line);
  display:flex;flex-wrap:wrap;gap:7px}
.dom .fw li{font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;color:var(--fg-mut);
  border:1px solid var(--line-2);border-radius:20px;padding:4px 10px}
.sec-dark .ticks li{color:var(--dk-soft)}
.sec-dark .ticks svg{color:var(--red-3)}

/* ---------- named-contact CTA ---------- */
/* The closing CTA, with the dark footer directly beneath it.
   The gradient used to run --bg-2 -> --bg, which did two things wrong: it
   stepped down ten points at the top edge, and it LIGHTENED on the way into a
   black footer, which is the wrong way round for grounding a page.

   It starts at --bg and darkens to --bg-2. Every light section on the site is
   now --bg, including the FAQ partial immediately above this, so the top edge
   is an exact colour match and the seam disappears. It then darkens downward to
   meet the footer.

   No border-top: with the tones matching exactly, a hairline would be the only
   thing visible at the join and would read as a stray rule.

   If a page is ever built that puts this CTA after something other than the
   FAQ, check the join - the top of this gradient is pinned to mist. */
/* No background. It was a --bg -> --bg-2 gradient, which is the same tonal
   banding in gradient form: the closing band arrived a shade darker than the
   page above it for no reason a reader could name. Transparent means the body
   base shows through and the whole light page is one colour. */
.personcta{}
.pc{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(30px,5vw,64px);align-items:center;
  max-width:1120px;margin:0 auto}
@media (max-width:860px){.pc{grid-template-columns:1fr;gap:26px}}
.pc-copy h2{font-size:clamp(28px,3.6vw,42px);line-height:1.1;letter-spacing:-.025em;margin:14px 0 0}
.pc-copy .lede{max-width:52ch}
.pc-face{position:relative;justify-self:center}
.pc-face img{display:block;width:clamp(240px,26vw,340px);height:auto}
.pc-name{position:absolute;right:0;bottom:6px;display:flex;flex-direction:column;gap:2px;
  background:var(--paper);border:1px solid var(--line);border-radius:12px;padding:10px 14px;
  box-shadow:var(--shadow-sm);font-size:12.5px;color:var(--fg-mut);line-height:1.35}
.pc-name strong{color:var(--ink);font-size:14.5px;font-weight:600}
@media (max-width:860px){.pc-name{position:static;margin:14px auto 0;width:max-content;text-align:center;align-items:center}}

/* generic variant: no face column, so the copy centres in the band */
.personcta.is-generic .pc{grid-template-columns:1fr;max-width:760px;text-align:center}
.personcta.is-generic .pc-copy .lede{max-width:none;margin-inline:auto}
.personcta.is-generic .btnrow{justify-content:center}

/* photo variant of the CTA */
.pc-shot{margin:0;justify-self:end;max-width:520px}
.pc-shot img{display:block;width:100%;height:auto;border-radius:var(--radius-lg);
  border:1px solid var(--line);box-shadow:var(--shadow)}
.pc-shot figcaption{margin:12px 2px 0;font-family:var(--mono);font-size:11px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--fg-mut)}
@media (max-width:860px){.pc-shot{justify-self:center;max-width:100%}}

/* A cut-out stands ON the band: no frame, and his feet meet the bottom edge
   rather than floating in the middle of it. The section drops its bottom
   padding and the copy column carries its own, so only the figure runs out. */
.pc-shot.is-cutout img{border:0;box-shadow:none;border-radius:0;
  width:auto;max-width:100%;max-height:430px;margin-inline:auto;vertical-align:bottom}
.pc-shot.is-cutout{justify-self:center;max-width:none;align-self:end;line-height:0}
.personcta:has(.is-cutout){padding-bottom:0}
.personcta:has(.is-cutout) .pc{align-items:end}
.personcta:has(.is-cutout) .pc-copy{padding-bottom:clamp(56px,7vw,96px)}
@media (max-width:860px){
  .pc-shot.is-cutout img{max-height:320px}
  .personcta:has(.is-cutout) .pc-copy{padding-bottom:0}
}

/* ---------- OT: four-step process ---------- */
.steprow{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,1fr);gap:22px}
@media (max-width:1000px){.steprow{grid-template-columns:repeat(2,1fr)}}
@media (max-width:620px){.steprow{grid-template-columns:1fr}}
.stepcard{position:relative;padding:26px 22px 24px;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius);box-shadow:var(--shadow-sm);
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.stepcard:hover{transform:translateY(-5px);border-color:var(--line-2);box-shadow:var(--shadow)}
/* The number badge runs through the site's FOUR colours rather than five
   identical red discs. A sequence of steps is exactly the place for a sequence
   of colours, and it gives somebody a way to say "the amber one" out loud.

   AMBER JOINED THE CYCLE 23 Sep 2026, on Adam looking at the four pricing
   steps: "make onboarding yellow that way we use all 4 colours, same colour as
   we do the SOC manager agent." A four-step sequence running red, blue, green,
   RED was the defect — the fourth step read as a repeat of the first. Red,
   blue, green and amber is the site's own four-square, already documented in
   agents.js, and the SOC Manager agent is the amber one.

   AMBER TAKES DARK INK, and this is not a style choice. White on --amber
   measures 2.2:1, which fails outright for a 13px bold numeral; --ink on the
   same disc is 8.8:1. The other three discs keep white because they pass on
   it. One colour in a set behaving differently is worth it when the
   alternative is one colour in a set being unreadable. */
.stepcard .sn{display:inline-grid;place-items:center;width:38px;height:38px;border-radius:50%;
  background:var(--red);color:#fff;font-family:var(--mono);font-size:13px;font-weight:700;
  letter-spacing:.02em;margin-bottom:16px;
  box-shadow:0 0 0 0 var(--red-glow);
  transition:box-shadow .3s var(--ease),transform .3s var(--ease)}
.stepcard:nth-child(2) .sn{background:var(--blue)}
.stepcard:nth-child(3) .sn{background:var(--green)}
.stepcard:nth-child(4) .sn{background:var(--amber);color:var(--ink)}
.stepcard:nth-child(5) .sn{background:var(--red)}
/* A ring rather than a lift on the badge: the card already moves, and two
   things moving at once reads as a wobble. */
.stepcard:hover .sn{box-shadow:0 0 0 6px color-mix(in srgb,var(--red) 12%,transparent)}
.stepcard:nth-child(2):hover .sn{box-shadow:0 0 0 6px color-mix(in srgb,var(--blue) 14%,transparent)}
.stepcard:nth-child(3):hover .sn{box-shadow:0 0 0 6px color-mix(in srgb,var(--green) 14%,transparent)}
.stepcard:nth-child(4):hover .sn{box-shadow:0 0 0 6px color-mix(in srgb,var(--amber) 22%,transparent)}
.stepcard:nth-child(5):hover .sn{box-shadow:0 0 0 6px color-mix(in srgb,var(--red) 12%,transparent)}
@media (prefers-reduced-motion:reduce){
  .stepcard .sn,.stepcard:hover .sn{transition:none;box-shadow:none}
}
.stepcard h3{font-size:18px;margin:0 0 9px}
.stepcard p{color:var(--fg-soft);font-size:14.5px;line-height:1.6;margin:0}
/* the connector between steps, on wide screens only */
@media (min-width:1001px){
  .stepcard::after{content:"";position:absolute;top:45px;right:-15px;width:12px;height:1.5px;
    background:var(--line-2)}
  .stepcard:last-child::after{display:none}
}
/* A six-step variant, three across. The column count has to be restated at
   every breakpoint because `.steprow.is-3` outranks the plain `.steprow` rules
   above wherever they sit, media query or not. The connector is dropped at the
   end of each row: a line pointing into the gutter reads as a broken step. */
.steprow.is-3{grid-template-columns:repeat(3,1fr)}
@media (max-width:1000px){.steprow.is-3{grid-template-columns:repeat(2,1fr)}}
@media (max-width:620px){.steprow.is-3{grid-template-columns:1fr}}
@media (min-width:1001px){.steprow.is-3 .stepcard:nth-child(3n)::after{display:none}}

/* DARK-SECTION VARIANT, and it was a real bug rather than a preference.
   .stepcard is a light-section component: it paints var(--paper) and lets the
   heading inherit. main.css sets `.sec-dark h1,h2,h3,h4{color:#fff}`, so a
   .stepcard dropped into a dark section rendered a WHITE card with a WHITE,
   invisible title - which is why it read as a number, a gap, then body text.
   The panel treatment matches .splitcol and .esc-step, which are dark-native. */
.sec-dark .stepcard{background:var(--dk-panel);border-color:var(--dk-line);box-shadow:none}
.sec-dark .stepcard:hover{border-color:var(--dk-line-2);
  box-shadow:0 20px 44px -26px rgba(0,0,0,.85)}
.sec-dark .stepcard h3{color:#fff}
.sec-dark .stepcard p{color:var(--dk-soft)}
.sec-dark .stepcard::after{background:var(--dk-line-2)}

/* A five-step variant, five across. Five steps in the default four-column grid
   leaves one card alone on a second row, which reads as an afterthought rather
   than as the fifth movement of a sequence. Column counts are restated at every
   breakpoint for the same reason .is-3 restates them: `.steprow.is-5` outranks
   the plain `.steprow` rules wherever they sit, media query or not.
   Padding tightens at five across because the columns are ~224px inside a
   1240px wrap, and the default 26/22 leaves too little room for the copy. */
.steprow.is-5{grid-template-columns:repeat(5,1fr);gap:16px}
.steprow.is-5 .stepcard{padding:22px 18px 20px}
.steprow.is-5 .stepcard .sn{width:34px;height:34px;font-size:12px;margin-bottom:13px}
.steprow.is-5 .stepcard h3{font-size:16.5px}
.steprow.is-5 .stepcard p{font-size:13.5px}
@media (max-width:1180px){
  .steprow.is-5{grid-template-columns:repeat(3,1fr);gap:22px}
  .steprow.is-5 .stepcard{padding:26px 22px 24px}
  .steprow.is-5 .stepcard h3{font-size:18px}
  .steprow.is-5 .stepcard p{font-size:14.5px}
  /* a connector pointing into the gutter reads as a broken step */
  .steprow.is-5 .stepcard:nth-child(3n)::after{display:none}
}
@media (max-width:1000px){.steprow.is-5{grid-template-columns:repeat(2,1fr)}}
@media (max-width:620px){.steprow.is-5{grid-template-columns:1fr}}

/* ---------- OT: capability cards ---------- */
.capgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:22px}
@media (max-width:1000px){.capgrid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:620px){.capgrid{grid-template-columns:1fr}}
/* ICON INLINE WITH THE HEADING, not stacked above it.
   Stacked, the 46px icon plus its 18px margin left roughly 50px of dead space
   between the glyph and the label in every single card. Over nine cards and
   three rows that is a lot of empty page, and it made the grid read as airy
   and unfinished rather than dense. The icon IS the label's marker, so the two
   belong on one line.

   Done as a two-column grid over the EXISTING markup (span.ico, h3, p), which
   is identical on all four .capgrid pages - pentest, sentinel, ot-security and
   careers - so no template changes and every one of them improves at once.
   The paragraph spans both columns so the body copy keeps full card width
   instead of being indented into a narrow channel beside the icon. */
.capgrid .card{display:grid;grid-template-columns:auto 1fr;column-gap:14px;
  align-content:start;padding:26px 24px}
.capgrid .card .ico{grid-row:1;grid-column:1;align-self:center;margin:0;
  width:40px;height:40px;border-radius:11px;
  transition:background .3s var(--ease),color .3s,transform .35s var(--ease),border-color .3s}
.capgrid .card .ico svg{width:21px;height:21px}
.capgrid .card h3{grid-row:1;grid-column:2;align-self:center;margin:0;
  font-size:17.5px;letter-spacing:-.012em;line-height:1.28}
.capgrid .card p{grid-row:2;grid-column:1/-1;margin:13px 0 0;
  font-size:14.5px;line-height:1.62}

/* The accent bar and the icon fill, borrowed from `.baseline .card` on the home
   page rather than invented: it is the treatment this site already uses to stop
   a card grid reading as a generic feature wall, and reusing it keeps the two
   grids looking like the same product. */
.capgrid .card::after{content:"";position:absolute;top:0;left:0;right:0;height:2px;
  background:linear-gradient(90deg,var(--red),var(--red-3) 40%,transparent 85%);
  transform:scaleX(0);transform-origin:left;transition:transform .4s var(--ease)}
.capgrid .card:hover::after{transform:scaleX(1)}
.capgrid .card:hover .ico{background:var(--red);border-color:var(--red);color:#fff;
  transform:scale(1.06) rotate(-4deg)}
@media (prefers-reduced-motion:reduce){
  .capgrid .card:hover .ico{transform:none}
  .capgrid .card::after{transition:none}
}

/* Jurisdiction tag on the compliance framework cards. Inline with the copy
   rather than on its own row, because "EU, in force" or "US, customer-driven"
   is part of the sentence a reader needs rather than a badge to scan past:
   which standard applies to you is decided by geography and by who is asking. */
.fw-tag{font-family:var(--mono);font-size:10.5px;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--red);margin-right:9px}
.sec-dark .fw-tag{color:var(--red-3)}

/* ---------- OT: proof block ----------
   Engagement card on the left, credential rail on the right. Both columns are
   independent, so the rail keeps its rhythm whatever length the card runs to. */
.otproof{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(22px,3vw,34px);align-items:start}
@media (max-width:940px){.otproof{grid-template-columns:1fr}}

.csnap{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:clamp(26px,3vw,38px);box-shadow:var(--shadow-sm)}
.cs-tag{display:inline-flex;align-items:center;gap:8px;font-family:var(--mono);font-size:11px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--red);font-weight:700;margin-bottom:16px}
.cs-tag .dot{width:7px;height:7px;border-radius:50%;background:var(--red);
  box-shadow:0 0 0 3px var(--red-wash)}
.csnap h3{font-size:23px;margin:0 0 14px}
.csnap p{color:var(--fg-soft);font-size:15.5px;line-height:1.65;margin:0 0 14px}
/* scoped past `.csnap p`, which would otherwise win on source order */
.csnap .cs-note{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--fg-mut);margin:24px 0 12px;padding-top:20px;border-top:1px solid var(--line)}
.cs-nums{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
@media (max-width:520px){.cs-nums{grid-template-columns:1fr;gap:14px}}
.cs-num{display:flex;flex-direction:column;gap:5px}
.cs-num .v{font-size:29px;font-weight:700;line-height:1;letter-spacing:-.02em;color:var(--red);
  font-variant-numeric:tabular-nums}
.cs-num .k{font-size:13px;line-height:1.4;color:var(--fg-mut)}

.credrail{display:grid;gap:12px;align-content:start}
.cred{display:flex;gap:14px;align-items:flex-start;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius);padding:18px 20px;box-shadow:var(--shadow-sm);
  transition:transform .3s var(--ease),border-color .3s}
.cred:hover{transform:translateX(3px);border-color:var(--line-2)}
.cr-ico{flex:none;width:38px;height:38px;border-radius:10px;display:grid;place-items:center;
  background:var(--red-wash);border:1px solid var(--line);color:var(--red)}
.cr-ico svg{width:19px;height:19px}
.cr-txt h4{font-size:15.5px;margin:0 0 4px;line-height:1.3}
.cr-txt p{color:var(--fg-mut);font-size:13.5px;line-height:1.5;margin:0}

/* ---------- /services/ot-security/managed-ot-iot-soc/ ---------- */

/* breadcrumb sits in a dark hero here, so it needs the dark palette. The base
   .crumbs in main.css is written for light sections. */
.hero .crumbs{color:var(--dk-mut)}
.hero .crumbs a{color:var(--dk-soft)}
.hero .crumbs a:hover{color:#fff}
.hero .crumbs .muted{color:var(--dk-mut)}

/* what's included: two columns of numbered rows, not cards. Eight cards would
   read as a feature dump; rows read as a service definition. */
.incgrid{display:grid;grid-template-columns:1fr 1fr;gap:26px 44px}
@media (max-width:860px){.incgrid{grid-template-columns:1fr;gap:24px}}
.incitem{display:flex;gap:18px;align-items:flex-start}
.incitem .inum{flex:none;width:32px;height:32px;border-radius:9px;display:grid;place-items:center;
  background:var(--red-wash);border:1px solid var(--line);color:var(--red);
  font-family:var(--mono);font-size:12px;font-weight:700}
.incitem h3{font-size:17.5px;margin:4px 0 8px;line-height:1.3}
.incitem p{color:var(--fg-soft);font-size:15px;line-height:1.62;margin:0}

/* ---------- the Purdue stack ----------
   A schematic, not a list. Dark, because this is the technical centre of the
   page and the site already uses dark as its accent for the SOC feed and the
   agent showcase; on a light band six coloured rows read as a bullet list.

   HTML and CSS rather than an image, so it stays sharp at any zoom, reflows on
   a phone and can be corrected without opening a design tool. */
.purdue{margin:38px 0 0}
.pd-panel{position:relative;background:linear-gradient(160deg,var(--dk-panel),var(--dk));
  border:1px solid var(--dk-line-2);border-radius:var(--radius-lg);
  padding:clamp(16px,2vw,24px);overflow:hidden;box-shadow:var(--shadow)}
/* faint blueprint grid, so the ground reads as drawing paper rather than a card */
.pd-panel::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.45;
  background-image:linear-gradient(rgba(255,255,255,.045) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(255,255,255,.045) 1px,transparent 1px);
  background-size:40px 40px}
.pd-panel>*{position:relative}

.pd-head{display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:0 2px 14px;margin-bottom:14px;border-bottom:1px solid var(--dk-line)}
.pd-brand{display:flex;align-items:center;gap:9px;font-family:var(--mono);font-size:11px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--dk-soft)}
.pd-dot{width:7px;height:7px;border-radius:50%;background:var(--red-3);
  box-shadow:0 0 0 3px rgba(255,59,82,.22)}
.pd-mode{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--dk-mut);border:1px solid var(--dk-line-2);border-radius:20px;padding:4px 11px}

/* zone brackets | levels | observation rail */
.pd-diagram{display:grid;grid-template-columns:30px 14px 1fr 226px;gap:6px 12px;align-items:stretch}
@media (max-width:1040px){.pd-diagram{grid-template-columns:26px 14px 1fr}}
@media (max-width:620px){.pd-diagram{grid-template-columns:1fr;gap:8px}}

/* --- zone brackets. These are what make a Purdue drawing read as one. --- */
.pd-zone{grid-column:1;position:relative;display:grid;place-items:center;
  border-left:2px solid var(--zc);border-radius:3px 0 0 3px}
.pd-zone::before,.pd-zone::after{content:"";position:absolute;left:-2px;width:9px;height:2px;background:var(--zc)}
.pd-zone::before{top:0}.pd-zone::after{bottom:0}
.pd-zone i{writing-mode:vertical-rl;transform:rotate(180deg);font-family:var(--mono);
  font-size:9.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--zc);
  font-style:normal;white-space:nowrap}
@media (max-width:620px){.pd-zone{display:none}}

/* Zone colours, lifted for a dark ground.
   The industrial stack uses it/dmz/ot; the smart-building schematic reuses the
   same component with bldg/remote. `remote` is the building equivalent of the
   DMZ — the layer that touches everything and belongs to nobody — so it takes
   the same colour and the same hatched boundary treatment below. */
.is-it{--zc:#5b8cff}
.is-dmz{--zc:#ff3b52}
.is-ot{--zc:#2fd98a}
.is-bldg{--zc:#2fd98a}
.is-remote{--zc:#ff3b52}

/* --- levels --- */
.pd-lvl{grid-column:3;position:relative;display:grid;
  grid-template-columns:58px 34px 1fr auto;align-items:center;gap:14px;
  padding:13px 16px;border-radius:11px;
  background:rgba(255,255,255,.028);border:1px solid var(--dk-line);
  transition:background .3s var(--ease),border-color .3s var(--ease),transform .3s var(--ease)}
.pd-lvl:hover{background:rgba(255,255,255,.06);border-color:var(--dk-line-2);transform:translateX(4px)}

.pd-n{font-family:var(--mono);font-size:11.5px;font-weight:700;letter-spacing:.04em;
  color:var(--zc);text-align:center;padding:5px 0;border-radius:6px;
  background:color-mix(in srgb,var(--zc) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--zc) 32%,transparent)}
.pd-ico{display:grid;place-items:center;width:34px;height:34px;border-radius:9px;
  background:rgba(255,255,255,.04);border:1px solid var(--dk-line);color:var(--zc)}
.pd-ico svg{width:18px;height:18px}
.pd-body h3{font-size:14.5px;margin:0 0 2px;line-height:1.25;color:var(--dk-fg)}
.pd-body p{margin:0;color:var(--dk-mut);font-size:12.5px;line-height:1.45}
.pd-kit{list-style:none;display:flex;flex-wrap:wrap;gap:5px;margin:7px 0 0;padding:0}
.pd-kit li{font-family:var(--mono);font-size:9.5px;letter-spacing:.06em;color:var(--dk-soft);
  border:1px solid var(--dk-line);border-radius:4px;padding:2px 6px;background:rgba(255,255,255,.03)}
.pd-tag{font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--zc);border:1px solid color-mix(in srgb,var(--zc) 34%,transparent);
  border-radius:20px;padding:4px 10px;white-space:nowrap}

/* The DMZ is a boundary, so it is drawn as one: hatched, and set apart from the
   levels either side rather than sitting flush in the stack. */
.pd-lvl.is-dmz,.pd-lvl.is-remote{margin:5px 0;border-style:dashed;
  background:repeating-linear-gradient(135deg,rgba(255,59,82,.10) 0 6px,transparent 6px 12px),
             rgba(255,59,82,.05)}
.pd-lvl.is-dmz:hover,.pd-lvl.is-remote:hover{background:repeating-linear-gradient(135deg,rgba(255,59,82,.16) 0 6px,transparent 6px 12px),
             rgba(255,59,82,.09)}

/* --- the descent. A pulse falls from the enterprise network, pauses at the
   boundary, and continues into the control layers: the IT/OT crossing the next
   section argues about, shown rather than asserted. --- */
.pd-spine{grid-column:2;position:relative;justify-self:center;
  width:2px;border-radius:2px;pointer-events:none;align-self:stretch;
  background:linear-gradient(180deg,#5b8cff,#ff3b52 30%,#2fd98a 62%);opacity:.22}
/* The building diagram labels rows by name (ROOF, BASEMENT) rather than by
   level number (L3.5), so the chip column has to be wider and the type smaller.
   At the inherited 58px, "BASEMENT" overflowed its own chip. */
.page-smartbuilding .pd-lvl{grid-template-columns:88px 34px 1fr auto}
.page-smartbuilding .pd-n{font-size:9.5px;letter-spacing:.06em;padding:6px 4px}
@media (max-width:620px){
  .page-smartbuilding .pd-lvl{grid-template-columns:78px 1fr}
  /* ownership tags here are phrases rather than two words, so they wrap on a
     phone instead of forcing the row wider than the panel */
  .page-smartbuilding .pd-tag{margin-left:0;white-space:normal;line-height:1.5}
}

/* the building diagram inverts the story: four owned layers, then the shared
   network at the bottom that nobody owns */
.page-smartbuilding .pd-spine{background:linear-gradient(180deg,#2fd98a,#2fd98a 62%,#ff3b52)}
.page-smartbuilding .pd-watch::before{background:linear-gradient(180deg,#2fd98a,#2fd98a 62%,#ff3b52)}
@media (max-width:1040px){
  .page-smartbuilding .pd-watch::before{background:linear-gradient(90deg,#2fd98a,#2fd98a 62%,#ff3b52)}
}
.pd-spine i{position:absolute;left:50%;top:0;width:8px;height:8px;margin-left:-4px;
  border-radius:50%;background:#5b8cff;box-shadow:0 0 0 4px rgba(91,140,255,.22);
  animation:pddrop 7.5s var(--ease) infinite}
@keyframes pddrop{
  0%   {top:2%;  opacity:0; background:#5b8cff;box-shadow:0 0 0 4px rgba(91,140,255,.22)}
  7%   {opacity:1}
  26%  {top:22%; opacity:1; background:#5b8cff;box-shadow:0 0 0 4px rgba(91,140,255,.22)}
  /* holds at the boundary, because that is the part worth looking at */
  36%  {top:29%; opacity:1; background:#ff3b52;box-shadow:0 0 0 7px rgba(255,59,82,.26)}
  50%  {top:31%; opacity:1; background:#ff3b52;box-shadow:0 0 0 10px rgba(255,59,82,.12)}
  90%  {top:97%; opacity:1; background:#2fd98a;box-shadow:0 0 0 4px rgba(47,217,138,.22)}
  100% {top:97%; opacity:0; background:#2fd98a}
}
/* and the pulse ends at the shared network rather than passing into it */
.page-smartbuilding .pd-spine i{animation-name:pdsettle}
@keyframes pdsettle{
  0%   {top:2%;  opacity:0; background:#2fd98a;box-shadow:0 0 0 4px rgba(47,217,138,.22)}
  7%   {opacity:1}
  55%  {top:60%; opacity:1; background:#2fd98a;box-shadow:0 0 0 4px rgba(47,217,138,.22)}
  72%  {top:88%; opacity:1; background:#ff3b52;box-shadow:0 0 0 7px rgba(255,59,82,.26)}
  86%  {top:97%; opacity:1; background:#ff3b52;box-shadow:0 0 0 10px rgba(255,59,82,.12)}
  100% {top:97%; opacity:0; background:#ff3b52}
}
@media (max-width:620px){.pd-spine{display:none}}
/* the spine track collapses with it, so levels keep the full width */
@media (max-width:620px){.pd-diagram>.pd-lvl{grid-column:1}}

/* --- observation rail. Dashed taps reach toward each level and stop short of
   it: nothing installed, nothing inline. That gap is the claim. --- */
.pd-watch{grid-column:4;position:relative;align-self:stretch;
  background:rgba(255,255,255,.03);border:1px solid var(--dk-line);border-radius:11px;
  padding:20px 18px;display:flex;flex-direction:column;justify-content:center}
.pd-watch::before{content:"";position:absolute;left:-7px;top:16px;bottom:16px;width:2px;
  border-radius:2px;background:linear-gradient(180deg,#5b8cff,#ff3b52 30%,#2fd98a 62%);opacity:.5}
.pd-lvl::after{content:"";position:absolute;right:-12px;top:50%;width:12px;
  border-top:1px dashed color-mix(in srgb,var(--zc) 55%,transparent);opacity:.75}
@media (max-width:1040px){
  .pd-watch{grid-column:1/-1;grid-row:auto!important;margin-top:10px}
  .pd-watch::before{left:16px;right:16px;top:-1px;bottom:auto;width:auto;height:2px;
    background:linear-gradient(90deg,#5b8cff,#ff3b52 30%,#2fd98a 62%)}
  .pd-lvl::after{display:none}
}
.pd-eye{display:grid;place-items:center;width:34px;height:34px;border-radius:9px;
  background:rgba(255,255,255,.05);border:1px solid var(--dk-line-2);color:var(--red-3);margin-bottom:12px}
.pd-eye svg{width:18px;height:18px}
.pd-watch h3{font-size:15px;margin:0 0 7px;color:var(--dk-fg)}
.pd-watch p{margin:0;color:var(--dk-soft);font-size:13px;line-height:1.55}

@media (max-width:620px){
  .pd-lvl{grid-template-columns:52px 1fr;gap:10px;padding:12px 13px}
  .pd-ico{display:none}
  .pd-tag{grid-column:1/-1;justify-self:start;margin-left:62px}
}

/* levels arrive top-down, which is the direction the diagram reads */
.purdue.reveal .pd-lvl{opacity:0;transform:translateY(10px);
  transition:opacity .5s var(--ease) calc(var(--i) * .07s),transform .5s var(--ease) calc(var(--i) * .07s)}
.purdue.reveal.in .pd-lvl{opacity:1;transform:none}
.purdue.reveal.in .pd-lvl:hover{transform:translateX(4px);transition-delay:0s}

.purdue figcaption{margin-top:14px;font-family:var(--mono);font-size:11px;
  letter-spacing:.06em;color:var(--fg-mut);text-align:center}

@media (prefers-reduced-motion:reduce){
  .pd-spine i{animation:none;top:30%}
  .pd-lvl{transition:none}
  .pd-lvl:hover{transform:none}
  .purdue.reveal .pd-lvl{opacity:1;transform:none;transition:none}
}


/* ---------- /services/ : four practices, one operation ----------
   Drawn rather than listed: four cards state that the practices share an
   operation, a convergence shows it.

   LIGHT, deliberately. The dark version read as a diagram to look at rather
   than a set of things to click, and this section is the page's main navigation
   — every row is a route somewhere. So the panel is a light ground and the
   practices are raised white cards on it, which is the shape a person already
   knows to press.

   .pd-panel and .pd-head are the dark schematic components shared with the
   Purdue and building diagrams. They are OVERRIDDEN here rather than modified,
   so those two pages are untouched. */
.conv{margin:0}
.conv .pd-panel{background:linear-gradient(180deg,var(--bg-2),var(--mist));
  border-color:var(--line);box-shadow:var(--shadow-sm);padding:clamp(18px,2.2vw,26px)}
.conv .pd-panel::before{opacity:.5;
  background-image:linear-gradient(rgba(15,20,35,.035) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(15,20,35,.035) 1px,transparent 1px)}
.conv .pd-head{border-bottom-color:var(--line)}
.conv .pd-brand{color:var(--fg-mut)}
.conv .pd-dot{background:var(--red);box-shadow:0 0 0 3px var(--red-wash)}
.conv .pd-mode{color:var(--fg-mut);border-color:var(--line-2);background:var(--paper)}

.conv-grid{display:grid;grid-template-columns:minmax(0,1fr) 40px 272px;align-items:stretch}
@media (max-width:1040px){.conv-grid{grid-template-columns:minmax(0,1fr)}}

/* Room to breathe: these were 8px apart and read as one block.
   Rows size to their own content deliberately — forcing them to a common height
   was tried and reverted, because padding three cards out to match Detection &
   Response left more dead space than the tidiness was worth. */
.conv-list{list-style:none;margin:0;padding:0;display:grid;gap:20px}

.cprac{position:relative;display:grid;grid-template-columns:34px 42px minmax(0,1fr);
  gap:16px;align-items:start;padding:24px 26px;border-radius:14px;
  background:var(--paper);border:1px solid var(--line);box-shadow:var(--shadow-sm);
  transition:border-color .3s var(--ease),box-shadow .3s var(--ease)}
/* NO transform on hover: the row carries the connector that lands on the rail,
   and moving it detaches the diagram from itself. The lift is done with shadow
   and colour instead. */
.cprac:hover{border-color:var(--line-2);box-shadow:var(--shadow)}
.cprac:hover .cico{background:var(--red);border-color:var(--red);color:#fff}
.cprac:hover::after{opacity:.7}

.cn{font-family:var(--mono);font-size:12px;font-weight:700;color:var(--red);
  letter-spacing:.04em;padding-top:11px}
.cico{display:grid;place-items:center;width:42px;height:42px;border-radius:11px;
  background:var(--red-wash);border:1px solid var(--line);color:var(--red);
  transition:background .3s var(--ease),border-color .3s var(--ease),color .3s var(--ease)}
.cico svg{width:22px;height:22px}
/* the frame recedes, the glyph carries the weight. Both inherit currentColor,
   so filling the tile red on hover takes the whole icon with it. */
.cico .i-base{opacity:.34}
.cico .i-main{opacity:1}

.cbody h3{font-size:19px;margin:0 0 6px;line-height:1.25;color:var(--ink);
  display:flex;align-items:baseline;flex-wrap:wrap;gap:9px}
.cbody h3 a{display:inline-flex;align-items:baseline;gap:7px;color:var(--ink);
  transition:color .25s var(--ease)}
.cbody h3 a svg{width:14px;height:14px;transition:transform .25s var(--ease)}
.cbody h3 a:hover{color:var(--red)}
.cprac:hover .cbody h3 a{color:var(--red)}
.cprac:hover .cbody h3 a svg{transform:translateX(4px)}
.cwip{font-family:var(--mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--fg-mut);border:1px solid var(--line-2);border-radius:20px;padding:3px 8px}
.cbody>p{margin:0 0 14px;color:var(--fg-soft);font-size:14.5px;line-height:1.6;max-width:64ch}

/* The services are the interaction. Three of seventeen are built, so those
   three are buttons and the rest are quiet labels — the contrast is the fastest
   read of what you can actually go and open. */
.cchips{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px}
.cchip{display:inline-flex;align-items:center;gap:6px;font-size:13px;line-height:1.3;
  border-radius:20px;padding:7px 13px;border:1px solid var(--line);
  color:var(--fg-mut);background:var(--mist)}
.cchip svg{width:12px;height:12px;transition:transform .25s var(--ease)}
.cchip.is-live{background:linear-gradient(180deg,var(--red-2),var(--red));color:#fff;
  border-color:transparent;font-weight:600;
  box-shadow:0 8px 20px -9px var(--red-glow),inset 0 1px 0 rgba(255,255,255,.28);
  transition:transform .25s var(--ease),box-shadow .25s var(--ease)}
.cchip.is-live:hover{transform:translateY(-2px);
  box-shadow:0 14px 30px -10px var(--red-glow),inset 0 1px 0 rgba(255,255,255,.4)}
.cchip.is-live:hover svg{transform:translateX(2px)}

/* the rail: every practice runs into it, and it runs into the hub */
.conv-rail{position:relative;display:block}
.conv-rail::before{content:"";position:absolute;left:50%;top:26px;bottom:26px;width:2px;
  margin-left:-1px;border-radius:2px;
  background:linear-gradient(180deg,transparent,var(--red) 14%,var(--red) 86%,transparent);
  opacity:.32}
.conv-rail::after{content:"";position:absolute;left:50%;right:-1px;top:50%;height:2px;
  background:var(--red);opacity:.32}
.conv-rail i{position:absolute;left:50%;top:0;width:8px;height:8px;margin-left:-4px;
  border-radius:50%;background:var(--red);box-shadow:0 0 0 4px var(--red-wash);
  animation:convflow 5.5s var(--ease) infinite}
@keyframes convflow{
  0%   {top:6%;  opacity:0}
  10%  {opacity:1}
  50%  {top:50%; opacity:1}
  62%  {top:50%; opacity:1;box-shadow:0 0 0 10px rgba(195,0,23,.10)}
  100% {top:50%; opacity:0;box-shadow:0 0 0 4px var(--red-wash)}
}
/* each practice reaches the rail and stops ON it. The rail column is 40px with
   the line down its centre, so the stub is half that. */
.cprac::after{content:"";position:absolute;right:-20px;top:50%;width:20px;height:1px;
  background:var(--red);opacity:.3;transition:opacity .3s var(--ease)}
@media (max-width:1040px){
  .conv-rail{display:none}
  .cprac::after{display:none}
}

/* the SOC everything runs into */
.conv-hub{position:relative;align-self:center;background:var(--paper);
  border:1px solid var(--line-2);border-radius:16px;padding:26px 24px;
  box-shadow:0 18px 44px -22px var(--red-glow),var(--shadow-sm)}
.conv-hub::before{content:"";position:absolute;left:0;right:0;top:0;height:3px;
  border-radius:16px 16px 0 0;background:linear-gradient(90deg,var(--red),var(--red-3))}
@media (max-width:1040px){.conv-hub{margin-top:20px}}
.ch-ico{display:grid;place-items:center;width:44px;height:44px;border-radius:12px;
  background:var(--red-wash);border:1px solid var(--line);color:var(--red);margin-bottom:16px}
.ch-ico svg{width:22px;height:22px}
.conv-hub h3{font-size:19px;margin:0 0 9px;color:var(--ink)}
.conv-hub p{margin:0 0 16px;color:var(--fg-soft);font-size:14px;line-height:1.6}
.ch-facts{list-style:none;margin:0 0 18px;padding:0;display:grid;gap:7px}
.ch-facts li{position:relative;padding-left:17px;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.08em;text-transform:uppercase;color:var(--fg-mut)}
.ch-facts li::before{content:"";position:absolute;left:0;top:5px;width:5px;height:5px;
  border-radius:1px;background:var(--red)}
/* The panel's only action, so it is a button rather than a text link. Full
   width because the panel is narrow and a left-aligned pill in a 230px column
   reads as an afterthought. `.btn` sets nowrap, which would overflow the panel
   on a phone, so the label is allowed to wrap here. */
.ch-cta{display:flex;width:100%;justify-content:center;text-align:center;white-space:normal;line-height:1.3;
  padding-left:14px;padding-right:14px;gap:8px}
.ch-cta svg{flex:none;width:15px;height:15px;transition:transform .25s var(--ease)}
.ch-cta:hover svg{transform:translateX(3px)}

.conv.reveal .cprac{opacity:0;transform:translateY(10px);
  transition:opacity .5s var(--ease) calc(var(--i) * .08s),transform .5s var(--ease) calc(var(--i) * .08s)}
.conv.reveal.in .cprac{opacity:1;transform:none}

@media (max-width:620px){
  .cprac{grid-template-columns:30px minmax(0,1fr);padding:20px}
  .cico{display:none}
}
@media (prefers-reduced-motion:reduce){
  .conv-rail i{animation:none;top:50%}
  .cprac,.cchip.is-live,.cico{transition:none}
  .cchip.is-live:hover{transform:none}
  .conv.reveal .cprac{opacity:1;transform:none;transition:none}
}

/* ---------- platforms and protocols ----------
   EVERY PLATFORM CARD IS IDENTICAL. No wider column, no accent bar, no heavier
   border on any one of them. The visual equality is the argument, so do not
   reintroduce a "primary" variant here — see the ruling in
   src/data/managed-ot-soc.js. */
.platintro{max-width:74ch;margin:0 auto 30px;text-align:center}
.platintro p{color:var(--fg-soft);font-size:16px;line-height:1.7;margin:0 0 14px}
.platintro p:last-child{margin-bottom:0}

/* Flex rather than grid, because these sets divide into nothing: five platforms
   on the managed SOC page, six building systems on the smart-building page. A
   grid strands an empty slot at three or two columns — the same hole that made
   the sectors grid look broken. Wrapping flex with a centred last row reads as
   deliberate at every width.

   The 168px basis is chosen so SIX fit on one desktop row (6*168 + 5*16 = 1088,
   inside the 1192 wrap). At 196px the sixth card wrapped alone onto its own
   line, which looked like a mistake. Five still grow to the same width they had
   before, so the sibling page is unchanged. */
.pgrid{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;
  justify-content:center;gap:16px}

.pcard{position:relative;flex:1 1 168px;max-width:264px;display:flex;flex-direction:column;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:22px 20px;box-shadow:var(--shadow-sm);
  transition:transform .3s var(--ease),border-color .3s var(--ease),box-shadow .3s var(--ease)}
.pcard:hover{transform:translateY(-5px);border-color:var(--line-2);box-shadow:var(--shadow)}
.pc-mark{display:grid;place-items:center;width:34px;height:34px;border-radius:9px;
  background:var(--red-wash);border:1px solid var(--line);color:var(--red);margin-bottom:15px}
.pc-mark svg{width:18px;height:18px}
.pcard h3{font-size:16px;margin:0 0 8px;line-height:1.3;letter-spacing:-.01em}
.pcard p{margin:0;color:var(--fg-mut);font-size:14px;line-height:1.55}
/* five cards, so the stagger runs off --i rather than the d1-d6 classes */
.pgrid .pcard.reveal{transition-delay:calc(var(--i) * .06s)}
.pgrid .pcard.reveal:hover{transition-delay:0s}

/* the two notes carry the only opinions in the section, so they sit apart from
   the grid rather than inside it */
.platnotes{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin:22px 0 0}
@media (max-width:860px){.platnotes{grid-template-columns:1fr}}
.pnote{position:relative;overflow:hidden;background:var(--mist);border:1px solid var(--line);
  border-radius:var(--radius);padding:22px 24px 22px 27px}
.pnote::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--seam-grad-v)}
.pnote.is-stack{border-left-color:var(--blue)}
.pn-k{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red);margin-bottom:9px}
.pnote.is-stack .pn-k{color:var(--blue)}
.pnote p{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.62}

/* protocols sit on their own rule under everything else: one claim, one row */
.protobar{margin:22px 0 0;padding:20px 26px;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--mist);display:flex;flex-wrap:wrap;
  align-items:center;justify-content:space-between;gap:14px 26px}
.protobar p{margin:0;color:var(--fg-soft);font-size:15px;max-width:56ch}
.chips{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px}
.chips li{font-family:var(--mono);font-size:11px;letter-spacing:.06em;color:var(--ink);
  border:1px solid var(--line-2);border-radius:20px;padding:5px 12px;
  background:var(--paper);font-weight:500}

@media (prefers-reduced-motion:reduce){
  .pcard{transition:none}
  .pcard:hover{transform:none}
  .pgrid .pcard.reveal{transition-delay:0s}
}

/* Industries.
   The grid draws its 1px rules by letting the container background show through
   a 1px gap, which is why an incomplete final row rendered as a grey block. The
   fix is BOTH halves: eight tiles rather than seven, and fixed column counts
   that always divide into eight. auto-fit could still strand a row at some
   intermediate width, so it is gone. */
.indgrid{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(4,1fr);gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
@media (max-width:1000px){.indgrid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){.indgrid{grid-template-columns:1fr}}

.ind{position:relative;background:var(--paper);padding:24px;isolation:isolate;
  transition:background .35s var(--ease)}
.ind h3{font-size:17px;margin:0 0 7px;line-height:1.3;transition:color .35s var(--ease)}
.ind p{color:var(--fg-mut);font-size:14.5px;line-height:1.55;margin:0}
/* the category line reads as a subtitle, the risk line as the body under it */
.ind .il{color:var(--fg-soft);font-weight:500;margin-bottom:9px}
.ind .il+p{font-size:14px;color:var(--fg-mut)}

/* A red rule wipes in along the top edge, the tile lifts its own background,
   and the heading takes the brand colour. Nothing moves position: these are
   1px-separated cells and translating one opens a seam against its neighbour. */
.ind::before{content:"";position:absolute;inset:0 0 auto;height:3px;
  background:linear-gradient(90deg,var(--red),var(--red-3));
  transform:scaleX(0);transform-origin:left;transition:transform .45s var(--ease)}
.ind::after{content:"";position:absolute;inset:0;z-index:-1;
  background:radial-gradient(120% 90% at 0% 0%,var(--red-wash),transparent 70%);
  opacity:0;transition:opacity .45s var(--ease)}
.ind:hover{background:var(--mist)}
.ind:hover::before{transform:scaleX(1)}
.ind:hover::after{opacity:1}
.ind:hover h3{color:var(--red)}

/* the eighth tile is a prompt, not a sector, so it reads slightly quieter and
   carries the only link in the grid */
.ind.is-ask{background:var(--mist);display:flex;flex-direction:column}
.ind.is-ask .textlink{margin-top:auto;padding-top:14px;font-size:14px}
.ind.is-ask:hover{background:var(--paper)}

/* Entrance stagger. The delay classes in main.css stop at d6 and there are
   eight tiles, so the sequence is driven from here instead. */
.indgrid .ind:nth-child(1){transition-delay:0s}
.indgrid .ind:nth-child(2){transition-delay:.05s}
.indgrid .ind:nth-child(3){transition-delay:.10s}
.indgrid .ind:nth-child(4){transition-delay:.15s}
.indgrid .ind:nth-child(5){transition-delay:.20s}
.indgrid .ind:nth-child(6){transition-delay:.25s}
.indgrid .ind:nth-child(7){transition-delay:.30s}
.indgrid .ind:nth-child(8){transition-delay:.35s}
/* ...but a hover must react now, not after a third of a second */
.indgrid .ind:hover{transition-delay:0s}

@media (prefers-reduced-motion:reduce){
  .ind,.ind::before,.ind::after,.ind h3{transition:none}
  .indgrid .ind{transition-delay:0s}
}

/* ---------- vendor logo banner ---------- */
.vendorband{margin:22px 0 0;padding:20px 0 22px;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--paper);overflow:hidden}
.vb-k{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--fg-mut);text-align:center;margin-bottom:16px}
/* The shared .marquee sets the scroll, the mask and pause-on-hover; this only
   normalises logo height and keeps wordmark fallbacks the same size.

   The gap moves onto the items. `scrollx` loops at translateX(-50%), which is
   only seamless if half the track is exactly one full pass. With flex `gap`,
   N items carry N-1 gaps, so half the width lands half a gap short and the
   loop stutters once per cycle. A trailing margin per item makes every pass
   self-contained and the seam disappears. */
.marquee.logos .track{gap:0;align-items:center}
.marquee.logos .item{font-size:17px;color:var(--fg-mut);margin-right:64px}
.marquee.logos .item.has-logo::before{display:none}
.marquee.logos .item img{display:block;height:30px;width:auto;max-width:190px;
  object-fit:contain;filter:grayscale(1);opacity:.65;
  transition:filter .3s var(--ease),opacity .3s var(--ease)}
.marquee.logos .item img:hover{filter:none;opacity:1}
@media (max-width:640px){
  .marquee.logos .item{margin-right:40px}
  .marquee.logos .item img{height:24px}
}

/* ---------- partners: the two programmes ----------
   Two cards, not three. There is no referral tier, so the row is a pair and is
   capped rather than stretched across the full measure: two very wide cards
   read as a table of features, which these are not. */
.prgrow{display:grid;grid-template-columns:1fr 1fr;gap:20px;
  max-width:940px;margin:clamp(26px,3vw,36px) auto 0;align-items:stretch}
.prg{display:flex;flex-direction:column;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius-lg);padding:28px 26px;box-shadow:var(--shadow-sm);
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.prg:hover{transform:translateY(-4px);border-color:var(--line-2);box-shadow:var(--shadow)}
.prg.is-feat{position:relative;overflow:hidden;padding-top:30px}
.prg.is-feat::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;background:var(--seam-grad)}
.prg-n{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red)}
.prg h3{font-size:21px;letter-spacing:-.018em;margin:10px 0 11px}
.prg p{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.6}
.prg-who{margin:auto 0 0 !important;padding-top:16px;border-top:1px solid var(--line);
  color:var(--ink) !important;font-size:13.5px !important;font-weight:600}
@media (max-width:760px){.prgrow{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.prg:hover{transform:none}}

/* ---------- partners: what the partner's customers get ----------
   Four points in a fixed 2x2 rather than an auto-fit row: auto-fit lands three
   across with one orphan underneath at desktop widths. */
/* RENAMED from .inc/.ingrid, 15 Sep 2026. `.inc` was already the comparison
   matrix's "included" tick, and this rule's border-top painted a blue line
   across the top of every tick in that table - which looked like a rendering
   artefact and took a while to trace. The collision checker did not catch it
   because both declarations were in this one file, and it only flags a class
   declared in two different files. Two components, two names. */
.benefgrid{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-top:clamp(26px,3vw,36px)}
.benefcard{background:rgba(255,255,255,.04);border:1px solid var(--dk-line);
  border-radius:var(--radius);padding:24px 26px;border-top:2px solid var(--blue)}
.benefcard h3{font-size:17px;color:#fff;letter-spacing:-.01em;margin:0 0 9px;line-height:1.35}
.benefcard p{margin:0;color:var(--dk-mut);font-size:14.5px;line-height:1.6}
@media (max-width:760px){.benefgrid{grid-template-columns:1fr}}

/* ---------- named customer proof ----------
   Two cards, one per consented case study. Deliberately asymmetric in content
   rather than in layout: Synergy gave us quotes and Revalize gave us a video,
   so one card carries speech and the other carries an outcome line. Padding a
   quote out of thin air to make them match would be the whole point missed. */
.csrow{display:grid;grid-template-columns:1fr 1fr;gap:20px;
  margin-top:clamp(26px,3vw,36px);align-items:stretch}
.cs{display:flex;flex-direction:column;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius-lg);padding:30px 28px;box-shadow:var(--shadow-sm);
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.cs:hover{transform:translateY(-4px);border-color:var(--line-2);box-shadow:var(--shadow)}
.cs-tag{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red)}
.cs h3{font-size:22px;letter-spacing:-.02em;margin:10px 0 3px}
.cs-sector{display:block;font-size:13px;color:var(--fg-mut);margin-bottom:14px}
.cs p{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.62}
.cs blockquote{margin:20px 0 0;padding:0 0 0 18px;border-left:3px solid var(--red);
  font-family:var(--display);font-weight:500;font-size:17px;line-height:1.45;
  letter-spacing:-.012em;color:var(--ink);text-wrap:pretty}
.cs blockquote + blockquote{margin-top:14px}
.cs-by{display:block;margin-top:14px;font-size:13px;color:var(--fg-mut)}
.cs-by strong{display:block;color:var(--ink);font-size:14px;font-weight:600}
.cs-out{margin-top:auto !important;padding-top:18px;border-top:1px solid var(--line);
  color:var(--ink) !important;font-size:14px !important}
.cs-foot{margin-top:18px;padding-top:16px;border-top:1px solid var(--line)}
@media (max-width:820px){.csrow{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.cs:hover{transform:none}}

/* ---------- click-to-load video facade ----------
   Ships as a button; public/js/main.js replaces it with the Vimeo iframe on
   click. The replacement is an <iframe> at the same place in the flow, so the
   aspect ratio is set on both to stop the card jumping when it swaps. */
.vfacade,.cs-foot iframe{display:block;width:100%;aspect-ratio:16/9;border:0;
  border-radius:var(--radius);background:var(--bg-2)}
/* The poster is the video's own Vimeo thumbnail, served from this site. The
   play button and caption sit ON it, so the button is a position container and
   the fallback gradient stays underneath for the moment before it decodes. */
.vfacade{position:relative;overflow:hidden;display:flex;align-items:flex-end;gap:14px;
  padding:18px;cursor:pointer;
  background:linear-gradient(135deg,var(--bd-1),var(--bd-2));text-align:left;
  transition:transform .25s var(--ease),box-shadow .25s var(--ease)}
.vf-poster{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transition:transform .4s var(--ease)}
.vfacade:hover .vf-poster{transform:scale(1.03)}
/* A scrim under the controls only. A full-frame wash would mute the thumbnail,
   which is the thing worth looking at - these are branded stills with the
   customer's name on them, not generic video frames. */
.vfacade::after{content:"";position:absolute;left:0;right:0;bottom:0;height:62%;
  background:linear-gradient(to top,rgba(8,4,6,.88),rgba(8,4,6,.55) 45%,transparent);
  pointer-events:none}
.vfacade > .vf-play,.vfacade > .vf-t{position:relative;z-index:1}
.vfacade:hover{transform:translateY(-2px);box-shadow:var(--shadow)}
.vfacade:focus-visible{outline:2px solid var(--red);outline-offset:3px}
.vf-play{flex:none;display:grid;place-items:center;width:52px;height:52px;border-radius:50%;
  background:var(--red);color:#fff;box-shadow:0 10px 30px -10px var(--red-glow);
  transition:transform .25s var(--ease)}
.vf-play svg{width:24px;height:24px;margin-left:2px}
.vfacade:hover .vf-play{transform:scale(1.08)}
.vf-t{font-family:var(--display);font-weight:600;font-size:15px;line-height:1.3;color:#fff;
  text-shadow:0 1px 12px rgba(0,0,0,.5)}
.vf-t em{display:block;margin-top:4px;font-style:normal;font-weight:400;
  font-size:12.5px;line-height:1.4;color:var(--dk-mut)}
@media (prefers-reduced-motion:reduce){
  .vfacade:hover{transform:none}.vfacade:hover .vf-play{transform:none}
}
/* ===========================================================================
   MANAGED SIEM — definition block                    .siem-* (page-scoped)
   ---------------------------------------------------------------------------
   The shared .defband/.def components are used by six pages, so this page gets
   its own variant rather than restyling theirs. Prefixed .siem- so it can be
   promoted into main.css later if it earns its place, or deleted without
   touching anything else.

   WHY A FIGURE AT ALL: a SIEM's whole proposition is volume in, meaning out.
   The figure shows many events collapsing through a correlation layer into two
   alerts, one analyst-reviewed. It says what the paragraph says without
   repeating it, and the paragraph stays one intact block so an answer engine
   can still lift it whole.

   Motion is opt-in: everything below renders at its resting state, and the
   keyframes only attach under prefers-reduced-motion: no-preference.
   =========================================================================== */
/* Extra padding so the prism has bands above and below the card to show in.
   The card is nearly full width at every viewport, so vertical room is the only
   room that can be relied on. */
.siem-defsec{
  position:relative;overflow:hidden;
  padding-block:clamp(104px,11vw,158px);
  background:linear-gradient(180deg,var(--bg-2),var(--bg));
}

/* The separator.
   No border at the top: the hero above is dark and this section is light, so
   the change in value already does the work and a rule would only fence it off.
   The bottom is the problem — this section ends on --bg and the next one starts
   on --bg, so with nothing there the two run together. A flat grey hairline
   solves that but reads as a default.

   Instead: a rule that is brightest at the centre and gone by the edges, with a
   soft bloom rising off it. Two layers in one pseudo-element — the 2px rule
   pinned to the bottom, and a wide radial above it. The gradient means the line
   has a focal point rather than a uniform weight across the full width. */
/* A red line that varies in thickness: thick at the centre, thin at the ends.

   Shaped with a mask rather than clip-path. clip-path gives a hard geometric
   edge, and on a 5px band a hard diagonal edge is what made the last version
   look crude — it stair-steps. A mask's falloff antialiases the taper instead,
   so the thickness change reads as a smooth swell.

   The softness is doing antialiasing, not glowing. A glow needs vertical room
   to disperse into and this element is 6px tall, so there is physically nowhere
   for one to form — the mask can only take thickness away, never spread light. */
.siem-defsec::after{
  content:"";position:absolute;z-index:2;pointer-events:none;
  left:0;right:0;bottom:0;height:4px;
  background:linear-gradient(90deg,
    rgba(195,0,23,1) 0%,
    rgba(226,16,41,1) 50%,
    rgba(195,0,23,1) 100%);
  -webkit-mask-image:radial-gradient(ellipse 52% 50% at 50% 50%,
    #000 0%, #000 28%, rgba(0,0,0,.58) 66%, rgba(0,0,0,.22) 88%, transparent 100%);
  mask-image:radial-gradient(ellipse 52% 50% at 50% 50%,
    #000 0%, #000 28%, rgba(0,0,0,.58) 66%, rgba(0,0,0,.22) 88%, transparent 100%);
}
/* The prism's mount. Sits right of centre and taller than the card, so the
   card occludes its middle — an object the content overlaps reads as behind it.
   main.js appends the canvas; with no JS this element is simply empty. */
.siem-def{
  position:relative;z-index:1;overflow:hidden;
  display:grid;grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr);
  gap:clamp(26px,4vw,54px);align-items:center;
  background:linear-gradient(152deg,var(--dk-panel) 0%,var(--void) 78%);
  border:1px solid var(--dk-line);border-radius:var(--radius-lg);
  padding:clamp(26px,3.6vw,46px);
  box-shadow:0 34px 78px -38px rgba(12,18,38,.55);
}
/* Brand red appears on dark only as a diffuse glow behind the figure. At this
   opacity it reads as warmth, not as an alert state. */
.siem-def::before{
  content:"";position:absolute;right:-14%;top:-34%;width:62%;height:168%;
  background:radial-gradient(closest-side,rgba(195,0,23,.30),transparent 72%);
  pointer-events:none;
}
.siem-def-copy,.siem-def-fig{position:relative;z-index:1}

.siem-def-q{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--red-3);font-weight:700;margin:0 0 16px;
}
.siem-def-a{
  margin:0;font-family:var(--display);font-weight:500;
  font-size:clamp(17.5px,1.85vw,21.5px);line-height:1.52;letter-spacing:-.012em;
  color:var(--dk-fg);max-width:48ch;
}
.siem-def-a strong{font-weight:700;color:#fff}

/* ---------- the figure: many in, few out ---------- */
.siem-def-fig{display:flex;flex-direction:column;gap:14px}

.siem-fig-in{display:grid;grid-template-columns:repeat(6,1fr);gap:7px 8px}
.siem-fig-in i{
  display:block;height:4px;border-radius:2px;background:var(--dk-line-2);opacity:.55;
}

.siem-fig-gate{position:relative;display:flex;align-items:center;gap:12px;padding:2px 0}
.siem-fig-gate::before{
  content:"";flex:1;height:1px;
  background:linear-gradient(90deg,transparent,var(--red-3) 35%,var(--red-3) 65%,transparent);
  opacity:.7;
}
.siem-fig-gate span{
  font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--dk-mut);white-space:nowrap;flex:none;
}

.siem-fig-out{display:flex;flex-direction:column;gap:8px}
.siem-fig-alert{
  display:flex;align-items:center;gap:10px;
  border:1px solid var(--dk-line-2);border-radius:9px;
  background:rgba(255,255,255,.04);padding:10px 13px;
  font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;color:var(--dk-soft);
}
.siem-fig-alert b{width:5px;height:5px;border-radius:50%;background:var(--red-3);flex:none}
.siem-fig-alert svg{width:13px;height:13px;margin-left:auto;color:var(--green);flex:none}
.siem-fig-alert.is-signed{border-color:color-mix(in srgb,var(--green) 42%,transparent)}
.siem-fig-alert.is-signed b{background:var(--green)}

/* ---------- motion, opt-in ---------- */
@media (prefers-reduced-motion:no-preference){
  .siem-fig-in i{
    animation:siemFlow 6.5s ease-in-out infinite;
    animation-delay:calc(var(--i) * -0.21s);
  }
  .siem-fig-gate::before{animation:siemGate 6.5s ease-in-out infinite}
  .siem-fig-alert{animation:siemSurface 6.5s ease-in-out infinite}
  .siem-fig-alert:nth-child(2){animation-delay:.5s}
}
@keyframes siemFlow{
  0%,100%{opacity:.22;transform:translateY(0)}
  38%{opacity:.85;transform:translateY(1px)}
  62%{opacity:.18;transform:translateY(3px)}
}
@keyframes siemGate{
  0%,100%{opacity:.35}
  50%{opacity:.95}
}
@keyframes siemSurface{
  0%,44%{opacity:.5;transform:translateY(3px)}
  62%,100%{opacity:1;transform:translateY(0)}
}

@media (max-width:860px){
  .siem-def{grid-template-columns:1fr;gap:30px}
  .siem-def-a{max-width:none}
}

/* ---------- Managed SIEM: the three tracks ----------
   Off the shelf, build your own, managed. The two rejected options are drawn
   as outlines on the page ground; the one being argued for is lifted onto
   --paper with a brand edge and a shadow. Border, fill and elevation are spent
   on the difference between "considered and rejected" and "what we do" rather
   than stamped equally across all three, so the composition carries the
   argument before the copy is read.

   Deliberately NOT numbered: these are alternatives, and 01/02/03 would read
   as a sequence of steps.

   The cards stretch to a common height, so the short first option carries
   visible empty space. That is the honest shape of the argument — it has less
   to say — and evening it out with filler would flatten the point. */
.siem-tracks{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(14px,1.6vw,22px);align-items:stretch;
  margin-top:clamp(30px,4vw,48px);
}
/* The rejected options: present, legible, and behind.

   --bg-2 rather than --paper. On a light page white ADVANCES — --paper is
   brighter than the section's own --bg ground, so white cards stepped forward
   and stopped reading as unselected. A surface slightly darker than the ground
   recedes instead. Transparent was wrong in the other direction: that read as a
   hole cut in the page rather than an object on it.

   No drop shadow either. Elevation is what the chosen card has; these get a 1px
   inner highlight along the top instead, which reads as set INTO the page. */
.siem-track{
  position:relative;display:flex;flex-direction:column;gap:10px;
  border:1px solid var(--line);border-radius:var(--radius);
  padding:clamp(20px,2.2vw,28px);
  background:var(--bg-2);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.7);
  text-decoration:none;
}
.siem-track > *{position:relative;z-index:3}
/* Sits between the system's h4 (18px) and h3 (20-26px). An earlier 15px put it
   below every heading size the design system defines, which is what made the
   cards read as captioned paragraphs rather than titled objects.

   Text colour is NOT where the de-emphasis goes. Muting it far enough to read
   as unselected took the body to 3.8:1, which fails AA — an option being
   argued against still has to be readable by everyone. The recessed surface
   and the absence of elevation carry that signal instead, and they cost
   nothing in legibility. */
.siem-track h3{
  margin:0;font-size:clamp(18px,1.9vw,22px);font-weight:700;
  letter-spacing:-.018em;line-height:1.22;
  color:var(--fg);
}
.siem-track p{margin:0;font-size:14.5px;line-height:1.62;color:var(--fg-soft)}

/* THE FLOOR OF THE CARD. margin-top:auto is the whole fix for the ragged
   bottoms: in a flex column it pushes the last element down, so three cards
   of unequal copy still close on the same line. Without it the grid stretches
   each card to the tallest and leaves the difference as empty space under the
   paragraph, which is exactly what looked wrong.

   Borrowed from .siem-track-go, which already did this on the chosen card -
   that card was the only one with a footer, so it was the only one that
   looked deliberate. */
/* --fg-soft, not --fg-mut: this sits on .siem-track's #eef1f6 panel rather
   than on paper, which is darker than either --mist or --bg, and 11.5px
   text there needs 4.5. --fg-mut gives 4.32 on that ground - the last
   near-miss left after the 22 Sep sweep. --fg-soft gives 5.9. */
.siem-track-cost{
  margin-top:auto;padding-top:14px;
  border-top:1px solid var(--line);
  font-family:var(--mono);font-size:11.5px;font-weight:700;
  letter-spacing:.055em;text-transform:uppercase;
  color:var(--fg-soft);
}

/* THE CHOSEN TRACK, RETHEMED 21 Sep 2026 (Adam).

   It was a four-stop gradient running brand red into near-black, carrying a
   bloom layer, a cross-fading hover gradient and a keyframed light sweep.
   Adam's objection was the red-to-black fade, and checked against the rest of
   the site he is right on a second count too: THE SITE ALREADY HAS AN ANSWER
   TO THIS EXACT PROBLEM, and it looks nothing like that.

   .tier.is-featured - the recommended package on the pricing row - is the same
   job, one of three options argued for. It stays the same material as its
   siblings and earns emphasis with a red border, a red glow and a solid-red
   badge. No gradient, no bloom, no sweep. This follows that pattern.

   It fixes the token problem underneath as well: the old gradient was built
   from #a80013, #6d000c, #2a0207, #8a000f and #3a040a, five colours that exist
   nowhere in the palette. Everything below is a token.

   The two rejected tracks stay recessed on --bg-2, so the row now reads
   recessed, recessed, RAISED - the same three-beat as the packages. */
/* THE EDGE CARRIES ALL THREE COLOURS, the badge stays red.

   A border cannot take a gradient directly, so the card paints two
   backgrounds: the paper fill clipped to the padding box, and the gradient
   clipped to the border box, with the border itself transparent so the second
   shows through. Long-standing technique and it degrades safely - a browser
   that ignores background-clip renders a plain bordered card.

   Red leads and green closes, the same order as the masthead dot and the stat
   rails, so the sequence is the site's rather than decorative. The badge stays
   solid red on purpose: it is the one element saying "choose this", and a
   recommendation that fades through three colours is less certain of itself
   than one that does not. */
.siem-track.is-chosen{
  border:1.5px solid transparent;
  background:
    linear-gradient(var(--paper),var(--paper)) padding-box,
    linear-gradient(115deg,var(--red) 0%,var(--red-2) 28%,var(--blue) 62%,var(--green) 100%) border-box;
  box-shadow:0 34px 80px -34px var(--red-glow);
  transition:transform .34s var(--ease), box-shadow .34s var(--ease);
}
/* Straight off .tier .rec. This is what says "recommendation" rather than
   "third option", and it was the piece doing least work while a whole gradient
   shouted in its place. */
.siem-track.is-chosen::before{
  content:"Recommended";
  position:absolute;z-index:3;top:-13px;left:clamp(20px,2.2vw,28px);
  background:var(--red);color:#fff;
  font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;
  padding:6px 16px;border-radius:100px;white-space:nowrap;
}
/* Back to the ink tokens. The white overrides existed only because the card
   used to be a dark gradient. */
.siem-track.is-chosen h3{color:var(--ink)}
.siem-track.is-chosen p{color:var(--fg-soft)}

/* The link footer, matching .siem-track-cost on the other two so all three
   cards close on the same line. */
.siem-track-go{
  margin-top:auto;display:inline-flex;align-items:center;gap:8px;
  padding-top:14px;border-top:1px solid var(--line);
  font-size:14px;font-weight:700;color:var(--red);
}
.siem-track-go svg{width:16px;height:16px;transition:transform .3s var(--ease)}

a.siem-track.is-chosen:hover,
a.siem-track.is-chosen:focus-visible{
  transform:translateY(-3px);
  box-shadow:0 42px 90px -34px var(--red-glow);
}
a.siem-track.is-chosen:hover .siem-track-go svg,
a.siem-track.is-chosen:focus-visible .siem-track-go svg{transform:translateX(4px)}
a.siem-track.is-chosen:focus-visible{outline:2px solid var(--red);outline-offset:3px}

@media (prefers-reduced-motion:reduce){
  a.siem-track.is-chosen:hover{transform:none}
}
/* The badge overhangs the card, so the row needs headroom for it. */
@media (max-width:860px){
  .siem-tracks{grid-template-columns:1fr}
}

.siem-split{
  display:grid;grid-template-columns:minmax(0,1.02fr) minmax(0,.98fr);
  background:linear-gradient(180deg,var(--dk),var(--void));
  border:1px solid var(--dk-line-2);border-radius:var(--radius-lg);
  box-shadow:0 40px 92px -38px rgba(0,0,0,.7);
  overflow:hidden;
  max-width:1060px;margin:0 auto;
}
.siem-split-copy{padding:clamp(26px,3.2vw,42px)}
/* Sized between the system's h3 (20-26px) and h2 (30-56px). It stays an <h3>
   in the markup because the live page nests this under "Why choose a managed
   SIEM?" and the heading outline should keep saying so — but it now heads a
   full-width card rather than a paragraph, so it needs to carry visually like
   a section heading even while ranking below one.

   Line-height comes up from the system's 1.03 too: that is set for display
   headings that fit on one line, and this one wraps. */
.siem-split-copy h3{
  margin:14px 0 12px;
  font-size:clamp(24px,2.9vw,34px);
  line-height:1.14;
  letter-spacing:-.025em;
}
.siem-split-copy p:last-of-type{margin-bottom:18px}
/* The console half. Same gradient, border and header treatment as the hero's
   .console, so the two read as one component family. No border-left needed —
   the jump from light to dark IS the divider. */
.siem-split-model{
  background:linear-gradient(180deg,var(--dk-panel),var(--dk));
  display:flex;flex-direction:column;
}
.siem-model-head{
  display:flex;align-items:center;gap:10px;
  padding:14px 18px;border-bottom:1px solid var(--dk-line);
  background:rgba(255,255,255,.02);
}
.siem-model-title{
  font-family:var(--display);font-weight:700;font-size:13.5px;
  color:#fff;letter-spacing:-.01em;
}
/* Mirrors .console .chead .live — mono, small, hard right. Swaps with the
   toggle so the header always names the state you are looking at. */
.siem-model-state{
  grid-area:1/1;margin-left:auto;
  font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--red-3);display:none;align-items:center;gap:7px;
}
.siem-model-state::before{
  content:"";width:6px;height:6px;border-radius:50%;flex:none;
  background:currentColor;box-shadow:0 0 9px currentColor;
}
.siem-split[data-model=full] .siem-model-state[data-for=full],
.siem-split[data-model=co] .siem-model-state[data-for=co]{display:inline-flex}
.siem-model-body{padding:clamp(22px,2.6vw,30px)}

/* ---------- the two-state toggle ---------- */
.siem-toggle{
  position:relative;display:inline-flex;gap:2px;padding:3px;margin-bottom:22px;
  background:var(--bg-2);border:1px solid var(--line);border-radius:100px;
}
/* The moving indicator. Transform rather than left, so it animates on the
   compositor and the label text underneath never reflows. */
.siem-toggle-pill{
  position:absolute;top:3px;bottom:3px;left:3px;width:calc(50% - 3px);
  border-radius:100px;background:var(--paper);
  box-shadow:0 2px 6px -2px rgba(20,30,60,.26);
  transition:transform .34s var(--ease);
}
.siem-split[data-model="co"] .siem-toggle-pill{transform:translateX(100%)}
.siem-toggle-btn{
  position:relative;z-index:1;border:0;background:none;cursor:pointer;
  font-family:var(--display);font-weight:600;font-size:13.5px;
  padding:8px 18px;border-radius:100px;color:var(--fg-mut);
  transition:color .28s var(--ease);
}
.siem-toggle-btn[aria-pressed="true"]{color:var(--ink)}
.siem-toggle-btn:focus-visible{outline:2px solid var(--red);outline-offset:2px}

/* ---------- the ownership matrix ---------- */
.siem-matrix{margin:0;display:flex;flex-direction:column}
.siem-matrix-row{
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:14px;align-items:center;
  padding:13px 0;border-top:1px solid var(--line);
}
.siem-matrix-row:first-child{border-top:0}
.siem-matrix dt{font-size:14.5px;color:var(--fg-soft)}
.siem-matrix dd{margin:0;position:relative;display:grid;justify-items:end}

/* Both values occupy the same cell; only the active one is visible. Stacking
   them rather than removing one keeps every label in the DOM. */
.siem-owner{
  grid-area:1/1;display:inline-flex;align-items:center;gap:8px;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.04em;
  white-space:nowrap;
  opacity:0;transform:translateY(4px);
  transition:opacity .3s var(--ease), transform .3s var(--ease);
  transition-delay:calc(var(--r,0) * 28ms);
}
.siem-split[data-model="full"] .siem-owner[data-for="full"],
.siem-split[data-model="co"] .siem-owner[data-for="co"]{
  opacity:1;transform:translateY(0);
}
.siem-owner::before{
  content:"";width:7px;height:7px;border-radius:50%;flex:none;
  background:currentColor;
}
.siem-owner[data-tone="us"]{color:var(--red)}
.siem-owner[data-tone="both"]{color:var(--blue)}
.siem-owner[data-tone="you"]{color:var(--green)}
.siem-owner[data-tone="none"]{color:var(--fg-mut)}
.siem-owner[data-tone="none"]::before{background:none;box-shadow:inset 0 0 0 1px currentColor}

@media (max-width:880px){
  .siem-split{grid-template-columns:1fr}
  .siem-split-model{border-left:0;border-top:1px solid var(--line)}
}
@media (prefers-reduced-motion:reduce){
  .siem-toggle-pill,.siem-owner{transition:none}
}

/* ---------- the matrix, on dark ----------
   Overrides the light treatment above. --red fails on this ground at 2.8:1, so
   Wizard Cyber's rows take --red-3; --blue only reaches 3.9:1 so the shared
   state uses a lighter blue. Everything here clears AA for body text. */
.siem-split-model .siem-toggle{
  background:rgba(255,255,255,.04);border-color:var(--dk-line-2);
}
.siem-split-model .siem-toggle-pill{
  background:rgba(255,255,255,.10);
  box-shadow:0 2px 8px -2px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.14);
}
.siem-split-model .siem-toggle-btn{color:var(--dk-mut)}
.siem-split-model .siem-toggle-btn[aria-pressed="true"]{color:#fff}
.siem-split-model .siem-toggle-btn:focus-visible{outline-color:var(--red-3)}

.siem-split-model .siem-matrix-row{border-top-color:var(--dk-line)}
.siem-split-model .siem-matrix dt{color:var(--dk-soft)}
.siem-split-model .siem-owner[data-tone="us"]{color:var(--red-3)}
.siem-split-model .siem-owner[data-tone="both"]{color:var(--blue-dk)}
.siem-split-model .siem-owner[data-tone="you"]{color:var(--green-dk)}
.siem-split-model .siem-owner[data-tone="none"]{color:var(--dk-mut)}

@media (max-width:880px){
  .siem-split-model{border-top:1px solid var(--dk-line)}
}

/* ---------- the copy half, on dark ----------
   The card is now --dk -> --void, the same range Adam's .sec-dark uses, so
   --red behaves here exactly as it does on his pages: it clears the 3:1
   large-text threshold and is used on the heading accent, the way .sec-dark
   .grad does. It does NOT clear 4.5:1, so the 12.5px eyebrow and the link
   still take --red-3 — that size split is Adam's own rule, stated in the
   comment above .grad in main.css. */
.siem-split-copy .eyebrow{color:var(--red-3)}
.siem-split-copy .eyebrow::before{background:var(--red-3)}
.siem-split-copy h3{color:#fff}
.siem-split-copy h3 .accent{color:var(--red)}
.siem-split-copy .lede{color:var(--dk-fg)}
.siem-split-copy p{color:var(--dk-soft)}
.siem-split-copy .textlink{color:var(--red-3)}
.siem-split-copy .textlink:hover{color:#fff}

/* The console half sits DEEPER than the copy half, with a hairline between, so
   the split still reads without either side going light. It has to go below
   --void now that the card itself is --dk -> --void: an earlier value of
   #121620 was actually lighter than the card's own top stop, which inverted
   the relationship and flattened the divide. */
.siem-split-model{
  background:linear-gradient(180deg,var(--void),var(--void));
  border-left:1px solid var(--dk-line);
}

/* ---------- Managed SIEM: link transition ----------
   Three things move, at three durations, so it does not land as one step:
   the colour lifts, an underline draws in from the left, and the arrow steps
   right. Scoped to .siem-link rather than .textlink, which six pages share.

   The underline is a scaled pseudo-element rather than text-decoration —
   text-decoration cannot be animated, and animating width would reflow. */
.siem-link{
  position:relative;
  transition:color .26s var(--ease);
}
.siem-link span{position:relative}
.siem-link span::after{
  content:"";position:absolute;left:0;right:0;bottom:-3px;height:1px;
  background:currentColor;
  transform:scaleX(0);transform-origin:left center;
  transition:transform .38s var(--ease);
}
.siem-link svg{transition:transform .3s var(--ease)}

.siem-link:hover span::after,
.siem-link:focus-visible span::after{transform:scaleX(1)}
.siem-link:hover svg,
.siem-link:focus-visible svg{transform:translateX(5px)}
.siem-link:focus-visible{outline:2px solid var(--red-3);outline-offset:4px;border-radius:3px}

@media (prefers-reduced-motion:reduce){
  .siem-link,.siem-link span::after,.siem-link svg{transition:none}
  .siem-link:hover svg,.siem-link:focus-visible svg{transform:none}
  /* the underline still appears, it just does not draw */
  .siem-link:hover span::after,
  .siem-link:focus-visible span::after{transform:scaleX(1)}
}

/* ---------- prisms, narrow viewports ----------
   Below 860px the card stacks to one column, so it goes nearly full width AND
   much taller — 327 x 636 at 375px. The desktop placement puts both stones in
   the side gutters, and at that size there are none: the red one ended up 89%
   behind the card and the steel one 73% off the left edge.

   So they move into the bands above and below the card, which is the only
   space that exists here. The section gains a little padding to make those
   bands deep enough to hold them.

   The bleed is a fraction of each stone's OWN width, not a percentage of the
   section: -8% of the section was 30px at 375 but 60px at 768, while the
   stones cap out at 208px — so proportionally more of them slid off as the
   viewport grew. A fraction of --w holds the same overhang everywhere. */
@media (max-width:860px){
  .siem-defsec{padding-block:clamp(122px,16vw,150px)}

  /* red: top band, clipping the card's top-right corner */
  .siem-prism{
    --w:min(52vw,208px);
    width:var(--w);
    right:calc(var(--w) * -0.13);
    top:6px;
    transform:none;
  }

  /* steel: bottom band, mirrored to the left so the diagonal survives */
  .siem-prism-2{
    --w:min(40vw,158px);
    width:var(--w);
    right:auto;
    left:calc(var(--w) * -0.13);
    top:auto;bottom:10px;
    transform:none;
  }
}


/* ---------- Managed SIEM: the layered architecture ----------
   Section 4 was a 900px centred column of three paragraphs with nothing
   beside it — the emptiest block on the page, sitting on the most
   characteristic thing about the product. Copy moves left, the architecture
   goes right.

   The diagram is three boxes nested outside in, and the nesting IS the
   content: Sentinel is deployed into the estate, and the analysts work inside
   Sentinel. Every label but two comes from this section's own paragraphs.

   It is the third dark card on the page, so it has to have its own character.
   Section 1's definition card is a warm gradient with a red bloom and reads as
   a statement; section 3's split is a flat cool instrument. This one is a
   WELL: it sits below the section ground rather than above it, and gets
   brighter the further in you go, so the layer a buyer cannot assemble
   himself is the one that glows. */
.siem-arch{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.02fr);
  gap:clamp(30px,4.4vw,66px);align-items:center;
}

/* Layer 1 — your environment. Below --void, which is the darkest stop of the
   section behind it, so the whole object reads as cut into the ground. */
.siem-stack{
  position:relative;isolation:isolate;
  padding:clamp(18px,2vw,24px);
  border:1px solid var(--dk-line);border-radius:var(--radius-lg);
  background:var(--void);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
}
.siem-stack > *{position:relative;z-index:1}

/* ---------- S5.2 · the glow ----------
   Behind the CORE, not behind the whole card. A glow under everything would be
   decoration; under the innermost layer it points at the thing being sold, and
   it is the one place on this ground where brand red can sit at strength
   without reading as an alert. */
.siem-stack-glow{
  position:absolute;z-index:0;left:50%;bottom:0;
  width:min(84%,470px);height:270px;
  transform:translate(-50%,26%);
  background:radial-gradient(50% 50% at 50% 50%,
    rgba(195,0,23,.46),rgba(195,0,23,.12) 52%,transparent 72%);
  filter:blur(34px);
  pointer-events:none;
}

/* The layer name. Display face, sentence case — the mono uppercase micro-label
   is already doing work twice on this page (.siem-model-state, .siem-owner)
   and a third use would stop meaning anything. */
.siem-layer-lab{
  margin:0 0 12px;
  font-family:var(--display);font-weight:600;font-size:15px;
  letter-spacing:-.012em;color:#fff;
}

.siem-pills{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px}
.siem-pills li{
  font-size:13px;line-height:1;padding:9px 13px;
  border:1px solid var(--dk-line);border-radius:100px;
  background:rgba(255,255,255,.05);color:var(--dk-soft);
  white-space:nowrap;
}

/* Layer 2 — Microsoft Sentinel. One step lighter than the well. */
.siem-layer{
  margin-top:0;
  padding:clamp(14px,1.6vw,18px);
  border:1px solid var(--dk-line-2);border-radius:16px;
  background:rgba(255,255,255,.045);
}

/* Layer 3 — the SOC. Brightest box, brand edge, and the only red in the
   diagram apart from the flow dots. */
.siem-core{
  border-color:color-mix(in srgb,var(--red) 50%,transparent);
  background:
    linear-gradient(152deg,rgba(195,0,23,.26),rgba(195,0,23,.06) 56%,rgba(255,255,255,.05));
  border-radius:12px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.10);
}
.siem-core-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:9px}
.siem-core-list li{
  position:relative;padding-left:17px;
  font-size:14.5px;line-height:1.5;color:var(--dk-fg);
}
.siem-core-list li::before{
  content:"";position:absolute;left:0;top:.55em;
  width:6px;height:6px;border-radius:50%;
  background:var(--red-3);box-shadow:0 0 8px var(--red-3);
}

/* ---------- the flow connectors ----------
   The only motion in the block, and it carries the one thing the nesting
   cannot say on its own: direction. Logs land in the estate, get correlated by
   Sentinel, and end up in front of an analyst.

   The two dots are offset by half the cycle so the pipeline reads as
   continuous rather than as two things blinking in time. */
.siem-flow{position:relative;height:30px;margin:14px 0}
.siem-flow::before{
  content:"";position:absolute;left:50%;top:0;bottom:0;width:1px;
  transform:translateX(-50%);
  background:linear-gradient(180deg,transparent,var(--dk-line-2) 30%,var(--dk-line-2) 70%,transparent);
}
.siem-flow::after{
  content:"";position:absolute;left:50%;top:0;
  width:4px;height:4px;margin-left:-2px;border-radius:50%;
  background:var(--red-3);box-shadow:0 0 9px var(--red-3);
  animation:siem-drop 3s var(--ease) infinite;
}
.siem-flow.is-2::after{animation-delay:1.5s}
@keyframes siem-drop{
  0%{transform:translateY(0);opacity:0}
  18%{opacity:1}
  82%{opacity:1}
  100%{transform:translateY(30px);opacity:0}
}

@media (max-width:980px){
  .siem-arch{grid-template-columns:1fr;gap:34px}
  .siem-stack{max-width:620px;width:100%;margin:0 auto}
}
@media (max-width:520px){
  .siem-pills li{font-size:12.5px;padding:8px 11px}
  .siem-core-list li{font-size:14px}
  .siem-flow{height:24px;margin:12px 0}
  @keyframes siem-drop{
    0%{transform:translateY(0);opacity:0}
    18%{opacity:1}
    82%{opacity:1}
    100%{transform:translateY(24px);opacity:0}
  }
}
@media (prefers-reduced-motion:reduce){
  /* the line stays, the dot goes — a static dot parked at the top of a
     connector looks like a bug rather than a decision */
  .siem-flow::after{display:none}
}


/* ---------- THE BENEFIT CARDS                 .benefit-*  (S7.1, then N3.2)

   RENAMED FROM .siem-bcard ON 15 SEP, when the NDR page wanted the same
   treatment. Built for Managed SIEM §6 and named after it; "siem" became the
   wrong word once a second page used it. Used by: Managed SIEM §6, NDR §2.

   ----------------------------------------------------------------------------
   The register's P2: six text blocks, no icons, no internal hierarchy — the
   tallest section on the page and the least dense.

   .card and .ico are Adam's and are used as they are: the holder already had
   the thin border and the 46px box S7.1 described. This adds only what was
   actually missing — a top-down gradient so a card reads as an object rather
   than as a paragraph with a line round it, and a matching one inside the icon
   holder so the two agree.

   Scoped to .benefit-card. .card alone is shared by six pages and must not move. */
.benefit-card,
.estcard,
.sp{
  background:linear-gradient(180deg,var(--paper) 0%,var(--mist) 100%);
  /* Forces a stacking context so the texture below can sit at z-index 0 and
     the content at 1. Without it the pseudo-element paints over the text,
     because an absolutely positioned pseudo outranks in-flow content. */
  isolation:isolate;
}
.benefit-card > *,
.estcard > *,
.sp > *{position:relative;z-index:1}

/* ---------- the card texture ----------
   Graph paper behind the copy, faded out with a radial mask. Same technique as
   .hero-agent::after in main.css — paired 1px linear-gradients at a fixed
   background-size, masked — so this is the system's own device at card scale
   rather than a new one.

   Six background layers in one pseudo: four filled cells, then the horizontal
   and vertical rules. Only background-position changes per card.

   The cells are placed DETERMINISTICALLY, unlike the reference, which rolls
   Math.random() on every render — that would repaint a different texture on
   every page load and could differ between renders of the same page. Six fixed
   arrangements give the same "no two cards alike" effect and hold still.

   22px cell: the house grids are 34px and 40px, but those run across a whole
   hero. On a 381px card that reads as four fat squares; 22px reads as paper. */
.benefit-card::before,
.estcard::before,
.sp::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  --gl:rgba(15,20,35,.075);
  --sq:rgba(15,20,35,.042);
  /* THREE BRAND COLOURS IN THE GRID, not one. It was a single red cell among
     three neutrals; the site's accent rhythm everywhere else - the masthead
     dot, the dark-band auras, the stat rails below - is red, blue and green
     together, so the card texture uses the same three.

     Held at 7-9% alpha, which is the level the original red cell sat at and
     the level that lets a filled cell read as part of the paper rather than
     as a marker somebody left on it. Blue and green are a touch lighter than
     the red because both are more luminous at full strength and would
     otherwise pull the eye off it. */
  --sq-r:rgba(195,0,23,.085);
  --sq-b:rgba(47,107,255,.070);
  --sq-g:rgba(18,161,80,.070);
  background-image:
    linear-gradient(var(--sq-r),var(--sq-r)),
    linear-gradient(var(--sq-b),var(--sq-b)),
    linear-gradient(var(--sq-g),var(--sq-g)),
    linear-gradient(var(--sq),var(--sq)),
    linear-gradient(var(--gl) 1px,transparent 1px),
    linear-gradient(90deg,var(--gl) 1px,transparent 1px);
  background-size:22px 22px;
  background-repeat:no-repeat,no-repeat,no-repeat,no-repeat,repeat,repeat;
  background-position:154px 22px,198px 66px,176px 110px,220px 44px,0 0,0 0;
  /* Strongest at the top, gone before the paragraph — the copy never competes
     with the texture. The reference composites two masks to get this; one
     radial does the same job without mask-composite, which Chrome only
     supported unprefixed from 120. */
  -webkit-mask-image:radial-gradient(118% 76% at 50% 0,#000 16%,transparent 78%);
  mask-image:radial-gradient(118% 76% at 50% 0,#000 16%,transparent 78%);
  transition:opacity .3s var(--ease);
}

/* No two cards carry the same arrangement. Cells stay on 22px multiples so
   they land in the grid rather than straddling it. */


/* The filled cells are anchored to the RIGHT edge, not to a fixed x. Cards on
   this page are 584px, 327px or anything between depending on breakpoint, and
   absolute offsets that sat nicely on a 381px card either bunched at the left
   of a wide one or fell outside a narrow one. Right-anchored, one set of
   numbers holds everywhere.

   Still deterministic, one arrangement per card — no two alike, and stable
   across renders, unlike the reference which re-rolls Math.random() each time. */
.benefit-card:nth-child(1)::before{background-position:right 110px top 22px,right 44px top 66px,right 154px top 110px,right 66px top 44px,0 0,0 0}
.benefit-card:nth-child(2)::before{background-position:right 66px top 44px,right 132px top 88px,right 22px top 66px,right 110px top 110px,0 0,0 0}
.benefit-card:nth-child(3)::before{background-position:right 154px top 22px,right 44px top 110px,right 88px top 66px,right 22px top 88px,0 0,0 0}
.benefit-card:nth-child(4)::before{background-position:right 22px top 110px,right 88px top 22px,right 132px top 88px,right 66px top 44px,0 0,0 0}
.benefit-card:nth-child(5)::before{background-position:right 132px top 88px,right 66px top 44px,right 22px top 132px,right 110px top 66px,0 0,0 0}
.benefit-card:nth-child(6)::before{background-position:right 44px top 132px,right 110px top 22px,right 154px top 44px,right 88px top 110px,0 0,0 0}

/* Adam's .card:hover already lifts the card; the paper coming up slightly as
   it rises is the only thing added. */
.benefit-card::before{opacity:.78}
.benefit-card:hover::before{opacity:1}
/* The holder's flat --red-wash goes slightly deeper at the bottom, matching the
   card's own direction. Same light source, two nested objects. */
.benefit-card .ico{
  background:linear-gradient(180deg,var(--red-wash) 0%,var(--red-wash) 100%);
}
/* Adam's .card:hover already lifts the card. The icon holder picking up the
   brand edge at the same time ties the two together, and it is the only thing
   on the card that changes colour. */
.benefit-card .ico{transition:border-color .3s var(--ease),background .3s var(--ease)}
.benefit-card:hover .ico{
  border-color:color-mix(in srgb,var(--red) 34%,transparent);
  background:linear-gradient(180deg,var(--red-wash) 0%,#fbe3e7 100%);
}
@media (prefers-reduced-motion:reduce){
  .benefit-card .ico,.benefit-card::before{transition:none}
}


/* ---------- PROMOTING TWO OF THE SIX          (S7.2, then N3.2) ----------
   Six equal cards means nothing is important. Two leads carry the claims the
   rest of the page has been arguing; four compacts carry the supporting ones.

   Twelve columns so both rows divide cleanly: a lead takes six, a compact
   three. Replaces .cards-3 for this section only — .cards-3 is Adam's and is
   used elsewhere.

   No copy is shortened to make a card "compact". The paragraphs are all
   intact; the compacts get a tighter type scale and put the icon beside the
   heading instead of above it, which is where the height actually comes from. */
.benefit-grid{
  display:grid;grid-template-columns:repeat(12,1fr);gap:24px;
}
.benefit-card.is-lead{grid-column:span 6}
/* Two across, not the four the register described. At four the compacts are
   280px wide, which leaves a 224px text column — a 50-word paragraph becomes
   a tall ribbon and the demoted cards end up 82px TALLER than the promoted
   ones, inverting the hierarchy the change exists to create. The demotion is
   carried by the internal treatment below instead. */
.benefit-card:not(.is-lead){grid-column:span 6}

/* ONE TREATMENT FOR ALL SIX, and the reason is two lines above this: both
   tiers resolve to `grid-column:span 6`.

   The two-tier design was meant to give the first two cards prominence -
   stacked icon, 20-24px heading - against a compact rest at 16.5px with the
   icon inline. That works when the promoted card is BIGGER. These are the
   same width, and at equal width a different internal treatment does not read
   as hierarchy, it reads as a mistake: six identical boxes, two of which have
   inexplicably larger headings.

   The comment above records that four-across was abandoned because the
   compacts ended up TALLER than the leads, inverting the hierarchy. That was
   the signal. Going to two-across fixed the heights and removed the last
   thing that made the demotion legible, leaving only the inconsistency.

   So the generous treatment wins for all of them: a half-width card can carry
   a 20px heading, and the icon above rather than beside gives every card the
   same top-left anchor to scan from. `.is-lead` stays harmless in the markup
   in case a genuinely different size is wanted later. */
.benefit-card h3{font-size:clamp(19px,2vw,22px);line-height:1.2}

/* The texture's filled cells were placed for a 381px card. A lead is 608px and
   a compact 292px, so the same absolute positions would bunch at the left edge
   of one and fall outside the other. Each width gets its own spread. */

@media (max-width:720px){
  .benefit-card.is-lead,.benefit-card:not(.is-lead){grid-column:span 12}
}


/* ---------- the split FAQ block ----------          .faqsplit*
   Not a restyle of .faq — that class belongs to partials/faq.njk, which nine
   of Adam's pages share. Everything here is .faqsplit*, so his accordion is
   untouched on all of them, and the two blocks coexist by design rather than
   one replacing the other.

   RENAMED 17 SEP, from .siem-faq-*. ITDR is the second page to take it, and
   that is the same trigger that promoted .defcard, .benefit-card, .cta-centred
   and .sec-close out of page scope. The markup moved to
   partials/faq-split.njk in the same pass; nothing here changed but the names.

   Two changes of substance over the shared block:
     · The heading, and the lede that was centred under it with nothing to do,
       get a column of their own and stay in view while the answers scroll.
     · The items are hairlines on ONE surface rather than four bordered cards.
       Sections 1, 3, 4 and 6 are all card-shaped; a fifth set of cards stops
       carrying any meaning. */
.faqsplit{
  display:grid;grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);
  gap:clamp(30px,5vw,72px);align-items:start;
}
.faqsplit-head{position:sticky;top:96px}
.faqsplit-head h2{
  font-size:clamp(28px,3.4vw,40px);line-height:1.08;
  letter-spacing:-.025em;margin:16px 0 0;
}
.faqsplit-head .lede{max-width:38ch}
.faqsplit-head .lede a{
  color:var(--red);text-decoration:none;
  border-bottom:1px solid color-mix(in srgb,var(--red) 38%,transparent);
  transition:border-color .25s var(--ease);
  white-space:nowrap;
}
.faqsplit-head .lede a:hover{border-bottom-color:var(--red)}
.faqsplit-head .lede a:focus-visible{outline:2px solid var(--red);outline-offset:3px;border-radius:2px}

.faqsplit-panel{
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-sm);
  overflow:hidden;
}
/* The divider is the only thing between items, so the first one must not have
   one — otherwise it reads as a line under the panel's own top edge. */
.faqsplit-item + .faqsplit-item{border-top:1px solid var(--line)}
.faqsplit-item[open]{background:linear-gradient(180deg,var(--mist),var(--paper))}

.faqsplit-item summary{
  display:flex;align-items:flex-start;gap:20px;
  padding:21px clamp(20px,2.4vw,28px);
  cursor:pointer;list-style:none;
  font-family:var(--display);font-weight:600;font-size:16.5px;line-height:1.42;
  color:var(--ink);letter-spacing:-.01em;
  transition:color .25s var(--ease),background-color .25s var(--ease);
}
.faqsplit-item summary::-webkit-details-marker{display:none}
.faqsplit-item summary:hover{color:var(--red);background-color:var(--mist)}
.faqsplit-item summary:focus-visible{outline:2px solid var(--red);outline-offset:-3px}
.faqsplit-q{flex:1}

/* Chevron, same path as the shared accordion, turning a half circle on open.
   .45s rather than the house .28s: the answer beneath it now takes about that
   long to unfold, and an arrow that has finished turning while the panel is
   still moving reads as two separate events. */
.faqsplit-chev{
  width:18px;height:18px;flex:none;margin-top:4px;color:var(--red);
  transition:transform .45s var(--ease);
}
.faqsplit-item[open] .faqsplit-chev{transform:rotate(180deg)}

/* The outer box is what main.js animates, so it carries overflow and nothing
   else. All spacing lives on the inner one — padding on a box animating to
   zero height stays visible and the answer never closes flush. */
.faqsplit-a{overflow:hidden}
.faqsplit-a-in{padding:0 clamp(20px,2.4vw,28px) 24px;margin-top:-5px}
.faqsplit-a p{margin:0;color:var(--fg-soft);font-size:15.5px;line-height:1.72;max-width:70ch}

@media (max-width:940px){
  .faqsplit{grid-template-columns:1fr;gap:32px}
  .faqsplit-head{position:static}
  .faqsplit-head .lede{max-width:62ch}
}
@media (prefers-reduced-motion:reduce){
  .faqsplit-chev,.faqsplit-item summary{transition:none}
}


/* ---------- our closing CTA bands ----------          .cta-centred
   Shared by all three pages we have rebuilt: Managed SIEM (§8), SOC Triad (§3,
   T4.1) and NDR (§5, N6.1). The band itself is Adam's .ctaband, used
   unchanged — this is only the hook for our deltas, so his five files keep
   exactly the CTA they have.

   ONE CLASS, RENAMED 16 SEP. It was .siem-cta and .triad-cta sharing one rule
   through a selector list. The note here said that a third page wanting it
   would be the argument for promoting it properly rather than adding a third
   copy; NDR is that third page, so the two page-scoped names collapsed into
   one that says what it does. Three names for one identical fix was already
   one too many.

   .eyebrow is a flex row, so on a centred band it needs centring explicitly.
   The CyberShield page does this with style="justify-content:center" on the
   element; a class does the same thing without putting layout in the markup. */
.cta-centred .eyebrow{justify-content:center;align-items:flex-start}
/* With the inherited align-items:center the 28px dash centres against the
   whole wrapped block and ends up floating beside the MIDDLE line. Aligning to
   the top and nudging the dash down by half a line box puts it against the
   first line — and because that offset equals the centre of a single line box
   too, it stays correct when the text does not wrap.

   CORRECTED 15 Sep. This said the CyberShield page "never hits this: its
   eyebrow is four words", implying four words are safe. They are not. The SOC
   Triad eyebrow is also four words — "Responsive expertise, assured guidance"
   — and wraps to two lines at 390px, where it needs this fix exactly as much.
   What triggers it is rendered width against the container, not word count,
   and at these letter-spaced small caps that happens sooner than it looks. */
.cta-centred .eyebrow::before{margin-top:.74em}


/* ---------- Managed SIEM §4: the included-in-the-service callout ----------
   New copy rather than ported, and the only claim on the page Adam has named
   "the commercial point". Set apart from the ported paragraphs so it reads as
   the conclusion they build toward rather than a fourth paragraph.

   On this dark ground --red clears 3:1 only at 24px and up, so the label takes
   --red-3 at 12.5px. Same size-dependent rule as the eyebrows elsewhere. */
.siem-included{
  /* It sits inside .siem-arch, so it must be told to span both tracks;
     otherwise it becomes a third grid item and drops into column one. */
  grid-column:1 / -1;
  max-width:1000px;margin:clamp(30px,3.6vw,44px) auto 0;padding:18px 22px;
  border-left:2px solid var(--red);
  background:linear-gradient(90deg,rgba(195,0,23,.09),rgba(195,0,23,0) 72%);
  border-radius:0 8px 8px 0;
}
.siem-included-lab{
  margin:0 0 7px;
  font-family:var(--mono);font-size:11px;font-weight:700;
  letter-spacing:.13em;text-transform:uppercase;color:var(--red-3);
}
.siem-included p:last-child{
  margin:0;font-size:15px;line-height:1.66;color:var(--dk-fg);
}

/* ===========================================================================
   SOC TRIAD — the triad figure                       .triad-* (page-scoped)

   Rebuild of the Lottie the live page used to run, as plain SVG + CSS. The
   geometry and the source's own measurements are documented in
   partials/triad-figure.njk; this file dresses and animates it.

   IT IS DRESSED AS A CONSOLE, not as the old animation. Every other hero on
   the site puts a .console panel beside its headline, and this figure now
   sits on the same surface: the identical gradient, hairline and shadow, cut
   to a triangle instead of a rounded rectangle. The source's own palette has
   been dropped - it was drawn for a white band and had nothing to do with
   this system.

   The consoles' red scan sweep was tried here and taken out again. On a flat
   panel of data rows a 14% red band reads as a scanline; over three large
   red triangles it is invisible, and it was still invisible at 38%, nearly
   three times the console's value. The only places it registered were the
   recessed centre and the thin margin, which made it look like a rendering
   fault rather than a sweep. It was costing a permanent compositing layer to
   show nothing, so the plate carries the consistency on its own.

   NO JAVASCRIPT IS ADDED. The whole sequence hangs off the `in` class the
   existing scroll-reveal observer already sets on .reveal, so main.js gains
   nothing - which matters, because three gated blocks in there are already
   queued for Adam to look at (register item H2).

   The figure carries .reveal only to get itself observed. Its own fade and
   rise are cancelled below, because the triangles building themselves IS the
   reveal; running both would be two motions where the page wants one.
   =========================================================================== */
.triad{
  margin:0;width:100%;max-width:580px;justify-self:end;
  opacity:1;transform:none;transition:none;      /* cancel the generic reveal */
}
.triad svg{display:block;width:100%;height:auto}

/* ---------- the plate ----------
   .console, to the value: background linear-gradient(180deg,#141824,#0b0e15),
   border 1px solid var(--dk-line-2), border-radius 16px, and that very deep
   soft shadow. The radius is baked into the path as a 28-unit corner arc
   because SVG has no border-radius; 28 units is 16px at this figure's render
   scale. The shadow is a drop-shadow rather than a box-shadow so it follows
   the triangle instead of boxing it. */
.triad .t-plate{
  fill:url(#triad-plate-fill);
  stroke:var(--dk-line-2);stroke-width:1.8;
  filter:drop-shadow(0 30px 40px rgba(0,0,0,.8));
}


/* ---------- the four planes ----------
   Brand red on the console surface. The centre triangle is cut INTO the
   plate rather than laid on it - a shade below the plate's own dark end, with
   a hairline, so it reads as a recess. That is the console's own layering:
   panels sit above, wells sit below.

   The separators are the plate colour, not the hero colour, so the gaps read
   as the plate showing through between three pieces resting on it. */
.triad .t-shape{fill:var(--red)}
.triad .t-core .t-shape{fill:var(--void);stroke:var(--dk-line);stroke-width:1.4}
.triad .t-siem .t-shape,
.triad .t-ndr .t-shape,
.triad .t-edr .t-shape{stroke:var(--dk);stroke-width:2.6;stroke-linejoin:round}

/* ---------- icons and labels ----------
   Weights taken off the source rather than guessed. Its icons are 84 units
   across with a 12-unit stroke at 23.455% layer scale - an effective 2.81
   units, or 3.3% of the icon width. Drawn at scale(3.5) from a 24 box, that
   is stroke-width .8. The first pass here had 64-unit icons at 3.2 units,
   half again too heavy and noticeably clumsier beside the type.

   Labels are 44px: the source's "NDR" measures 100 units across at a 30-unit
   cap height, which is a 44px set. White on red clears AA at 5.9:1. The
   centre label takes --dk-soft's lighter relative, matching the console's
   own type on the same surface rather than sitting pure white on it. */
.triad .t-icon{
  fill:none;stroke:#fff;stroke-width:.8;
  stroke-linecap:round;stroke-linejoin:round;
}
.triad .t-label{
  fill:#fff;font-family:Montserrat,system-ui,sans-serif;font-weight:700;
  font-size:44px;letter-spacing:1px;text-anchor:middle;
}
.triad .t-core .t-label{font-size:44px;letter-spacing:1px;fill:var(--dk-fg)}

/* ---------- SIEM is a link ----------
   The original diagram was navigation: soc_triadscript.js put an onclick on
   the SIEM, EDR and NDR triangles. Only SIEM's target exists in the rebuild,
   so only SIEM is a link (content register B1). Hover changes the plane's
   colour rather than moving it - the triad is a fixed arrangement, and
   sliding one corner of it would break the shape. */
.triad a.t-p{cursor:pointer}
.triad a.t-p .t-shape{transition:fill .22s ease}
.triad a.t-p:hover .t-shape{fill:var(--red-2)}
.triad a.t-p:focus-visible{outline:none}
.triad a.t-p:focus-visible .t-shape{fill:var(--red-2);stroke:#fff;stroke-width:4}

/* ---------- the build ----------
   The source scales each triangle from zero on a five-frame stagger at 25fps,
   in the order NDR, EDR, SOC TRIAD, SIEM - so the thing assembles from its
   base upwards - then draws the icon strokes on with trim paths and reveals
   the labels letter by letter (trim mode 2, "individually"). Order and
   sequence kept; timing compressed, because the source runs four seconds and
   this sits above the fold.

   Each triangle scales about its own layer origin, taken from the JSON rather
   than from the bounding box, because a triangle's box centre is not its
   visual centre and using it made the pieces drift as they grew. */
.triad .t-p{transform-box:view-box}
.triad .t-ndr {--d:0s;    transform-origin:180.7px 493px}
.triad .t-edr {--d:.11s;  transform-origin:542.2px 487.9px}
.triad .t-core{--d:.22s;  transform-origin:361.5px 406px}
.triad .t-siem{--d:.33s;  transform-origin:365px 198.8px}

.triad .t-plate{opacity:0}
.triad .t-shape{transform:scale(0);transform-box:view-box;transform-origin:inherit}
.triad .t-icon{opacity:0}
.triad .t-icon>*{stroke-dasharray:1;stroke-dashoffset:1}
.triad .t-label tspan{opacity:0}
.triad .t-corelabel{transform:scale(0);transform-box:fill-box;transform-origin:center}

/* The plate arrives first and on its own, so the triangles have something to
   land on rather than assembling in mid-air. */
.triad.in .t-plate{animation:triInk .5s ease-out both}
.triad.in .t-shape{animation:triBuild .58s cubic-bezier(.22,.72,.3,1) calc(var(--d) + .18s) both}
/* pathLength="1" on every icon primitive normalises the dash maths, so paths,
   rects, circles and ellipses of wildly different real lengths all draw on in
   the same time instead of finishing whenever they happen to. */
.triad.in .t-icon>*{animation:triDraw .62s ease-out calc(var(--d) + .58s) both}
.triad.in .t-icon{animation:triInk .4s ease-out calc(var(--d) + .58s) both}
/* --i is the letter's index, set per tspan in the markup. This is the source's
   trim mode 2 - the letters arrive one at a time, not the word as a block. */
.triad.in .t-label tspan{animation:triInk .34s ease-out calc(var(--d) + .64s + var(--i) * .04s) both}
/* The source pops the centre label from 0 to full in 0.28s, on its own beat
   after its triangle lands. It is the only element with a scale of its own,
   so it stays the thing the eye finishes on. */
.triad.in .t-corelabel{animation:triPop .3s cubic-bezier(.2,.8,.3,1.02) calc(var(--d) + .52s) both}

@keyframes triBuild{from{transform:scale(0)}to{transform:scale(1)}}
@keyframes triPop{from{transform:scale(0)}to{transform:scale(1)}}
@keyframes triDraw{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}
@keyframes triInk{from{opacity:0}to{opacity:1}}

/* ---------- responsive ----------
   .hero-grid drops to one column at 1080px (main.css), not at a breakpoint of
   our own choosing - so this has to match it exactly or the figure re-centres
   while it is still sitting in a side column. */
@media (max-width:1080px){
  .triad{justify-self:center;max-width:480px;margin-top:12px}
}
@media (max-width:560px){
  .triad{max-width:380px}
  /* the svg is scaling down, so the line work has to scale back up to survive */
  .triad .t-icon{stroke-width:1.05}
  .triad .t-label{font-size:48px}
  /* the centre label is NOT bumped: it is the one label boxed in by its own
     triangle, and scaling it up is what ran TRIAD into the red edges. */
  .triad .t-plate{filter:drop-shadow(0 18px 26px rgba(0,0,0,.8))}
}

/* ---------- reduced motion ----------
   The finished diagram, immediately, and no scan at all - a sweep that loops
   forever is exactly what this setting is asking us to stop. */
@media (prefers-reduced-motion:reduce){
  .triad .t-shape,.triad .t-corelabel{transform:none}
  .triad .t-plate,.triad .t-icon,.triad .t-label tspan{opacity:1}
  .triad .t-icon>*{stroke-dashoffset:0}
  .triad a.t-p .t-shape{transition:none}
  .triad.in .t-plate,.triad.in .t-shape,.triad.in .t-icon,.triad.in .t-icon>*,
  .triad.in .t-label tspan,.triad.in .t-corelabel{animation:none}
}

/* ---------------------------------------------------------------------------
   THE DEFINITION CARD                          .defcard*  (T2.1, then N2.1)

   The Managed SIEM answer box, minus the illustration, carrying a whole
   section rather than sitting above it. A dark card dropped onto a light
   band, so the definition reads as a stated fact rather than as more page
   copy.

   RENAMED FROM .triad-def* ON 15 SEP. It was built for the SOC Triad page and
   named after it; the NDR page then wanted the same treatment (N2.1) and
   "triad" became the wrong word for a card that has nothing to do with a
   triad. One shared component under a name that describes what it is.

   Used by: SOC Triad §1, NDR §1. Managed SIEM has its own .siem-def, which
   differs — it keeps the two-column split with an illustration on the right.
   THREE pages now want this shape, which is the argument for H3: promoting it
   into main.css properly rather than holding it in a page stylesheet.

   .triad-legs still carries its old name on purpose — it holds EDR, NDR and
   SIEM, so "triad" is accurate for that part even inside a generic card.

   IT REPLACED TWO BLOCKS. There was a definition card and then a separate
   "What is the SOC Triad?" splitrow asking the same question a screenful
   later. The card now holds the eyebrow, the heading, the definition and the
   three body paragraphs, so the question is asked once.

   WHY OUR OWN CLASSES AND NOT .siem-def. Same reason the FAQ got its own
   block: these are page-scoped names, and borrowing .siem-def here would mean
   any change Adam or we make for that page silently lands on this one. The
   values are duplicated on purpose. Worth noting though that this is now the
   SECOND page to want this exact treatment, which is the argument for
   promoting it into the design system properly - register item H3.

   NO FIGURE AND NO PRISM. The SIEM card puts the many-in/few-out illustration
   in its right column and mounts two canvases that main.js paints. Here the
   right column is the body copy, which is what the splitrow was already
   doing, and there is no canvas and no JavaScript.
   --------------------------------------------------------------------------- */
.defcard-sec{
  position:relative;overflow:hidden;
  /* Less vertical room than .siem-defsec, which is padded out so the prism has
     bands above and below the card to show in. Nothing has to show here. */
  padding-block:clamp(72px,8vw,112px);
  background:linear-gradient(180deg,var(--bg-2),var(--bg));
}

/* The separator, same construction as the SIEM one and for the same reason:
   this section ends on --bg and the .sec-alt below it starts on --bg-2, which
   are close enough to run together. A flat grey hairline would fix it and read
   as a default. This is a red rule that is thick at the centre and gone by the
   ends, tapered with a mask rather than a clip-path - on a 4px band a hard
   diagonal edge stair-steps, where a mask's falloff antialiases.
   No rule on top: the hero above is dark, so the change in value already does
   that job and a line would only fence it off. */
.defcard-sec::after{
  content:"";position:absolute;z-index:2;pointer-events:none;
  left:0;right:0;bottom:0;height:4px;
  background:linear-gradient(90deg,
    rgba(195,0,23,1) 0%,
    rgba(226,16,41,1) 50%,
    rgba(195,0,23,1) 100%);
  -webkit-mask-image:radial-gradient(ellipse 52% 50% at 50% 50%,
    #000 0%, #000 28%, rgba(0,0,0,.58) 66%, rgba(0,0,0,.22) 88%, transparent 100%);
  mask-image:radial-gradient(ellipse 52% 50% at 50% 50%,
    #000 0%, #000 28%, rgba(0,0,0,.58) 66%, rgba(0,0,0,.22) 88%, transparent 100%);
}

/* align-items:start, not center. The two columns are different heights - the
   heading side is short, the body side is three paragraphs - and centring them
   would float the heading down the card away from its own eyebrow. */
.defcard{
  position:relative;z-index:1;overflow:hidden;
  display:grid;grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);
  gap:clamp(28px,4vw,56px);align-items:start;
  background:linear-gradient(152deg,var(--dk-panel) 0%,var(--void) 78%);
  border:1px solid var(--dk-line);border-radius:var(--radius-lg);
  padding:clamp(30px,4vw,56px);
  box-shadow:0 34px 78px -38px rgba(12,18,38,.55);
}
/* Brand red on dark as a diffuse glow, never as an alert state. Kept high and
   right, off the heading, and pulled back from the SIEM card's .30 because
   there is body copy under it here rather than a figure. */
.defcard::before{
  content:"";position:absolute;right:-12%;top:-40%;width:58%;height:170%;
  background:radial-gradient(closest-side,rgba(195,0,23,.20),transparent 72%);
  pointer-events:none;
}
.defcard-copy,.defcard-body{position:relative;z-index:1}

.defcard-q{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--red-3);font-weight:700;margin:0 0 16px;
}
/* h2 defaults to --ink, which is invisible on this panel. And .accent is
   --red: on the card's lightest point (#151925) that measures 2.79:1, under
   the 3:1 large-text floor, so it takes --red-3 here at 5.04:1. Adam's
   documented rule, and the same swap .siem-def-q makes. */
.defcard h2{
  color:#fff;font-size:clamp(28px,3.4vw,42px);margin:0 0 20px;
}
.defcard h2 .accent{color:var(--red-3)}

.defcard-a{
  margin:0;font-family:var(--display);font-weight:500;
  font-size:clamp(17px,1.75vw,20.5px);line-height:1.52;letter-spacing:-.012em;
  color:var(--dk-fg);
}
.defcard-a strong{font-weight:700;color:#fff}

/* The body copy sits a step back from the definition - --dk-soft rather than
   --dk-fg, and set smaller - so the card has a clear first and second voice
   rather than two paragraphs of equal weight competing. */
.defcard-body p{
  margin:0 0 16px;font-size:15.5px;line-height:1.72;color:var(--dk-soft);
}
.defcard-body p:last-child{margin-bottom:0}

/* Matches .siem-def's own collapse point. */
@media (max-width:860px){
  .defcard{grid-template-columns:1fr;gap:26px}
}

/* ---------- T2.2 · the three legs ----------
   The first body paragraph was three one-line sentences describing the three
   services, which is a three-part structure written as prose. Set as three
   parts it previews the triad before §2 explains it, using the page's own
   words.

   Spans both columns rather than sitting inside the body column: at full card
   width each leg gets a comfortable measure, and side by side they read as a
   set. Inside the right column they would have been three cramped stacks.

   The keys are display-weight white, NOT the mono red the eyebrow uses. There
   is already one mono red label on this card; three more would make four
   competing for the same job, and these are names rather than a section
   label. */
.triad-legs{
  grid-column:1/-1;position:relative;z-index:1;
  list-style:none;margin:clamp(26px,3vw,36px) 0 0;
  padding:clamp(24px,2.8vw,32px) 0 0;
  border-top:1px solid var(--dk-line);
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(20px,2.6vw,38px);
}
.triad-leg-k{
  display:block;font-family:var(--display);font-weight:700;
  font-size:16.5px;letter-spacing:-.01em;color:#fff;margin:0 0 8px;
}
.triad-legs p{margin:0;font-size:14.5px;line-height:1.65;color:var(--dk-soft)}

/* Collapses with the card, at .siem-def's own breakpoint. */
@media (max-width:860px){
  .triad-legs{grid-template-columns:1fr;gap:20px;
    padding-top:22px;margin-top:24px}
}

/* ---------------------------------------------------------------------------
   SOC TRIAD — §2, the three services as a triad     .triad-explore*  (T3.1–3.4)

   The three services on the vertices of a triangle, one SOC at the centre,
   each node selecting to show its own description beside the diagram. Replaces
   three identical cards in a row, which was the page's core diagnosis: a row
   is not a triad.

   DELIBERATELY A DIFFERENT DRAWING FROM THE HERO. The hero figure is solid
   filled triangles - an emblem. This is nodes and spokes - a map you can use.
   Same arrangement, different register, so the page states the idea twice in
   two voices rather than printing the same picture 1,500px apart.

   NO JAVASCRIPT. Selection is three radio inputs and `~`. Everything below is
   `#ts-x:checked ~ …`, which is why the inputs have to stay first in the
   container and why the panels have to come after them.
   --------------------------------------------------------------------------- */
.triad-explore{
  position:relative;
  display:grid;grid-template-columns:minmax(0,.86fr) minmax(0,1.14fr);
  gap:clamp(26px,4.5vw,62px);align-items:center;
  margin:clamp(30px,4vw,50px) 0 0;
}

/* Off-view, not display:none — that would drop them out of the tab order and
   take the arrow-key navigation with it. A radio group is exactly the right
   control here: pick one of three. */
.triad-r{
  position:absolute;top:0;left:0;width:1px;height:1px;
  opacity:0;margin:0;pointer-events:none;
}

/* ---------- the diagram ---------- */
.triad-map{position:relative;width:100%;max-width:440px;margin:0 auto;aspect-ratio:1}
.triad-wires{position:absolute;inset:0;width:100%;height:100%}
/* The perimeter marches, slowly and in one direction. It is the one piece of
   motion here that nobody triggers, and it earns that by meaning something:
   the three services watch a boundary continuously, which is the section's
   whole claim. Kept at roughly 8px a second so it reads as attention rather
   than as a loading state.

   Gated on no-preference rather than switched off under reduce, which is the
   same way .siem-defsec handles its motion: resting state first, keyframes
   attached only if the reader has not asked otherwise. */
.tw-edge{fill:none;stroke:var(--line-2);stroke-width:.45;stroke-dasharray:2.2 2.8}
@media (prefers-reduced-motion:no-preference){
  .tw-edge{animation:triMarch 2.6s linear infinite}
}
@keyframes triMarch{to{stroke-dashoffset:-5}}

/* The three spokes stay, faintly: they are what says all three run from one
   place. They no longer recolour on selection — the hand does that job, and
   two things going red at once made it ambiguous which was the indicator. */
.tw-spoke{fill:none;stroke:var(--line);stroke-width:.6;stroke-linecap:round}

/* The hand. Pivots on the hub centre; 0deg points at the apex, which is why
   EDR is the default selection and needs no rule of its own.

   The sweep is deliberately unhurried and lands rather than bounces. Two of
   the six transitions are the long way round — NDR to SIEM and back travel
   234deg through the apex, because with three fixed angles there is no set of
   values that makes all six take the short path without JavaScript tracking
   which way it last went. Through the top is the better of the two long
   paths: it passes over a node rather than across empty ground. */
.triad-hand{
  fill:none;stroke:var(--red);stroke-width:1.35;stroke-linecap:round;
  transform-box:view-box;transform-origin:50px 57.33px;
  transform:rotate(0deg);
  transition:transform .62s cubic-bezier(.62,.04,.24,1);
}
#ts-siem:checked ~ .triad-map .triad-hand{transform:rotate(120deg)}
#ts-ndr:checked  ~ .triad-map .triad-hand{transform:rotate(-120deg)}

.triad-hub{
  position:absolute;left:50%;top:57.33%;transform:translate(-50%,-50%);
  width:27%;aspect-ratio:1;border-radius:50%;
  display:grid;place-content:center;text-align:center;line-height:1.15;
  background:var(--ink);color:#fff;
  box-shadow:0 14px 30px -12px rgba(15,20,35,.55);
}
.triad-hub b{display:block;font-family:var(--display);font-weight:800;font-size:clamp(15px,1.9vw,21px);letter-spacing:-.02em}
.triad-hub span{display:block;font-family:var(--mono);font-size:clamp(8.5px,.95vw,10px);letter-spacing:.1em;color:var(--dk-soft);margin-top:4px}

/* ---------- the nodes ---------- */
/* The node box IS the dot, so the dot lands exactly on the vertex. The label
   is positioned out of it rather than stacked with it: a flex column centred
   on the vertex pushes the dot off the point by half the label's height, and
   on the apex it put the label straight through its own spoke. Labels hang
   outward — above the top node, below the bottom two — so none of them
   crosses a line. */
.triad-node{
  position:absolute;transform:translate(-50%,-50%);
  width:clamp(48px,5.4vw,62px);height:clamp(48px,5.4vw,62px);
  cursor:pointer;text-align:center;
}
.nd-edr {left:50%;top:18%}
.nd-ndr {left:15.94%;top:77%}
.nd-siem{left:84.06%;top:77%}

/* .42s, not .28s: the colours now change while the hand is still travelling,
   and a snap that finished long before the hand arrived read as two unrelated
   events rather than one. Still shorter than the hand's .62s, so the node is
   lit by the time the hand lands on it. */
.triad-dot{
  position:relative;
  width:100%;height:100%;border-radius:50%;
  display:grid;place-items:center;
  background:var(--paper);border:2px solid var(--line-2);color:var(--ink);
  transition:background .42s var(--ease),border-color .42s var(--ease),
             color .42s var(--ease),transform .42s var(--ease),box-shadow .42s var(--ease);
}
/* A ring that opens out of the dot as it is selected — the confirmation that
   the click landed, on the thing that was clicked. */
.triad-dot::after{
  content:"";position:absolute;inset:-10px;border-radius:50%;
  border:1px solid rgba(195,0,23,.38);
  opacity:0;transform:scale(.7);
  transition:opacity .42s var(--ease),transform .42s var(--ease);
}
.triad-dot svg{width:52%;height:52%}
.triad-k{
  position:absolute;left:50%;transform:translateX(-50%);white-space:nowrap;
  font-family:var(--display);font-weight:700;font-size:15px;letter-spacing:-.01em;
  /* 1.2, not the inherited body 1.6: these are one-word labels, and the extra
     leading was the last 2px pushing the apex label out of the box on a
     290px-wide map. */
  line-height:1.2;
  color:var(--fg-mut);transition:color .34s var(--ease);
}
.nd-edr  .triad-k{bottom:calc(100% + 9px)}
.nd-ndr  .triad-k,
.nd-siem .triad-k{top:calc(100% + 9px)}
.triad-node:hover .triad-dot{border-color:var(--red);transform:scale(1.06)}
.triad-node:hover .triad-k{color:var(--ink)}

/* ---------- selected state ----------
   One rule set per service because CSS has no way to say "the node that
   matches the input that is checked". Three services, three triplets. */
#ts-edr:checked  ~ .triad-map .nd-edr  .triad-dot,
#ts-ndr:checked  ~ .triad-map .nd-ndr  .triad-dot,
#ts-siem:checked ~ .triad-map .nd-siem .triad-dot{
  background:var(--red);border-color:var(--red);color:#fff;transform:scale(1.1);
  box-shadow:0 12px 26px -10px rgba(195,0,23,.6);
}
#ts-edr:checked  ~ .triad-map .nd-edr  .triad-k,
#ts-ndr:checked  ~ .triad-map .nd-ndr  .triad-k,
#ts-siem:checked ~ .triad-map .nd-siem .triad-k{color:var(--red)}

#ts-edr:checked  ~ .triad-map .nd-edr  .triad-dot::after,
#ts-ndr:checked  ~ .triad-map .nd-ndr  .triad-dot::after,
#ts-siem:checked ~ .triad-map .nd-siem .triad-dot::after{opacity:1;transform:scale(1)}

/* Keyboard focus lands on the input, which is off-view, so the ring is drawn
   on the dot the input controls. */
#ts-edr:focus-visible  ~ .triad-map .nd-edr  .triad-dot,
#ts-ndr:focus-visible  ~ .triad-map .nd-ndr  .triad-dot,
#ts-siem:focus-visible ~ .triad-map .nd-siem .triad-dot{
  outline:3px solid var(--red);outline-offset:3px;
}

/* ---------- the panel ---------- */
/* All three panels occupy the SAME grid cell, so the container is always as
   tall as the tallest one and the diagram beside it never moves. Toggling
   display:none instead left a 106px spread between the shortest panel and the
   longest — clicking a node shifted the node you had just clicked.

   Hidden with visibility rather than display: it keeps them out of the tab
   order and away from screen readers while still contributing their height,
   and pointer-events:none stops the SIEM button being clickable while its
   panel is the hidden one. All three stay in the markup either way. */
.triad-panels{display:grid}
.triad-panel{
  grid-area:1/1;
  visibility:hidden;opacity:0;pointer-events:none;transform:translateY(8px);
  /* Quick out. The incoming panel below waits for this to finish before it
     starts, because a straight cross-fade runs both sets of text through each
     other in the same grid cell and you can read both at once. */
  transition:opacity .16s var(--ease),transform .16s var(--ease),visibility .16s;
  background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius-lg);padding:clamp(24px,2.9vw,38px);
  box-shadow:0 22px 48px -30px rgba(15,20,35,.42);
  align-self:start;
}
#ts-edr:checked  ~ .triad-panels .pn-edr,
#ts-ndr:checked  ~ .triad-panels .pn-ndr,
#ts-siem:checked ~ .triad-panels .pn-siem{
  visibility:visible;opacity:1;pointer-events:auto;transform:none;
  /* …and slow in, after the outgoing one has cleared. */
  transition:opacity .3s var(--ease) .15s,transform .3s var(--ease) .15s,visibility 0s;
}

.triad-panel h3{margin:0 0 12px;font-size:clamp(21px,2.3vw,27px)}
.triad-panel p{margin:0;font-size:15.5px;line-height:1.7;color:var(--fg-mut)}
.triad-panel .btn{margin-top:22px}

/* ---------- the joining statement ----------
   T3.3: this was a second centred paragraph stacked above the cards. It is the
   claim the diagram makes, so it sits under the diagram. */
.sec-close{
  max-width:64ch;margin:clamp(30px,3.6vw,46px) auto 0;
  text-align:center;font-size:clamp(15.5px,1.55vw,17.5px);
  line-height:1.7;color:var(--fg);
}

/* ---------- responsive ---------- */
@media (max-width:900px){
  .triad-explore{grid-template-columns:1fr;gap:34px}
  .triad-map{max-width:360px}
}
@media (max-width:560px){
  .triad-map{max-width:290px}
  .triad-dot{width:46px}
  .triad-k{font-size:13.5px}
  .nd-edr .triad-k{bottom:calc(100% + 7px)}
  .nd-ndr .triad-k,.nd-siem .triad-k{top:calc(100% + 7px)}
  .triad-panel{padding:22px}
}

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  .triad-dot,.triad-dot::after,.triad-k,.triad-hand{transition:none}
  .tw-edge{animation:none}
  .triad-panel{transition:none;transform:none}
  .triad-node:hover .triad-dot{transform:none}
}

/* ---------- data-source logo band ----------
   Two rows travelling in OPPOSITE directions. One row scrolling on its own
   reads as a ticker; two in opposition read as breadth, which is the claim the
   section is making. The edge fade comes from the shared .marquee mask.

   The logos are a mix of square marks (Google Cloud, SentinelOne) and wide
   wordmarks (AWS, Vectra), so they are normalised on HEIGHT with a generous
   max-width - matching on width would shrink the wordmarks to nothing and blow
   the marks up. Greyscale at rest so twelve brand palettes do not fight the
   page, full colour on hover. */
.logoband{margin-top:clamp(40px,5vw,64px);text-align:center}
.lb-k{display:block;font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--red);margin-bottom:14px}
.lb-h{font-size:clamp(22px,2.6vw,29px);letter-spacing:-.022em;margin:0 0 14px}
.lb-l{max-width:66ch;margin:0 auto clamp(26px,3vw,36px);color:var(--fg-soft);
  font-size:15.5px;line-height:1.65}
/* Reserve the row height. The images carry no width/height attributes, so
   without this the band would collapse and then jump as each one decodes. */
.logoband .marquee{padding:6px 0;min-height:76px}

/* Every logo gets the SAME BOX and is scaled to fit inside it. Normalising on
   height alone does not work for a set this mixed: Google Cloud and SentinelOne
   are square marks, AWS and Vectra are wide wordmarks, and at a shared height
   Vectra came out six times the width of Cisco. A fixed box with object-fit
   gives them equal visual weight and equal spacing, which is what makes a logo
   wall read as a wall rather than as a row of accidents. */
.logoband .marquee.logos .item{width:150px;height:64px;margin-right:34px;
  display:flex;align-items:center;justify-content:center}
/* Do NOT pause on hover. The shared .marquee does, which suits a short text
   ticker you might want to read - but a visitor moving the pointer across a
   wide logo row is almost never trying to stop it, and a band that freezes
   under the cursor reads as a page that has hung. */
.logoband .marquee:hover .track{animation-play-state:running}
.logoband .marquee.logos .item img{max-height:40px;max-width:130px;
  width:auto;height:auto;object-fit:contain;
  /* Full colour. These are the customer's own vendors and the point of the row
     is recognition - greyscale at 65% made them read as disabled. */
  filter:none;opacity:1}
.logoband .marquee + .marquee{margin-top:10px}
/* Reverse row. `animation-direction` rather than a second keyframe set, so both
   rows stay locked to the same duration and the pair cannot drift apart. */
.marquee.is-rev .track{animation-direction:reverse}
.lb-n{max-width:70ch;margin:clamp(22px,2.6vw,30px) auto 0;color:var(--fg-mut);
  font-size:13px;line-height:1.6}
@media (prefers-reduced-motion:reduce){
  /* Stop the travel, keep the logos. A row frozen mid-scroll would clip the
     first and last mark, so the track is centred and allowed to wrap. */
  .logoband .track{animation:none;width:auto;flex-wrap:wrap;justify-content:center;gap:24px 48px}
  .logoband .marquee{-webkit-mask-image:none;mask-image:none}
  .logoband .item[aria-hidden="true"]{display:none}
  .logoband .marquee.logos .item{margin-right:0}
}

/* ---------- article: contextual service link ----------
   Sits inside the article, under the prose. Deliberately quiet - a red rule and
   a heading rather than a card - because it is an editorial aside, not an ad,
   and a loud box here trains readers to skip it. */
/* No radius and no background here, so border-image is the whole job. It could
   NOT be used on the rounded panels above: border-image makes a browser drop
   border-radius completely. */
/* ---------------------------------------------------------------------------
   RELATED SERVICES on an article. The most important block on the page: it is
   the one that moves somebody from reading to a service page, and it used to
   be a bare text link under a guessed label while the related-ARTICLES section
   beneath it had proper cards. Rebuilt 17 Sep 2026.

   The signal band across the top is the site's own, so the block reads as part
   of the product rather than a promo strip. Cards lift on hover because they
   are the thing to click; the related-articles cards below deliberately stay
   quieter so the hierarchy is unambiguous.
   --------------------------------------------------------------------------- */
.artsvc{margin:clamp(38px,5vw,56px) 0 0;padding:clamp(26px,3vw,34px) 0 0;position:relative;
  border-top:2px solid transparent;border-image:var(--seam-grad) 1}
.artsvc-k{display:block;font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--red);margin-bottom:18px}
.artsvc-row{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(224px,1fr))}
.artsvc-row.is-1{grid-template-columns:minmax(0,1fr)}

.artsvc-card{position:relative;display:flex;flex-direction:column;gap:7px;
  padding:22px 22px 20px;border-radius:var(--radius-lg);text-decoration:none;color:inherit;
  background:linear-gradient(180deg,#12161f,#0d1017);border:1px solid var(--dk-line);
  overflow:hidden;transition:transform .28s var(--ease),border-color .28s,box-shadow .28s}
.artsvc-card::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--seam-grad);transform:scaleX(0);transform-origin:left;
  transition:transform .4s var(--ease)}
.artsvc-card:hover{transform:translateY(-4px);border-color:var(--dk-line-2);
  box-shadow:0 26px 60px -30px rgba(0,0,0,.85)}
.artsvc-card:hover::before{transform:scaleX(1)}
.artsvc-card:focus-visible{outline:2px solid var(--red-3);outline-offset:3px}

.artsvc-grp{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red-3)}
.artsvc-t{font-family:var(--display);font-weight:600;font-size:18.5px;letter-spacing:-.02em;
  color:#fff;line-height:1.25}
.artsvc-l{font-size:14px;line-height:1.55;color:var(--dk-mut);flex:1}
.artsvc-go{display:inline-flex;align-items:center;gap:7px;margin-top:6px;
  font-family:var(--display);font-weight:600;font-size:14px;color:var(--red-3)}
.artsvc-go svg{width:15px;height:15px;transition:transform .25s var(--ease)}
.artsvc-card:hover .artsvc-go svg{transform:translateX(4px)}

@media (max-width:560px){.artsvc-row{grid-template-columns:minmax(0,1fr)}}
@media (prefers-reduced-motion:reduce){
  .artsvc-card,.artsvc-card::before,.artsvc-go svg{transition:none}
  .artsvc-card:hover{transform:none}
}

/* ---------- homepage: the one customer quote ----------
   A quote, not a card. It sits between two card-heavy sections and boxing it
   would make it the third grid in a row; setting it large and open is what
   makes it read as somebody speaking rather than as another tile. */
.bigquote{max-width:940px;margin:0 auto;text-align:center}
.bq-k{display:block;font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--red);margin-bottom:20px}
.bigquote blockquote{margin:0;font-family:var(--display);font-weight:600;
  font-size:clamp(20px,2.7vw,30px);line-height:1.38;letter-spacing:-.022em;
  color:var(--ink);text-wrap:pretty}
.bigquote figcaption{margin-top:22px;display:flex;flex-direction:column;gap:2px}
.bigquote figcaption strong{font-size:15px;color:var(--ink)}
.bigquote figcaption span{font-size:13.5px;color:var(--fg-mut)}
.bigquote .textlink{margin-top:20px}

/* ---------- author page ---------- */
.authhead{display:grid;grid-template-columns:auto 1fr;gap:clamp(24px,4vw,44px);align-items:center;
  padding-top:clamp(18px,3vw,30px)}
.auth-img{width:clamp(110px,13vw,168px);height:auto;aspect-ratio:1;object-fit:cover;
  border-radius:50%;border:2px solid var(--dk-line-2);background:var(--dk-panel)}
.auth-cred{font-family:var(--mono);font-size:11.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--red-3);margin:0 0 14px}
@media (max-width:760px){
  .authhead{grid-template-columns:1fr;text-align:center;justify-items:center}
}
/* A byline photo, where the author has one. Same footprint as the initial
   avatar it replaces, so the meta row does not reflow. */
.av-img{border-radius:50%;object-fit:cover}

/* ---------------------------------------------------------------------------
   NDR — the hero figure, lateral movement       .ndrfig / .nf-*  (N1.1, v2)

   Geometry and reasoning are in partials/ndr-figure.njk; this dresses and
   animates it.

   IT LOOPS, ON TWO CLOCKS. Traffic moves continuously, because traffic does and
   watching it is the service. The breach re-traces on a slow 9s cycle, because
   that is the demonstration rather than the state. Running both at one speed
   made the intrusion look routine, which is the opposite of the point.

   NO JAVASCRIPT. The entrance hangs off the `in` class the existing scroll
   observer already sets; everything after is keyframes.
   --------------------------------------------------------------------------- */
.ndrfig{
  margin:0;width:100%;max-width:560px;justify-self:end;
  opacity:1;transform:none;transition:none;      /* cancel the generic reveal */
}
.ndrfig svg{display:block;width:100%;height:auto}

/* ---------- the plate ---------- */
.ndrfig .nf-plate{
  fill:url(#nf-plate);
  stroke:var(--dk-line-2);stroke-width:1.4;
  filter:drop-shadow(0 26px 36px rgba(0,0,0,.8));
}
/* The house lattice, at a scale that suits a 560px figure rather than a hero. */
.ndrfig .nf-lattice{stroke:none;pointer-events:none}

/* ---------- the three zones ----------
   The core is a shade lighter than the plate, so it reads as somewhere worth
   protecting before anything happens in it. The boundaries are dashed because
   a boundary something crosses is not a wall — and the whole point of the
   figure is that both of them get crossed. */
.ndrfig .nf-core{fill:rgba(255,255,255,.04)}
.ndrfig .nf-bound{
  fill:none;stroke:rgba(255,255,255,.2);stroke-width:1.3;
  /* 5/9 rather than 3/7: at the shorter dash the line read as noise on the
     lattice behind it instead of as a boundary. */
  stroke-dasharray:5 9;stroke-linecap:round;
}

/* ---------- the mesh ----------
   Two weights. One uniform opacity read as a wireframe; a real network does not
   have twelve equally important links. */
.ndrfig .nf-mesh{fill:none;stroke-linecap:round}
.ndrfig .nf-mesh-a{stroke:rgba(255,255,255,.20);stroke-width:1.5}
.ndrfig .nf-mesh-b{stroke:rgba(255,255,255,.10);stroke-width:1.2}

/* ---------- devices ----------
   A lit face, a hairline border and a small centre light. The first version
   drew flat squares with a single fill, which read as placeholders. */
.ndrfig .nf-dev rect{fill:url(#nf-face);stroke:rgba(255,255,255,.28);stroke-width:1.4}
/* Stands, feet and rack bays. Stroked rather than filled so a workstation and
   a rack are the same drawing language at two sizes. */
.ndrfig .nf-dev path{fill:none;stroke:rgba(255,255,255,.4);stroke-width:1.5;stroke-linecap:round}

/* The entry point: outlined in red, not filled. --red-3 rather than --red
   because this sits on a near-black plate, where #C30017 is 2.4:1 — Adam's
   rule for the dark surfaces. */
.ndrfig .nf-entry rect{fill:rgba(255,59,82,.08);stroke:var(--red-3);stroke-width:2}
.ndrfig .nf-entry path{fill:none;stroke:var(--red-3);stroke-width:1.8;stroke-linecap:round;opacity:.8}

/* ---------- the breach ---------- */
.ndrfig .nf-hop{fill:none;stroke:var(--red-3);stroke-width:2.4;stroke-linecap:round}
/* The blurred copy underneath. Wider and dimmer: it is a glow, not a second
   line, and at full opacity it fattens the path instead of lighting it. */
.ndrfig .nf-route-glow .nf-hop{stroke-width:5;opacity:.5}

/* ---------- the reached device ---------- */
.ndrfig .nf-hit-face rect{fill:var(--red-3)}
/* The bays go dark on a filled face, not light — otherwise the rack loses its
   shape at the exact moment it matters most. */
.ndrfig .nf-hit-face path{fill:none;stroke:rgba(20,5,9,.55);stroke-width:1.8;stroke-linecap:round}
.ndrfig .nf-hit-glow{fill:var(--red-3);opacity:.85}
.ndrfig .nf-ring{fill:none;stroke:var(--red-3);stroke-width:1.6}

/* ---------- resting state ----------
   Everything starts hidden and is brought in by the entrance below. Stated
   before any keyframes so the figure is correct with animation unavailable. */
.ndrfig .nf-plate,.ndrfig .nf-lattice,.ndrfig .nf-core,.ndrfig .nf-bound,
.ndrfig .nf-mesh,.ndrfig .nf-dev,.ndrfig .nf-traffic{opacity:0}
.ndrfig .nf-entry,.ndrfig .nf-hit{opacity:0}
/* pathLength="1" on every hop, set in the markup. WITHOUT it, stroke-dasharray
   of 1 means one USER UNIT — on a 154-unit path that is a dash pattern
   repeating 77 times, so the line renders as a dotted line that is already
   fully there and never draws. An earlier comment here claimed pathLength
   could not be used because Chrome ignores it alongside offset-path. That is
   wrong twice over: offset-path is on the traffic packets, not on these, and
   the two do not interact at all. */
.ndrfig .nf-hop{stroke-dasharray:1;stroke-dashoffset:1}

/* The head. Hidden until its run starts, and gone once it arrives. */
.ndrfig .nf-head{
  fill:#fff;opacity:0;
  filter:drop-shadow(0 0 6px var(--red-3)) drop-shadow(0 0 14px var(--red-3));
  offset-path:path('M118 230 278 302 372 380 524 330');
  offset-rotate:0deg;
}
.ndrfig .nf-ring{opacity:0}

/* ---------- entrance: once, on reveal ---------- */
.ndrfig.in .nf-plate{animation:nfIn .5s ease-out both}
.ndrfig.in .nf-lattice{animation:nfIn .7s ease-out .3s both}
.ndrfig.in .nf-core{animation:nfIn .6s ease-out .28s both}
.ndrfig.in .nf-bound{animation:nfIn .6s ease-out .36s both}
.ndrfig.in .nf-mesh{animation:nfIn .6s ease-out .42s both}
.ndrfig.in .nf-dev{animation:nfPop .4s cubic-bezier(.22,.8,.3,1) both}
.ndrfig.in .nf-dev:nth-of-type(1){animation-delay:.44s}
.ndrfig.in .nf-dev:nth-of-type(2){animation-delay:.50s}
.ndrfig.in .nf-dev:nth-of-type(3){animation-delay:.56s}
.ndrfig.in .nf-dev:nth-of-type(4){animation-delay:.62s}
.ndrfig.in .nf-dev:nth-of-type(5){animation-delay:.68s}
.ndrfig.in .nf-traffic{animation:nfIn .6s ease-out .9s both}
.ndrfig.in .nf-entry{animation:nfIn .4s ease-out 1.05s both}

@keyframes nfIn{from{opacity:0}to{opacity:1}}
@keyframes nfPop{from{opacity:0;transform:scale(.55)}to{opacity:1;transform:none}}
.ndrfig .nf-dev,.ndrfig .nf-hit{transform-box:fill-box;transform-origin:center}

/* ---------- the two loops ----------
   Gated on no-preference rather than switched off under reduce: resting state
   first, keyframes attached only if the reader has not asked otherwise. Same
   way .siem-defsec and the triad figure handle their motion. */
@media (prefers-reduced-motion:no-preference){

  /* Traffic. Each packet has its own link, its own pace and its own offset, so
     they never fall into step — four dots pulsing together would read as a
     progress bar rather than as a network. */
  /* Duration and delay live in the SAME rule as the shorthand, not in a
     separate lower-specificity one. Written the other way round, the
     shorthand on .ndrfig.in .nf-pk reset duration to the
     initial 0s and outranked a .ndrfig .pk1 { animation-duration } — so every
     packet had a keyframe, a path, and nowhere to go. */
  .ndrfig.in .pk1{animation:nfPacket 3.4s linear 0s   infinite}
  .ndrfig.in .pk2{animation:nfPacket 4.1s linear .7s  infinite}
  .ndrfig.in .pk3{animation:nfPacket 3.8s linear 1.6s infinite}
  .ndrfig.in .pk4{animation:nfPacket 4.6s linear 2.3s infinite}

  /* The breach, on its own much slower clock: draw, hold, fade, wait. The three
     hops share one keyframe set and chain on delay, so the fade cascades in the
     same order the path was drawn. 1.05s of the entrance is subtracted from the
     first delay so the first trace begins as the figure settles. */
  /* One hop at a time. They used to run 1.26s each starting 0.45s apart, so
     all three were drawing at once and the route appeared rather than
     travelled. Each now takes 0.54s and the next starts as the last lands.
     The first waits until 1.6s, which is after the entry node has finished
     arriving — a route cannot leave a device that is not there yet. */
  .ndrfig.in .nf-hop{animation:nfHop 9s linear infinite}
  .ndrfig.in .h1{animation-delay:1.6s}
  .ndrfig.in .h2{animation-delay:2.14s}
  .ndrfig.in .h3{animation-delay:2.68s}
  .ndrfig.in .nf-head{animation:nfHead 9s linear 1.6s infinite}
  .ndrfig.in .nf-hit{animation:nfHit 9s linear 3.2s infinite}
  .ndrfig.in .r1{animation:nfRing 9s linear 3.2s infinite}
  .ndrfig.in .r2{animation:nfRing 9s linear 3.5s infinite}
}

/* Draw over 1.26s, hold seven seconds, fade, wait. The hold is what stops this
   reading as a looping animation and lets it read as a state the page is in. */
@keyframes nfHop{
  0%   {stroke-dashoffset:1;opacity:1}
  6%   {stroke-dashoffset:0;opacity:1}
  78%  {stroke-dashoffset:0;opacity:1}
  88%  {stroke-dashoffset:0;opacity:0}
  100% {stroke-dashoffset:1;opacity:0}
}
/* The head covers all three hops in one run — 18% of the cycle is the 1.62s the
   three draws take together — then goes out at the device it reaches. */
@keyframes nfHead{
  0%   {offset-distance:0%;opacity:0}
  1.5% {opacity:1}
  16%  {opacity:1}
  18%  {offset-distance:100%;opacity:0}
  100% {offset-distance:100%;opacity:0}
}
@keyframes nfHit{
  0%      {opacity:0;transform:scale(.55)}
  5%      {opacity:1;transform:scale(1)}
  72%     {opacity:1;transform:scale(1)}
  82%,100%{opacity:0;transform:scale(1)}
}
/* Rings expand once each and stop, rather than pulsing for the whole hold —
   something spreading, not a beacon. */
@keyframes nfRing{
  0%      {opacity:0;transform:scale(.45)}
  4%      {opacity:.6}
  22%     {opacity:0;transform:scale(2)}
  100%    {opacity:0;transform:scale(2)}
}
.ndrfig .nf-ring{transform-box:fill-box;transform-origin:center}

/* transform-box:fill-box is REQUIRED on anything driven by offset-path here,
   and its absence is silent. SVG defaults to transform-box:view-box, which
   resolves the offset anchor against the whole 640x460 box rather than the
   element's own; the dot's cx/cy is then ADDED to the path position instead of
   being the point that rides it. Every packet was landing hundreds of units
   away — two of them off the plate entirely — while offset-distance reported a
   perfectly correct percentage the whole time. Measure the position, not the
   property. */
.ndrfig .nf-pk,.ndrfig .nf-head{transform-box:fill-box}
/* The paths themselves sit outside the motion query: a packet still needs to
   know where it lives even when it is not travelling. */
/* All four run INSIDE the internal zone: normal traffic between workstations,
   which is the thing the breach has to hide among. */
.ndrfig .pk1{offset-path:path('M300 118 392 205')}
.ndrfig .pk2{offset-path:path('M392 205 524 170')}
.ndrfig .pk3{offset-path:path('M278 302 372 380')}
.ndrfig .pk4{offset-path:path('M300 118 278 302')}
@keyframes nfPacket{from{offset-distance:0%}to{offset-distance:100%}}

/* ---------- responsive ----------
   .hero-grid drops to one column at 1080px in main.css, so this has to match or
   the figure re-centres while still in a side column. */
@media (max-width:1080px){
  .ndrfig{justify-self:center;max-width:480px;margin-top:14px}
}
@media (max-width:560px){
  .ndrfig{max-width:100%}
  .ndrfig .nf-plate{filter:drop-shadow(0 16px 22px rgba(0,0,0,.8))}
}

/* ---------- reduced motion ----------
   The finished picture, immediately and still: the breach drawn, the device
   flagged, the traffic parked on its links. It carries the same information
   without anything moving. */
@media (prefers-reduced-motion:reduce){
  .ndrfig .nf-plate,.ndrfig .nf-lattice,.ndrfig .nf-core,.ndrfig .nf-bound,
  .ndrfig .nf-mesh,.ndrfig .nf-dev,.ndrfig .nf-traffic,
  .ndrfig .nf-entry,.ndrfig .nf-hit{opacity:1}
  .ndrfig .nf-hop{stroke-dashoffset:0}
  .ndrfig .nf-ring,.ndrfig .nf-head{opacity:0}
  .ndrfig.in .nf-plate,.ndrfig.in .nf-lattice,.ndrfig.in .nf-core,
  .ndrfig.in .nf-bound,.ndrfig.in .nf-mesh,
  .ndrfig.in .nf-dev,.ndrfig.in .nf-traffic,.ndrfig.in .nf-entry,
  .ndrfig.in .nf-hit,.ndrfig.in .nf-hop,.ndrfig.in .nf-pk,
  .ndrfig.in .nf-head,.ndrfig.in .r1,.ndrfig.in .r2{animation:none}
}


/* ---------- report an incident: the numbers ----------
   The largest thing on the page, because the page has one job. Cards rather
   than links so the whole block is a tap target on a phone, which is where
   somebody reading this is most likely to be. */
.callrow{display:grid;grid-template-columns:1fr 1fr;gap:16px;max-width:760px;
  margin:clamp(24px,3vw,32px) 0 0}
.callcard{display:flex;flex-direction:column;gap:6px;padding:26px 28px;
  background:rgba(255,255,255,.05);border:1px solid var(--dk-line-2);
  border-radius:var(--radius-lg);text-decoration:none;
  transition:transform .25s var(--ease),border-color .25s,background .25s}
.callcard:hover{transform:translateY(-3px);border-color:var(--red);background:rgba(195,0,23,.12)}
.cc-k{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--dk-mut)}
.cc-n{font-family:var(--display);font-weight:700;font-size:clamp(22px,3vw,30px);
  letter-spacing:-.02em;color:#fff;line-height:1.15}
.cc-a{font-size:13px;font-weight:600;color:var(--red-3)}
@media (max-width:640px){.callrow{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.callcard:hover{transform:none}}

/* ---------- agent x package matrix ----------
   ONE rule block. There used to be two - an original and a later "tighter,
   scannable" revision that never removed the first - so half these properties
   were being set twice with different values, which is how the name column
   ended up 796px of a 1177px table.

   IT SITS ON ITS OWN PANEL. Floating the table straight onto the dark section
   left it washed out: the aura and the mesh showed through every cell, so the
   rows had nothing solid to sit on and the whole grid read as a stain rather
   than a table. A near-black panel gives every value the same background and
   is what actually fixes the contrast - brightening the text alone did not.

   THE RECOMMENDED COLUMN IS LIGHTER, NOT REDDER. It was a 10% red wash, which
   on a dark section already tinted red produced mud: the column looked dirty
   rather than picked out. Lifting it with white at low alpha and marking the
   header with a red rule reads as emphasis instead. */
.matrix{width:100%;border-collapse:collapse;table-layout:fixed;
  font-size:14px;min-width:620px;
  background:#0b0f16;border-radius:var(--radius-lg);overflow:hidden}
/* 40% to the agent name, the rest split evenly between the three packages. */
.matrix thead th:first-child,.matrix tbody th{width:40%}
.matrix thead th.pk{width:20%}

.matrix th,.matrix td{padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.07)}
.matrix tbody th{text-align:left;font-weight:500;font-size:14.5px;color:var(--dk-fg);
  font-family:var(--body)}
.matrix td{text-align:center}
/* A stripe, kept very low: enough to follow a row across four columns without
   turning the table into a barcode. */
.matrix tbody tr:nth-child(even of :not(.grouprow)){background:rgba(255,255,255,.018)}

.matrix thead th{font-family:var(--display);font-weight:700;font-size:16px;color:#fff;
  padding:18px 16px;background:rgba(255,255,255,.04);
  border-bottom:1px solid rgba(255,255,255,.14)}
.matrix thead th:first-child{font-family:var(--mono);font-size:10.5px;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--dk-mut);text-align:left}
.matrix thead th small{display:block;font-family:var(--mono);font-size:9.5px;
  letter-spacing:.14em;color:var(--dk-mut);font-weight:400;margin-top:5px}

/* Group headings are a th/colgroup now, not a styled td - a screen reader
   announcing "Front line" as a column-group heading is the correct reading. */
.matrix .grouprow th{text-align:left;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:var(--red-3);
  background:rgba(255,255,255,.055);padding:9px 16px;
  border-bottom:1px solid rgba(255,255,255,.07)}

.matrix tbody tr:last-child th,.matrix tbody tr:last-child td{border-bottom:0}
.matrix .inc{color:#3ecf8e;font-size:17px;font-weight:700;line-height:1}
.matrix .add{display:inline-grid;place-items:center;width:23px;height:23px;border-radius:50%;
  border:1px solid rgba(255,255,255,.22);color:var(--dk-soft);font-size:14px;font-weight:600;line-height:1}
.matrix .no{color:var(--dk-mut);opacity:.45}
.matrix .ext{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:#3ecf8e}

/* The recommended column: lifted, with a red rule on the header. */
.matrix .feat-col{background:rgba(255,255,255,.045)}
.matrix thead th.feat-col{background:rgba(255,255,255,.08);
  box-shadow:inset 0 3px 0 var(--red)}
.matrix tbody tr:nth-child(even of :not(.grouprow)) .feat-col{background:rgba(255,255,255,.058)}

/* Legend. Real elements reusing the cell classes, so a swatch can never drift
   from the thing it is explaining. */
.mxkey{list-style:none;display:flex;flex-wrap:wrap;justify-content:center;gap:10px 26px;
  margin:0 0 clamp(22px,2.6vw,30px);padding:0}
.mxkey li{display:flex;align-items:center;gap:9px;font-size:13.5px;color:var(--dk-soft)}


/* ---------- AI monitoring: the three problems ----------
   Reuses .termrow for the grid but its own card class: these carry a second
   paragraph (which Microsoft product solves it) that the D&R term cards do not,
   and overloading .term with an extra element would have coupled two pages
   that have no reason to change together. */
.layer{display:flex;flex-direction:column;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius-lg);padding:28px 26px;box-shadow:var(--shadow-sm);
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.layer:hover{transform:translateY(-4px);border-color:var(--line-2);box-shadow:var(--shadow)}
.layer.is-feat{position:relative;overflow:hidden;padding-top:30px}
.layer.is-feat::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;background:var(--seam-grad)}
.layer-n{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red)}
.layer h3{font-size:20px;letter-spacing:-.018em;margin:10px 0 11px}
.layer p{margin:0;color:var(--fg-soft);font-size:14.5px;line-height:1.6}
/* The product line, pinned to the foot so the three cards line up on it
   whatever length the description above runs to. */
.layer-t{margin:auto 0 0 !important;padding-top:16px;border-top:1px solid var(--line);
  color:var(--ink) !important;font-size:13.5px !important;line-height:1.55 !important}
@media (prefers-reduced-motion:reduce){.layer:hover{transform:none}}

/* ---------- index: search and topic filter ----------
   ON THE LIGHT SECTION, with the results it controls. It was inside the dark
   hero, where it floated over the mesh and read as part of the artwork rather
   than as a control - and sat on the far side of a section boundary from the
   cards it filters. Ships hidden and is revealed by index-filter.js, so a
   browser that never runs it gets the full index instead of a dead search box.

   Left-aligned rather than centred: it is a toolbar over a grid, and the grid
   starts at the left edge. */
.ixtools{display:flex;flex-wrap:wrap;align-items:center;gap:12px;
  margin:0 0 clamp(26px,3vw,34px)}
.ixsearch{position:relative;flex:1 1 300px;max-width:380px}
.ixsearch svg{position:absolute;left:16px;top:50%;transform:translateY(-50%);
  width:17px;height:17px;color:var(--fg-mut);pointer-events:none}
.ixsearch input{width:100%;padding:12px 16px 12px 44px;font:inherit;font-size:15px;
  color:var(--ink);background:var(--paper);border:1px solid var(--line);
  border-radius:100px;box-shadow:var(--shadow-sm);
  transition:border-color .2s,box-shadow .2s}
.ixsearch input::placeholder{color:var(--fg-mut)}
.ixsearch input:focus{outline:none;border-color:var(--red);box-shadow:var(--shadow)}
.ixsearch input::-webkit-search-cancel-button{opacity:.5;cursor:pointer}

.ixchips{display:flex;flex-wrap:wrap;gap:8px}
.ixchip{font-family:var(--mono);font-size:10.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 15px;cursor:pointer;
  transition:color .2s,background .2s,border-color .2s}
.ixchip:hover{color:var(--ink);border-color:var(--line-2)}
.ixchip.on{color:#fff;background:var(--red);border-color:var(--red)}
@media (max-width:620px){.ixsearch{flex:1 1 100%;max-width:none}}

.ixcount{margin:0 0 22px;font-family:var(--mono);font-size:11.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-mut)}
.ixnone{margin:clamp(30px,4vw,48px) 0 0;text-align:center;color:var(--fg-soft);font-size:16px}
.ixnone .textlink{background:none;border:0;padding:0;font:inherit;cursor:pointer}

/* ---------- article: reading aids ----------
   A progress bar and a contents rail, both built from the article's own
   headings by public/js/article.js. Both ship hidden, so a page that never
   runs the script reads exactly as it did before. */
.readbar{position:fixed;top:0;left:0;right:0;height:3px;z-index:400;
  background:transparent;pointer-events:none}
/* The fill paints the SAME band as .seam, sized to the viewport rather than to
   its own width - so it is revealed left to right as you read and reads as the
   full signal band at 100%. Painting the gradient at the span's own width
   instead would squeeze all six stops into the first few pixels at 5% scroll,
   which looks like a bug. */
.readbar span{display:block;height:100%;width:0;
  background-image:var(--seam-grad);
  background-size:100vw 100%;background-repeat:no-repeat;
  transition:width .08s linear}

.arthero-img{display:block;width:100%;height:auto;aspect-ratio:16/9;object-fit:cover;
  border-radius:var(--radius-lg);margin-bottom:clamp(30px,4vw,44px);background:var(--bg-2)}

/* The contents sit above the prose rather than in a sticky sidebar: the article
   column is already narrow for readability, and stealing 220px of it to float a
   list alongside would cost the thing the page exists for. */
.arttoc{position:relative;overflow:hidden;margin:0 0 clamp(32px,4vw,44px);padding:22px 26px 22px 29px;
  background:var(--paper);border:1px solid var(--line);
  border-radius:0 var(--radius) var(--radius) 0}
.arttoc::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--seam-grad-v)}
.toc-k{display:block;font-family:var(--mono);font-size:10.5px;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--red);margin-bottom:14px}
.arttoc ol{margin:0;padding:0 0 0 20px;display:grid;gap:9px}
.arttoc li{color:var(--fg-mut);font-size:14.5px;line-height:1.45}
.arttoc a{color:var(--fg-soft);text-decoration:none;transition:color .2s}
.arttoc a:hover{color:var(--red)}
.arttoc a.on{color:var(--ink);font-weight:600}
/* Headings are jump targets now, so they need clearance from the floating
   header when one is linked to directly. */
.prose h2{scroll-margin-top:120px}
@media (prefers-reduced-motion:reduce){.readbar span{transition:none}}

/* ---------- index: sort control ----------
   The category-group styles that were here are gone with the groups. The index
   is one flat grid now, newest first, with topic as a filter rather than a
   structure — see the note at the top of src/views/pages/index-list.njk. */
.ixsort{display:flex;align-items:center;gap:9px;margin-left:auto}
.ixsort > span{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--fg-mut)}
.ixsort select{font:inherit;font-size:13px;font-weight:600;color:var(--fg);
  background:#fff;border:1px solid var(--line-2);border-radius:999px;
  padding:9px 14px;cursor:pointer;transition:border-color .15s}
.ixsort select:hover{border-color:var(--fg-mut)}
.ixsort select:focus-visible{outline:2px solid var(--red-2);outline-offset:2px}
@media (max-width:640px){
  .ixsort{margin-left:0;width:100%}
  .ixsort select{flex:1}
}

/* ---------- the triad's summary                        .triad-sum  (N5.2) ----
   What the three services add up to, under the diagram rather than trailing
   after the whole block. On the NDR page that is "Complete network visibility",
   which had a title and a description exactly like the three services above it
   but rendered as one bolded sentence after them — a fourth item dressed as
   prose.

   NOT in the hub, which is where N5.2 suggested it belonged. The hub is 119px
   across at the map's widest; "Complete network visibility" does not fit inside
   it at any size worth reading. Directly under the diagram is as close to the
   centre as the words can get.

   Optional: the SOC Triad page passes no triadSummary and gets no row here, so
   nothing about that page changes. */
/* All three children take EXPLICIT placement. Giving the panels a grid-row
   while the map and this were auto-placed made the packing algorithm put the
   panels in column 1 and the map in column 2 — the two swapped sides. Mixing
   definite and automatic placement in one grid is the trap; naming every cell
   removes it. */
.triad-sum{
  grid-column:1;grid-row:2;
  max-width:440px;margin:clamp(16px,1.8vw,24px) auto 0;
  text-align:center;
}
.triad-map{grid-column:1;grid-row:1}
.triad-sum b{
  display:block;font-family:var(--display);font-weight:700;
  font-size:16px;letter-spacing:-.01em;color:var(--ink);margin-bottom:5px;
}
.triad-sum span{display:block;font-size:14.5px;line-height:1.65;color:var(--fg-mut)}

/* The panels span both rows so they stay centred against the map AND its
   summary together. On a page with no summary the second row has no content and
   collapses to zero, so this resolves to row 1 and changes nothing. */
.triad-panels{grid-column:2;grid-row:1 / span 2;align-self:center}

/* The explicit two-column placement above has to be undone here, not just
   overridden in part. Left alone, .triad-panels kept grid-column:2 and grid-row
   spanning both rows, which in a one-column grid creates an implicit second
   column and strands the summary AFTER the panel — map, panel, summary. The
   summary belongs with the diagram, so all three are re-placed in one column,
   in reading order. */
@media (max-width:900px){
  .triad-map{grid-column:1;grid-row:1}
  .triad-sum{grid-column:1;grid-row:2;max-width:none}
  .triad-panels{grid-column:1;grid-row:3}
}

/* ---------------------------------------------------------------------------
   THE PARTNER BAND                                     .partnerband  (N4.3)

   §3 "Powered by Vectra" takes the page's only mid-run dark surface. Without
   it the run from the definition card to the CTA band is 3,015px of unbroken
   light - half the page on one ground. Three reasons it is this section:

   * It is the only one holding nothing that assumes a light ground. §2 is
     .benefit-card and §4 is the triad diagram, and BOTH are shared with
     Managed SIEM and SOC Triad - darkening either means inverting a component
     another page depends on. This one is a .splitrow: an eyebrow, a heading
     and four paragraphs.
   * At 479px it is the shortest section on the page by a wide margin, so the
     band reads as a beat rather than a slab. It splits the light run into
     roughly 1,300px and 1,200px either side.
   * It is the only section about somebody else's product. Setting a partner
     apart is structure, not decoration.

   NAMED FOR THE JOB, NOT THE PARTNER. Sentinel and Microsoft MSSP have the
   same kind of section, and .vectraband would have started lying the first
   time one of them reused it.

   ALMOST NOTHING IS NEEDED. .sec-dark already flips h2 to #fff and .lede / p
   to --dk-soft, and .sp-body p has carried its own dark variant since the day
   the splitrow was written. Only the two red things want a hand, and both take
   the same swap .defcard already makes.
   --------------------------------------------------------------------------- */
/* --red is #c30017, which on this backdrop (--bd-1 #180c0f falling to --bd-2
   #0b0507) measures 3.03:1 at the surface's LIGHTEST point. The eyebrow is
   12.5px, so it is body text and wants 4.5:1; --red-3 gives 5.46:1.

   The 28px dash in front of it is a separate `background`, not currentColor,
   so changing only the text leaves a --red rule sitting beside a --red-3 word.

   Eleven other dark .splitrow sections across the site have the same failure,
   and the fix for all of them is one line - `.sec-dark .eyebrow`, sitting next
   to the `.sec-dark .lede` that is already there. Scoped here instead, because
   that line would change eleven pages nobody has asked about. */
.partnerband .eyebrow{color:var(--red-3)}
.partnerband .eyebrow::before{background:var(--red-3)}
/* The heading accent clears the 3:1 large-text floor on --red by 0.03, which
   is not a margin, and --red-3 is the documented substitute on dark. Exactly
   what .defcard h2 .accent does on the card two sections up. */
.partnerband .sp-head h2 .accent{color:var(--red-3)}

/* ---------------------------------------------------------------------------
   ITDR — the hero figure, privilege escalation      .itdrfig / .idf-*  (I1.1)

   Geometry and reasoning are in partials/itdr-figure.njk; this dresses and
   animates it.

   IT LOOPS, ON TWO CLOCKS, the same way the NDR figure does. Sign-ins move
   continuously, because an identity estate authenticates all day and watching
   that is the service. The escalation re-traces on a slow 10s cycle, because
   that is the demonstration rather than the state. One speed for both made the
   intrusion look routine, which is the opposite of the point.

   NO JAVASCRIPT. The entrance hangs off the `in` class the existing scroll
   observer already sets; everything after is keyframes.
   --------------------------------------------------------------------------- */
.itdrfig{
  margin:0;width:100%;max-width:560px;justify-self:end;
  opacity:1;transform:none;transition:none;      /* cancel the generic reveal */
}
.itdrfig svg{display:block;width:100%;height:auto}

/* ---------- the plate ---------- */
.itdrfig .idf-plate{
  fill:url(#idf-plate);
  stroke:var(--dk-line-2);stroke-width:1.4;
  filter:drop-shadow(0 26px 36px rgba(0,0,0,.8));
}
.itdrfig .idf-lattice{stroke:none;pointer-events:none}
.itdrfig .idf-wash{pointer-events:none}

/* ---------- the three tiers ----------
   Dashed, and the innermost is brightest. A privilege boundary is something an
   attacker crosses, not a wall, so none of them is solid — but Tier 0 gets the
   most contrast because it is the thing the whole figure is about. */
.itdrfig .idf-tier{fill:none;stroke-dasharray:.006 .01}
.itdrfig .idf-tier.t2{stroke:rgba(255,255,255,.13);stroke-width:1.2}
.itdrfig .idf-tier.t1{stroke:rgba(255,255,255,.20);stroke-width:1.3}
.itdrfig .idf-tier.t0{stroke:rgba(255,255,255,.30);stroke-width:1.5}

/* ---------- ordinary authentication ----------
   Two weights. One uniform opacity read as a wireframe, and a real identity
   estate does not have fourteen equally important trust relationships. */
.itdrfig .idf-link{fill:none;stroke-linecap:round}
.itdrfig .idf-link.a{stroke:rgba(255,255,255,.18);stroke-width:1.4}
.itdrfig .idf-link.b{stroke:rgba(255,255,255,.085);stroke-width:1.1}

/* ---------- accounts ----------
   A token ring, a head and shoulders. Tier 1 is the same glyph at a larger
   radius rather than a different glyph, because an admin account IS a user
   account with more rights — that is the entire premise of the attack. */
.itdrfig .idf-acct .idf-tok{fill:rgba(255,255,255,.055);stroke:rgba(255,255,255,.26);stroke-width:1.3}
.itdrfig .idf-acct .idf-hd{fill:rgba(255,255,255,.55)}
.itdrfig .idf-acct .idf-sh{fill:none;stroke:rgba(255,255,255,.55);stroke-width:1.6;stroke-linecap:round}
.itdrfig .idf-acct.adm .idf-tok{stroke:rgba(255,255,255,.38)}
.itdrfig .idf-acct.adm .idf-hd,.itdrfig .idf-acct.adm .idf-sh{stroke:rgba(255,255,255,.72)}
.itdrfig .idf-acct.adm .idf-hd{fill:rgba(255,255,255,.72)}

/* The compromised account: outlined in red, not filled. --red-3 rather than
   --red because this sits on a near-black plate, where #c30017 measures 2.79:1
   — under the 3:1 large-text floor, let alone the 4.5:1 this size would want.
   Same swap .defcard and .partnerband make. */
.itdrfig .idf-entry .idf-tok{fill:none;stroke:var(--red-3);stroke-width:2}
.itdrfig .idf-entry .idf-hd{fill:var(--red-3)}
.itdrfig .idf-entry .idf-sh{fill:none;stroke:var(--red-3);stroke-width:1.7;stroke-linecap:round}

/* ---------- sign-ins in flight ---------- */
.itdrfig .idf-pk{fill:rgba(255,255,255,.8)}

/* ---------- the escalation ---------- */
.itdrfig .idf-hop{fill:none;stroke:var(--red-3);stroke-width:2.4;stroke-linecap:round}
/* The blurred copy underneath. Wider and dimmer: it is a glow, not a second
   line, and at full opacity it fattens the path instead of lighting it. */
.itdrfig .idf-route-glow .idf-hop{stroke-width:5;opacity:.5}

/* ---------- the privileged account, reached ---------- */
.itdrfig .idf-hit-glow{fill:var(--red-3);opacity:.85}
.itdrfig .idf-hit-face .idf-tok{fill:var(--red-3);stroke:var(--red-3)}
.itdrfig .idf-hit-face .idf-hd{fill:#fff}
.itdrfig .idf-hit-face .idf-sh{fill:none;stroke:#fff;stroke-width:2;stroke-linecap:round}
.itdrfig .idf-ring{fill:none;stroke:var(--red-3);stroke-width:1.6}

/* ---------- resting state ----------
   Everything starts hidden and is brought in by the entrance below. Stated
   before any keyframes, so the figure is correct where animation is
   unavailable rather than depending on one running. */
.itdrfig .idf-plate,.itdrfig .idf-lattice,.itdrfig .idf-wash,.itdrfig .idf-mesh,
.itdrfig .idf-accts,.itdrfig .idf-traffic,.itdrfig .idf-entry,.itdrfig .idf-hit{opacity:0}
/* pathLength="1" is set in the markup on the hops, and a dasharray of 1 is
   therefore the WHOLE path — one dash, which dashoffset can pull back to
   reveal. Without pathLength it would mean one USER UNIT: on a 161-unit hop
   that is a pattern repeating 161 times, so the line renders as dots that are
   already fully there and never draw. That shipped once on the NDR figure.

   The TIERS are deliberately not done this way. They carry a decorative dash
   pattern, and a dashoffset only shifts a repeating pattern around the path
   rather than revealing it — with .006/.01 repeating ~62 times around the
   ring, offset 1 and offset 0 are visually identical. A ring can draw on or it
   can look dashed; it cannot do both. These fade in instead. */
.itdrfig .idf-tier{opacity:0}
.itdrfig .idf-hop{stroke-dasharray:1;stroke-dashoffset:1}
.itdrfig .idf-ring{opacity:0}
.itdrfig .idf-head{opacity:0}

/* ---------- entrance: once, on reveal ----------
   Outward, tier by tier: the plate, then the rings drawing from Tier 0 out,
   then the accounts landing on them. It builds the model before it attacks it,
   which is the only order in which the attack means anything. */
.itdrfig.in .idf-plate{animation:idfIn .5s ease-out both}
.itdrfig.in .idf-lattice{animation:idfIn .7s ease-out .3s both}
.itdrfig.in .idf-wash{animation:idfIn .8s ease-out .5s both}
.itdrfig.in .idf-tier{animation:idfIn .55s ease-out both}
.itdrfig.in .t0{animation-delay:.30s}
.itdrfig.in .t1{animation-delay:.45s}
.itdrfig.in .t2{animation-delay:.60s}
.itdrfig.in .idf-mesh{animation:idfIn .6s ease-out .75s both}
.itdrfig.in .idf-accts{animation:idfIn .5s ease-out .85s both}
.itdrfig.in .idf-traffic{animation:idfIn .6s ease-out 1.15s both}
.itdrfig.in .idf-entry{animation:idfIn .4s ease-out 1.30s both}

@keyframes idfIn{from{opacity:0}to{opacity:1}}

/* transform-box:fill-box on everything that scales or follows a path. Without
   it an element's own coordinates are ADDED to the transform origin and it
   lands nowhere near where it should, silently. */
.itdrfig .idf-hit,.itdrfig .idf-ring{transform-box:fill-box;transform-origin:center}

/* ---------- the two loops ----------
   Gated on no-preference rather than switched off under reduce: resting state
   first, keyframes attached only if the reader has not asked otherwise. Same
   construction as .ndrfig and .siem-defsec. */
@media (prefers-reduced-motion:no-preference){

  /* Sign-ins. Each packet has its own trust relationship, its own pace and its
     own offset, so they never synchronise.

     ONE FULL SHORTHAND EACH, deliberately, rather than a shared
     `animation:idfPacket linear infinite` with per-packet durations beneath
     it. That shape looks tidier and does not work: the shorthand resets
     animation-duration to 0s, and at three classes it outranks a two-class
     animation-duration, so every packet gets an animation that cannot advance.
     The NDR figure hit this exact bug in this exact place — see .ndrfig.in
     .pk1 to .pk4, written the same way for the same reason. */
  .itdrfig.in .pk1{animation:idfPacket 3.6s linear 0s   infinite}
  .itdrfig.in .pk2{animation:idfPacket 4.3s linear .8s  infinite}
  .itdrfig.in .pk3{animation:idfPacket 3.9s linear 1.7s infinite}
  .itdrfig.in .pk4{animation:idfPacket 4.8s linear 2.4s infinite}

  /* The escalation, on its own much slower clock: draw, hold, fade, wait.
     One hop at a time — 0.6s each, the next starting as the last lands, so the
     route TRAVELS rather than appears. The first waits until 1.7s, which is
     after the compromised account has finished arriving: a route cannot leave
     an account that is not there yet. */
  .itdrfig.in .idf-hop{animation:idfHop 10s linear infinite}
  .itdrfig.in .h1{animation-delay:1.7s}
  .itdrfig.in .h2{animation-delay:2.3s}
  .itdrfig.in .h3{animation-delay:2.9s}
  .itdrfig.in .idf-head{animation:idfHead 10s linear 1.7s infinite}
  .itdrfig.in .idf-hit{animation:idfHit 10s linear 3.5s infinite}
  .itdrfig.in .r1{animation:idfRing 10s linear 3.5s infinite}
  .itdrfig.in .r2{animation:idfRing 10s linear 3.8s infinite}
}

/* Draw over 0.6s each, hold, fade, wait. The hold is what stops this reading as
   a looping animation and lets it read as a state the page is in. */
@keyframes idfHop{
  0%   {stroke-dashoffset:1;opacity:1}
  6%   {stroke-dashoffset:0;opacity:1}
  80%  {stroke-dashoffset:0;opacity:1}
  89%  {stroke-dashoffset:0;opacity:0}
  100% {stroke-dashoffset:1;opacity:0}
}
/* The head covers all three hops in one run — 18% of the cycle is the 1.8s the
   three draws take together — then goes out at the account it reaches. */
@keyframes idfHead{
  0%   {offset-distance:0%;opacity:0}
  1.5% {opacity:1}
  16%  {opacity:1}
  18%  {offset-distance:100%;opacity:0}
  100% {offset-distance:100%;opacity:0}
}
@keyframes idfHit{
  0%      {opacity:0;transform:scale(.55)}
  5%      {opacity:1;transform:scale(1)}
  74%     {opacity:1;transform:scale(1)}
  84%,100%{opacity:0;transform:scale(1)}
}
/* Rings expand once each and stop, rather than pulsing for the whole hold —
   something spreading, not a beacon. */
@keyframes idfRing{
  0%      {opacity:0;transform:scale(.45)}
  4%      {opacity:.6}
  22%     {opacity:0;transform:scale(2.1)}
  100%    {opacity:0;transform:scale(2.1)}
}
@keyframes idfPacket{from{offset-distance:0%}to{offset-distance:100%}}
/* The head follows all three hops as one path. transform-box:fill-box is
   mandatory here — see the note above. */
.itdrfig .idf-head{
  fill:#fff;transform-box:fill-box;
  filter:drop-shadow(0 0 6px var(--red-3)) drop-shadow(0 0 14px var(--red-3));
  offset-path:path('M244 76 307 136 450 210 320 285');
  offset-rotate:0deg;
}
.itdrfig .idf-pk{transform-box:fill-box}
/* The paths sit OUTSIDE the motion query: a sign-in still needs to know where
   it lives even when it is not travelling. All four run between accounts that
   the escalation never touches — the ordinary authentication the attack has to
   hide among. */
.itdrfig .pk1{offset-path:path('M106 343 224 400')}
.itdrfig .pk2{offset-path:path('M534 228 450 210')}
.itdrfig .pk3{offset-path:path('M359 504 395 415')}
.itdrfig .pk4{offset-path:path('M431 93 450 210')}

/* ---------- responsive ----------
   .hero-grid drops to one column at 1080px in main.css, so this has to match or
   the figure re-centres while still sitting in a side column. */
@media (max-width:1080px){
  .itdrfig{justify-self:center;max-width:470px;margin-top:14px}
}
@media (max-width:560px){
  .itdrfig{max-width:100%}
  .itdrfig .idf-plate{filter:drop-shadow(0 16px 22px rgba(0,0,0,.8))}
}

/* ---------- reduced motion ----------
   The finished picture, immediately and still: the tiers drawn, the escalation
   traced, the privileged account flagged, the sign-ins parked on their links.
   It carries the same information without anything moving. */
@media (prefers-reduced-motion:reduce){
  .itdrfig .idf-plate,.itdrfig .idf-lattice,.itdrfig .idf-wash,.itdrfig .idf-mesh,
  .itdrfig .idf-accts,.itdrfig .idf-traffic,.itdrfig .idf-entry,.itdrfig .idf-hit{opacity:1}
  .itdrfig .idf-tier{opacity:1}
  .itdrfig .idf-hop{stroke-dashoffset:0}
  .itdrfig .idf-ring,.itdrfig .idf-head{opacity:0}
  .itdrfig.in .idf-plate,.itdrfig.in .idf-lattice,.itdrfig.in .idf-wash,
  .itdrfig.in .idf-mesh,.itdrfig.in .idf-accts,.itdrfig.in .idf-traffic,
  .itdrfig.in .idf-entry,.itdrfig.in .idf-hit,.itdrfig.in .idf-hop,
  .itdrfig.in .idf-pk,.itdrfig.in .idf-head,.itdrfig.in .r1,.itdrfig.in .r2,
  .itdrfig.in .idf-tier{animation:none}
}

/* ---------------------------------------------------------------------------
   THE CAPABILITY STRIP                        .capsub / .capstrip / .cap (I2.2)

   Three capabilities, a SUBSECTION of the ITDR answer card rather than a
   section of their own. On the live page they were bolded run-in paragraphs
   inside the body copy — the "item dressed as prose" pattern N5.2 diagnosed
   on NDR.

   THEY LIVE INSIDE .defcard-sec, which matters for one reason beyond tidiness:
   that section draws a tapered red separator at its foot. Keeping the
   capabilities inside it puts them above that rule, so the rule closes the
   whole of §1 instead of cutting it in half.

   THE HEADING IS AN h3, AND THE CARD TITLES ARE h4. The section's own h2 is
   inside the answer card, so this is the correct level as well as the correct
   size — as an h2 it set at up to 56px directly above §2's "Key features of
   our identity...", two enormous headings in a row both starting with "Key".

   COLOURED, NOT TRANSPARENT. The first pass made these bare text columns
   divided by hairlines, on the theory that anything boxed would compete with
   §2's feature cards. That was too timid: it read as leftover body copy rather
   than as three things worth naming. They are cards now, with a surface of
   their own — a red-tinted one, because the whole of §1 is the brand's
   territory on this page and the answer card above them already carries a red
   glow. §2's .card is white on white, so the two still do not collide.
   --------------------------------------------------------------------------- */
.capsub{margin:clamp(40px,5vw,64px) auto 0;max-width:1180px}
.capsub h3{
  font-size:clamp(19px,2vw,23px);line-height:1.25;letter-spacing:-.02em;
  color:var(--ink);margin:0 0 clamp(20px,2.4vw,28px);
}
.capstrip{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(16px,1.8vw,24px);
}
/* DARK, LIKE THE CARD ABOVE THEM. The first pass made these light with a red
   tint, which put a pink card directly under a near-black one on the same band
   and read as a different design language. They match the answer card and the
   ATT&CK cards now: near-black panel, white heading, --dk-soft body.

   --c IS ADAM'S PATTERN, NOT A NEW ONE. .agent, .feat, .ag and .squad-tag all
   take a per-instance --c and derive their accents from it; main.css says in
   so many words that the squad colours are "the same three the agents use:
   red, blue, green". One capability, one colour, in that order.

   --red-3 rather than --red for the first card. #c30017 measures 3.05:1 on
   this panel; the brightened #ff3b52 is the documented dark substitute and is
   what every other dark surface on our pages uses. Blue and green need no
   substitute: both are already mid-tone against near-black. */
.cap{
  --c:var(--red-3);
  position:relative;
  border:2px solid transparent;
  border-radius:var(--radius);
  padding:clamp(22px,2.4vw,30px);
  /* The two-layer background is what makes a gradient BORDER possible: the
     first layer paints the card and is clipped to the padding box, the second
     paints the border and is clipped to the border box. Both need
     background-origin:border-box or the conic gradient starts at the wrong
     origin and the arc sits off-centre. */
  background-origin:border-box;
  background-clip:padding-box,border-box;
  background-image:
    linear-gradient(168deg,var(--dk-panel) 0%,var(--dk) 100%),
    conic-gradient(from var(--rotation,0deg),
      var(--c) 0deg, var(--c) 78deg,
      rgba(255,255,255,.10) 78deg, rgba(255,255,255,.10) 360deg);
  box-shadow:0 18px 44px -26px rgba(0,0,0,.75);
}
.cap:nth-child(2){--c:var(--blue)}
.cap:nth-child(3){--c:var(--green)}

/* The 28px dash, now in the card's own colour rather than brand red on all
   three — it is the only other place the colour appears, so it ties the border
   arc to the content instead of leaving the arc looking decorative. */
.cap h4::before{
  content:"";display:block;width:28px;height:2px;background:var(--c);
  margin:0 0 14px;transition:width .3s var(--ease);
}
.cap:hover h4::before{width:44px}
.cap h4{
  font-size:clamp(15.5px,1.6vw,16.5px);line-height:1.32;letter-spacing:-.015em;
  color:#fff;margin:0 0 10px;font-weight:700;
}
.cap p{
  margin:0;font-size:14.5px;line-height:1.72;color:var(--dk-soft);
}

/* Two columns before one: three tinted cards at tablet width squeeze the
   titles onto four lines each, and a 2+1 row reads better than three cramped
   ones. */
@media (max-width:980px){
  .capstrip{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:640px){
  .capstrip{grid-template-columns:1fr}
}

/* ---------------------------------------------------------------------------
   BENEFIT GRID, THREE COMPACTS TO A ROW           .compact-trio       (I3.1)

   A modifier on .benefit-grid, NOT a change to it. The component spans every
   card 6 of 12 — two to a row, promoted and compact alike — which is right for
   NDR's six items and Managed SIEM's, and would leave ITDR's fifth card alone
   on a row of two. The orphan is what I3.1 is about, so swapping .cards-3 for
   .benefit-grid without this would have moved the problem rather than fixed it.

   Two promoted at span 6 fill row one; three compact at span 4 fill row two
   exactly. At the width the grid reaches on a desktop a span-4 card is about
   381px. That matters: the note on .benefit-card records that a FOUR-across
   compact row was tried and abandoned, because at 280px a 50-word paragraph
   became a tall ribbon and the demoted cards ended up TALLER than the promoted
   ones, inverting the hierarchy. Three across is 100px clear of that, and
   these paragraphs are 24-29 words rather than 50. Measured: leads 265px tall,
   compacts 231px. The hierarchy holds.

   IT IS A min-width RULE, AND THAT IS THE WHOLE POINT. Written without one it
   applied at every width, and at four classes it out-specified the component's
   own collapse to span 12 at max-width 720px, which uses two. The result was
   three compact cards NINETY-THREE PIXELS wide on a phone. A modifier on a
   shared component must be scoped to the range it is for, not merely written
   after the component and left to defer to it — the more specific selector
   never defers.

   Bounding it this way also means the 721-900px band needs no rule from us at
   all: the component's own span 6 takes over, two across, fifth card on a row
   of its own, which is fine stacked in a way it is not in a wide row.
   --------------------------------------------------------------------------- */
@media (min-width:901px){
  .benefit-grid.compact-trio .benefit-card:not(.is-lead){grid-column:span 4}
}

/* ---------------------------------------------------------------------------
   ATT&CK TACTIC CAROUSEL        .tcar / .tcard / .tcar-rail   (I4.1, I4.2)

   Eleven tactic cards on an arc, five visible, each holding its techniques as
   sub-cards. Ported from a React + framer-motion + Tailwind component: the arc
   geometry, the five-card window and the scale/opacity falloff are the idea
   worth having. None of that stack exists here — no build step, no framework,
   and the CSP blocks CDN scripts — so it is CSS transforms plus about forty
   lines in our own main.js, the same way the triad selector was ported.

   WHAT THIS COSTS, said plainly. The column layout it replaces made coverage
   depth visible in the silhouette: one technique under Initial Access against
   eight under Credential Access, no words needed. A carousel shows one tactic
   at a time and throws that away. Two things put it back — a derived count on
   every card, and a NAVIGATION RAIL SIZED BY TECHNIQUE COUNT rather than a row
   of equal dots. The coverage profile now lives in the controls.

   NO-JS FIRST. Without .is-live the stage is a plain horizontal scroll row of
   all eleven cards and every technique is reachable. The script adds the class
   and takes over. Nothing here depends on JavaScript to be readable.
   --------------------------------------------------------------------------- */

/* The eyebrow and heading accent, for the same reason .partnerband carries
   them: --red is #c30017, which measures 3.05:1 on this surface. The eyebrow is
   12.5px so it is body text and wants 4.5:1; --red-3 gives 5.48:1.
   SECOND PAGE-SCOPED COPY OF A ONE-LINE FIX. H1 in the decision register, open
   since 14 Sep, is .sec-dark .eyebrow — which would make both copies
   unnecessary and fix eleven other pages at the same time. */
.attcksec .eyebrow{color:var(--red-3)}
.attcksec .eyebrow::before{background:var(--red-3)}
.attcksec .sec-head h2 .accent{color:var(--red-3)}

/* WIDER THAN THE SECTION. --wrap is 1240px, which leaves 1192px of content —
   enough for five cards, not for seven. This breaks the block out to the
   viewport up to 1560px, the standard left:50% / translateX(-50%) escape, so
   the fan has room without widening the prose around it. The JS reads the
   stage's real width and derives the radius from it, so the two cannot drift
   apart the way a hard-coded radius and a hard-coded container always do. */
.tcar{
  --tcar-w:min(100vw - 40px,1560px);
  /* ROW HEIGHT AND CARD HEIGHT ARE DERIVED, not two numbers kept in step by
     hand. --tcard-rows is the only thing to change to show more or fewer
     sub-items; the list cap, the card height and the negative margin that
     centres it all follow from it. 53px is the measured height of a one-line
     sub-card, 7px the measured gap, 155px the header plus its rule and the
     card's own padding. */
  --tcard-rows:5;
  --tcard-list-h:calc(var(--tcard-rows) * 53px + (var(--tcard-rows) - 1) * 7px);
  --tcard-h:calc(var(--tcard-list-h) + 155px);
  /* Was clamp(26px,3vw,38px) — too tight against the paragraphs above once the
     block broke out to 1560px and grew a deck of cards. A wider, taller block
     needs more air above it, not the same. */
  margin-top:clamp(52px,6vw,88px);outline:none;position:relative;
  width:var(--tcar-w);
  /* NOT left:50% + translateX(-50%), which is the usual escape and is wrong
     here: this element also carries .reveal, and .reveal.in sets
     transform:none at two classes against this rule's one. The centring would
     survive exactly until the scroll observer fired, then the block would jump
     right by half its width. A margin cannot be overwritten by .reveal. */
  margin-left:calc(50% - var(--tcar-w) / 2);
}
.tcar:focus-visible{outline:2px solid var(--red-3);outline-offset:6px;border-radius:var(--radius-lg)}

/* ---------- no-JS / pre-script state ---------- */
.tcar-stage{
  display:flex;gap:16px;overflow-x:auto;padding:4px;
  scroll-snap-type:x mandatory;
}
.tcar-stage > .tcard{flex:0 0 var(--tcard-w,260px);scroll-snap-align:center}

/* ---------- live state ---------- */
/* The card is centred on the stage's middle, so the stage needs the card's
   height plus twice the arc depth to hold it without clipping. Derived rather
   than typed, so raising --tcard-rows cannot silently push the deck out of
   its own container. */
.tcar.is-live .tcar-stage{
  display:block;position:relative;overflow:visible;padding:0;
  height:calc(var(--tcard-h) + 120px);perspective:1200px;
}
/* Width is a custom property because the JS reads it to work out the step, and
   two places holding the same number is how they end up disagreeing. The
   negative margins are half the width and half the height — that is what puts
   the card's CENTRE on the stage's centre, which every transform then works
   from. */
.tcar.is-live .tcard{
  position:absolute;left:50%;top:50%;
  width:var(--tcard-w,260px);
  margin:calc(var(--tcard-h) / -2) 0 0 calc(var(--tcard-w,260px) / -2);
  transition:transform .62s cubic-bezier(.22,1,.36,1),opacity .62s cubic-bezier(.22,1,.36,1);
  will-change:transform,opacity;
}
@media (min-width:1080px){ .tcar.is-live{--tcard-w:300px} }
@media (max-width:759px){ .tcar.is-live{--tcard-w:228px} }

/* ---------- a tactic card ---------- */
.tcard{
  display:flex;flex-direction:column;height:var(--tcard-h);
  background:linear-gradient(180deg,var(--dk-panel),var(--dk));
  border:1px solid var(--dk-line-2);
  border-radius:var(--radius-lg);
  padding:18px 16px 14px;
  box-shadow:0 8px 24px -6px rgba(0,0,0,.5);
  position:relative;
}
.tcar.is-live .tcard.is-active{
  border-color:rgba(255,59,82,.42);
  box-shadow:0 24px 60px -14px rgba(0,0,0,.75),0 0 0 1px rgba(255,59,82,.18);
}
.tcard-h{border-bottom:1px solid var(--dk-line);padding-bottom:12px;margin-bottom:12px;flex:none}
/* The step number is the ONE place on this page where a numbered marker is
   honest: TA0001 Initial Access through TA0040 Impact is the order an intrusion
   happens in. The TA ids do not carry it — TA0040 follows TA0010 — so without
   this the kill chain is invisible to anyone who does not already know it. */
.tcard-step{display:block;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.16em;color:var(--red-3);margin-bottom:6px}
.tcard-id{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;
  color:var(--dk-mut);margin-bottom:4px}
.tcard-n{font-family:var(--display);font-weight:700;font-size:17px;line-height:1.2;
  letter-spacing:-.015em;color:#fff;margin:0 0 6px}
.tcard-count{font-family:var(--mono);font-size:10px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--dk-mut)}

/* The sub-cards. Scroll INSIDE the card, because Credential Access has eight
   and a card tall enough for eight would be mostly empty for the six tactics
   that have one or two. */
/* TWO THINGS HERE ARE THE FIX, and both were bugs.

   align-content:start — this is a grid, and grid ROWS STRETCH to fill their
   container by default. With flex:1 1 auto the list filled the card, so a
   tactic with one technique stretched that single sub-card to the full height:
   "T1078 Valid Accounts", two short words, was rendering 183px tall. Measured,
   not guessed.

   flex:0 1 auto — the list now takes only the height its content needs, up to
   the cap. A card with two techniques shows two sub-cards and leaves the space
   below them empty, instead of a list that runs to the bottom of the card
   whether or not there is anything in it. */
/* The wrapper exists only to hold the scroll affordances. It takes the list's
   height so a short list still hugs its content. min-height:0 is what lets a
   flex child actually shrink and scroll rather than growing to fit. */
.tcard-scroll{position:relative;flex:0 1 auto;min-height:0;display:flex}

.tcard-list{
  list-style:none;margin:0;padding:0 8px 0 0;display:grid;gap:7px;
  align-content:start;
  width:100%;max-height:var(--tcard-list-h);
  overflow-y:auto;overscroll-behavior:contain;
  /* Firefox. The thumb picks up the card's own red rather than the browser
     default, which was the one unstyled thing left on these cards. */
  scrollbar-width:thin;
  scrollbar-color:rgba(255,59,82,.55) rgba(255,255,255,.06);
}
/* WebKit and Blink. scrollbar-width/color above covers Firefox; these two
   syntaxes do not overlap, so both are needed rather than one being a
   fallback for the other. */
.tcard-list::-webkit-scrollbar{width:6px}
.tcard-list::-webkit-scrollbar-track{
  background:rgba(255,255,255,.06);border-radius:99px;
}
.tcard-list::-webkit-scrollbar-thumb{
  background:linear-gradient(180deg,var(--red-3),#a8152a);
  border-radius:99px;
}
.tcard-list::-webkit-scrollbar-thumb:hover{background:var(--red-3)}

/* ---------- is there more? ----------
   THREE SIGNALS, because a scrollbar alone is not an affordance: it is thin,
   it is at the edge, and on a trackpad it is hidden until you already scroll.

   1. A fade at whichever end has content beyond it. The gradient ends in the
      card's own background colour, so the list looks like it continues under
      the card rather than being cut off.
   2. A chevron, which appears only while there is something below.
   3. The scrollbar itself, now in the card's red.

   All three are driven by classes the script sets, so a list that fits shows
   none of them — an affordance that is always on tells you nothing. */
.tcard-scroll::before,
.tcard-scroll::after{
  content:"";position:absolute;left:0;right:8px;height:34px;
  pointer-events:none;opacity:0;transition:opacity .25s;
}
.tcard-scroll::before{top:0;background:linear-gradient(180deg,var(--dk-panel),rgba(20,25,38,0))}
.tcard-scroll::after{bottom:0;background:linear-gradient(0deg,var(--ink),rgba(17,21,31,0))}
.tcard-scroll.has-above::before{opacity:1}
.tcard-scroll.has-below::after{opacity:1}

.tcard-more{
  position:absolute;left:50%;bottom:4px;transform:translateX(-50%);
  width:22px;height:22px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:rgba(255,59,82,.16);color:var(--red-3);
  pointer-events:none;opacity:0;transition:opacity .25s;
}
.tcard-more svg{width:13px;height:13px}
.tcard-scroll.has-below .tcard-more{opacity:1;animation:tcardNudge 1.8s ease-in-out infinite}
/* Only on the card being read. Eleven nudging chevrons at once is a fairground,
   and the ten behind are not scrollable by the reader anyway. */
.tcard:not(.is-active) .tcard-more{animation:none}
@keyframes tcardNudge{
  0%,100%{transform:translateX(-50%) translateY(0)}
  50%    {transform:translateX(-50%) translateY(2.5px)}
}
@media (prefers-reduced-motion:reduce){
  .tcard-scroll.has-below .tcard-more{animation:none}
  .tcard-scroll::before,.tcard-scroll::after,.tcard-more{transition:none}
}
.tcard-list li{
  font-size:12.5px;line-height:1.42;color:var(--dk-soft);
  background:rgba(255,255,255,.045);
  border:1px solid rgba(255,255,255,.07);
  border-radius:var(--radius-sm);padding:8px 10px;
}
/* The id above the name, not inline. Inline they wrapped mid-technique, and an
   id separated from its name is unverifiable — which is the entire value of
   printing ids at all. */
.tcard-list li b{display:block;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.08em;color:var(--dk-mut);margin-bottom:3px}

/* A cover button on inactive cards only. The card holds a scrollable list, so
   it cannot BE a button: you cannot scroll inside one, and nested interactive
   content is invalid. The script disables this on the active card. */
.tcard-pick{position:absolute;inset:0;border:0;background:none;cursor:pointer;
  border-radius:var(--radius-lg);padding:0}
.tcard-pick[disabled]{display:none}
.tcard-pick:focus-visible{outline:2px solid var(--red-3);outline-offset:2px}
.tcar:not(.is-live) .tcard-pick{display:none}

/* ---------- controls ---------- */
.tcar-ctl{display:flex;align-items:flex-end;justify-content:center;gap:18px;margin-top:22px}
.tcar-btn{
  flex:none;width:40px;height:40px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:rgba(255,255,255,.05);border:1px solid var(--dk-line-2);
  color:var(--dk-soft);cursor:pointer;transition:background .2s,color .2s,border-color .2s;
}
.tcar-btn svg{width:19px;height:19px}
.tcar-btn:hover{background:rgba(255,255,255,.12);color:#fff;border-color:var(--dk-mut)}
.tcar-btn:focus-visible{outline:2px solid var(--red-3);outline-offset:2px}

/* THE RAIL IS A BAR CHART. Each bar's height is its tactic's technique count,
   so the coverage profile the column layout showed in its silhouette survives
   here in the navigation. --n is set per bar in the markup, from the data. */
.tcar-rail{display:flex;align-items:flex-end;gap:5px;padding-bottom:2px}
.tcar-bar{
  width:9px;border:0;padding:0;cursor:pointer;border-radius:2px;
  height:calc(6px + var(--n) * 2.4px);
  background:rgba(255,255,255,.22);
  transition:background .25s,transform .25s;
}
.tcar-bar:hover{background:rgba(255,255,255,.45)}
.tcar-bar:focus-visible{outline:2px solid var(--red-3);outline-offset:2px}
.tcar-bar[aria-selected="true"]{background:var(--red-3);transform:scaleX(1.35)}

.tcar-count{
  text-align:center;margin:14px 0 0;font-family:var(--mono);font-size:11px;
  letter-spacing:.12em;color:var(--dk-mut);
}
.tcar-count [data-tcar-now]{color:#fff;font-weight:700}

/* ---------- responsive ---------- */
@media (max-width:760px){
  .tcar{--tcar-w:min(100vw - 28px,1560px);--tcard-rows:4}
  .tcar-ctl{gap:12px}
  .tcar-bar{width:7px}
}

/* Position is a transform, so with motion reduced the cards simply appear in
   place rather than sliding. The carousel still works; nothing animates. */
@media (prefers-reduced-motion:reduce){
  .tcar.is-live .tcard{transition:none}
  .tcar-bar,.tcar-btn{transition:none}
}

/* ---------------------------------------------------------------------------
   THE ROTATING BORDER ARC                              .cap --rotation  (I2.2)

   Ported from a React component that set --rotation from the pointer angle on
   every mousemove. Same idea, none of the stack: no shadcn, no Tailwind, no
   TypeScript, and the conic-gradient does the work CSS was always able to do.

   @property is what makes --rotation an ANGLE rather than an opaque token.
   Without it the transition below is silently ignored and the arc snaps
   between positions instead of sweeping — the declaration is not invalid, it
   just does nothing, which is the worst kind of not working. Browsers without
   @property fall back to snapping, which is the source component's behaviour
   anyway.
   --------------------------------------------------------------------------- */
@property --rotation{
  syntax:'<angle>';
  initial-value:0deg;
  inherits:false;
}
.cap{transition:--rotation .16s linear,box-shadow .3s var(--ease)}
.cap:hover{box-shadow:0 22px 52px -24px color-mix(in srgb,var(--c) 45%,rgba(0,0,0,.8))}

/* The arc follows the pointer, so it is motion answering an action rather than
   motion for its own sake — but the SWEEP is ours, so it goes under reduce. */
@media (prefers-reduced-motion:reduce){
  .cap{transition:none}
  .cap h4::before{transition:none}
}

/* ---------------------------------------------------------------------------
   THE MICROSOFT INTEGRATION CARDS      .msgrid / .mscard / .mscard-art  (I5.1)

   Three cards in one column, each with the live site's own artwork bleeding
   out of the bottom-right corner and zooming on hover.

   WHAT THIS REPLACES. Three stacked .splitrows — the same two-column
   construction three times, which I5.1 described as 1,400px of
   undifferentiated prose. A card with its own artwork gives each integration
   an identity the repeated splitrow could not.

   THE CARD IS FLAT WHITE BECAUSE THE ARTWORK DEMANDS IT. Two of the three
   source images have no alpha — sentinel.webp and entra.webp are lossy WebP,
   and their corner pixels sample at exactly rgb(255,255,255). defender.webp is
   lossless with real transparency. On a pure white card the two opaque ones
   have nothing to give away and need no blend mode at all.

   AN EARLIER VERSION USED mix-blend-mode:multiply to hide those white boxes on
   a tinted card. It worked, and it cost the marks their colour: multiply
   darkens every pixel it touches, so a bright red logo came out muted and read
   as half-transparent. The card colour was the bug, not the images. Anything
   other than flat white here brings the boxes back.
   --------------------------------------------------------------------------- */
.msgrid{
  display:grid;grid-template-columns:1fr;gap:clamp(16px,1.8vw,22px);
  max-width:1180px;margin:clamp(34px,4vw,52px) auto 0;
}

.mscard{
  position:relative;overflow:hidden;isolation:isolate;
  /* FLAT WHITE, AND THAT IS LOAD-BEARING. Sampled from the files themselves:
     sentinel.webp and entra.webp have backgrounds of exactly rgb(255,255,255)
     with no alpha, and defender.webp is properly transparent. On a pure white
     card the two opaque ones have nothing to give away — the white box IS the
     card — so the marks need no blend mode and render at full saturation.
     Any gradient here brings the boxes straight back. */
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  padding:clamp(26px,3vw,38px);
  box-shadow:0 10px 30px -22px rgba(12,18,38,.45);
  transition:border-color .3s var(--ease),box-shadow .3s var(--ease),transform .3s var(--ease);
}

/* ---------- the dot field ----------
   The reference's card is not just the outer gradient — its interior is a DOT
   MATRIX that fades out radially, with the dots nearest the centre brightest.
   That is the part worth having, and the first pass at this missed it: I read
   that page for background declarations, found the one radial-gradient line,
   and never looked at the card itself.

   THEIRS IS A CANVAS. 478x400, redrawn every frame. This is CSS, for three
   reasons: three cards means three render loops; a canvas needs a mount point,
   a resize observer and a teardown, all for decoration; and a dot grid is a
   repeating background, which is the one thing CSS backgrounds are actually
   for. The house already runs two canvases (the hero node field and the SIEM
   prism) and both earn it by drawing something a gradient cannot.

   THE MOTION IS A DRIFTING HIGHLIGHT, not redrawn dots. ::after is a soft
   bloom that travels slowly across the field on a 14s cycle, so dots brighten
   and dim as it passes. One compositor-friendly transform, no repaint, and the
   effect is the same: the field looks alive rather than printed.

   WHAT IS DELIBERATELY NOT HERE: the progress bar and the queued/running/
   succeeded loader. Those say "a job is running right now, and it is 41% done".
   These are three static cards about Microsoft integrations — a filling
   progress bar would be inventing a process that is not happening, which is
   the same class of thing approved-facts.js exists to stop. */
.mscard::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  /* 1px dots on a 13px pitch. Smaller and it reads as noise at laptop scale;
     larger and it reads as polka dots rather than a field. */
  background-image:radial-gradient(circle at center,rgba(15,20,35,.30) 1px,transparent 1.2px);
  background-size:13px 13px;
  /* TOP RIGHT, not bottom right. The marks sit in the bottom-right corner, and
     two of them are opaque white rectangles — dots behind them were simply
     covered up, which is why the field looked thinner than it is. Moving it to
     the opposite corner puts the dots where nothing overlaps them: above the
     artwork, right of the 62% text column, in the one part of the card that
     was empty. */
  -webkit-mask-image:radial-gradient(54% 104% at 100% 0,#000 0 24%,rgba(0,0,0,.45) 56%,transparent 86%);
  mask-image:radial-gradient(54% 104% at 100% 0,#000 0 24%,rgba(0,0,0,.45) 56%,transparent 86%);
  opacity:.85;transition:opacity .35s var(--ease);
}
.mscard:hover::before{opacity:1}

/* The dot grid lands on a different phase on each card, so three cards in a
   column do not read as one card printed three times — the same reason §2
   moves its filled cells. */
.mscard:nth-child(2)::before{background-position:6px 4px}
.mscard:nth-child(3)::before{background-position:-4px 7px}
.mscard:hover{
  border-color:var(--line-2);
  box-shadow:0 20px 48px -24px rgba(12,18,38,.55);
}

/* The text stops before the artwork rather than running under it. A percentage
   rather than a fixed width so the two stay in proportion as the card grows. */
.mscard-body{position:relative;z-index:1;max-width:min(100%,62%)}

.mscard-sum{
  font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--red);margin:0 0 12px;
}
.mscard h3{
  font-size:clamp(19px,2.1vw,24px);line-height:1.2;letter-spacing:-.02em;
  color:var(--ink);margin:0 0 12px;
}
.mscard-d{margin:0 0 18px;font-size:15.5px;line-height:1.7;color:var(--fg-soft)}
.mscard-pts{list-style:none;margin:0;padding:0;display:grid;gap:12px}
.mscard-pts li{font-size:14.5px;line-height:1.65;color:var(--fg-soft)}
.mscard-pts li b{display:block;color:var(--ink);font-weight:700;margin-bottom:2px}

/* ---------- the artwork ----------
   Anchored to the bottom-right corner and allowed to bleed off both edges, so
   it reads as part of the card rather than as a picture placed on one. */
.mscard-art{
  position:absolute;right:-18px;bottom:-24px;z-index:1;
  width:clamp(150px,20vw,232px);
  pointer-events:none;
  /* NO mix-blend-mode. It was here to drop the two opaque white backgrounds
     against a tinted card, and it cost the marks their saturation — multiply
     darkens every pixel it touches, so a bright red logo came out muted and
     looked half-transparent. With the card flat white the white backgrounds
     are invisible on their own and the reds are the reds. */
}
.mscard-art img{
  display:block;width:100%;height:auto;
  /* Bottom-right origin, so the zoom pushes the artwork further into its own
     corner instead of dragging it across the text. */
  transform-origin:100% 100%;
  transition:transform .55s var(--ease);
}
.mscard:hover .mscard-art img{transform:scale(1.12)}

/* ---------- responsive ----------
   Below this the artwork would take more of the card than the words do, so it
   shrinks and fades back rather than being removed: it is still the thing that
   tells the three cards apart at a glance. */
@media (max-width:720px){
  .mscard-body{max-width:100%}
  .mscard-art{width:120px;right:-10px;bottom:-14px;opacity:.5}
}

@media (prefers-reduced-motion:reduce){
  .mscard,.mscard-art img,.mscard::before{transition:none}
  .mscard:hover .mscard-art img{transform:none}
}

/* ---------------------------------------------------------------------------
   THE §4 CONCLUSION                        .msclose / .msclose-lab     (I5.2)

   "Why this matters for your security strategy" is the thing the three
   integration cards add up to. It was a FOURTH .splitrow with a bulleted list,
   which is exactly what I5.2 called out: built like the three above it, so it
   read as one more of them rather than as their conclusion.

   THE SHAPE IS .siem-included's, from Managed SIEM §4 — red rule on the left,
   mono label, a wash fading out to the right, square on the rule and rounded
   away from it. That component sits on a DARK ground and this one does not, so
   two things invert rather than carry across:

     LABEL   --red-3 there, --red here. On the dark band #c30017 measures
             3.03:1 and fails at 12.5px; on this light ground it measures
             5.82:1 and passes, while --red-3 drops to 3.23:1 and fails. The
             same size-dependent rule, pointing the other way.
     BODY    --dk-fg there, --ink here.

   THE WASH IS HALF THE STRENGTH of the original — .045 against .09. On a dark
   ground a red wash reads as a glow; on a light one the same alpha reads as
   pink, which is the note that sent the §1 capability cards back to the
   drawing board. The rule carries the colour; the wash only warms the ground
   it sits on.
   --------------------------------------------------------------------------- */
.msclose{
  max-width:1180px;margin:clamp(34px,4vw,52px) auto 0;
  padding:clamp(20px,2.2vw,26px) clamp(22px,2.4vw,30px);
  border-left:3px solid var(--red);
  background:linear-gradient(90deg,rgba(195,0,23,.045),rgba(195,0,23,0) 66%);
  border-radius:0 var(--radius) var(--radius) 0;
}
.msclose-lab{
  margin:0 0 14px;
  font-family:var(--mono);font-size:11px;font-weight:700;
  letter-spacing:.13em;text-transform:uppercase;color:var(--red);
}
.msclose ul{list-style:none;margin:0;padding:0;display:grid;gap:11px}
/* A hairline dash rather than a bullet or a tick. A tick would be claiming
   each line is a feature delivered; these are consequences of the three cards
   above, and a dash says "and this follows" without promising anything. */
.msclose li{
  position:relative;padding-left:26px;
  font-size:15.5px;line-height:1.66;color:var(--ink);
}
.msclose li::before{
  content:"";position:absolute;left:0;top:.72em;
  width:14px;height:2px;background:var(--red);border-radius:2px;
}

@media (max-width:640px){
  .msclose{padding:18px 18px 18px 20px}
  .msclose li{padding-left:20px;font-size:15px}
  .msclose li::before{width:10px}
}

/* ---------------------------------------------------------------------------
   THE INCIDENT RESPONSE HERO FIGURE          .irfig / .irf-*          (R1.1)

   The fifth hero figure, and the first that is not a diagram of a structure.
   Managed SIEM draws a console, SOC Triad the triforce, NDR a network
   topology, ITDR concentric identity rings — all of them things with a shape.
   Incident response has no shape. It has a clock, and the page's own second
   sentence is about time: "a slow response drastically increases the
   likelihood that a threat will negatively affect your infrastructure."

   NOTHING HERE CARRIES A NUMBER, and that is a constraint rather than a
   preference. The only durations cleared in approved-facts.js are l1Verdict
   and l2Verdict, which are managed-service triage verdicts. Whether they
   describe this engagement is ASK2 and nobody has answered it. So the ticks
   read as elapsed time and say nothing, which is honest and leaves the figure
   a place to put a real commitment the day ASK1 is settled.
   --------------------------------------------------------------------------- */
.irfig{
  margin:0;width:100%;max-width:580px;justify-self:end;
  opacity:1;transform:none;transition:none;      /* cancel the generic reveal */
}
.irfig svg{display:block;width:100%;height:auto}

/* ---------- the track ----------
   One rail the whole width, and a second "live" line over it from the incident
   onward carrying the gradient. Two lines rather than one because the section
   before the incident has to read as cooler without a second colour stop
   doing the work at the wrong place. */
.irf-rail{stroke:rgba(255,255,255,.14);stroke-width:2}
.irf-live{stroke:url(#irf-track);stroke-width:2.5;stroke-linecap:round}

/* ---------- elapsed time, unlabelled ---------- */
.irf-ticks line{stroke:rgba(255,255,255,.16);stroke-width:1}

/* ---------- preparation, before the incident ----------
   Dimmer than the rest on purpose: by the time anyone reads this page during
   an incident, this is the phase that is already over. */
.irf-pre{opacity:.62}
.irf-brace{stroke:rgba(47,107,255,.5);stroke-width:1.5;stroke-dasharray:3 4}
.irf-drop{stroke:rgba(47,107,255,.32);stroke-width:1.5}

/* ---------- nodes ---------- */
.irf-dot{fill:#fff;opacity:.9}
.irf-dot-hit{fill:var(--red-3)}
.irf-dot-end{fill:var(--green)}
.irf-stem{stroke:rgba(255,255,255,.22);stroke-width:1.5}

/* ---------- labels ----------
   Mono, tracked, tiny — the same treatment the eyebrows use, so the figure
   reads as part of the page rather than as an illustration dropped onto it. */
.irf-ph{
  font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.13em;
  fill:rgba(255,255,255,.82);
}
.irf-sub{
  font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;
  fill:rgba(255,255,255,.42);
}
.irf-hit{
  font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.15em;
  fill:var(--red-3);
}

/* ---------- motion ----------
   Two things move and nothing else: the burst at the incident breathes, and
   one marker sweeps the response. A figure where everything animates reads as
   a screensaver. */
.irf-burst{transform-origin:150px 250px}
@media (prefers-reduced-motion:no-preference){
  .irf-burst{animation:irfBurst 3.4s ease-in-out infinite}
  .irf-sweep{animation:irfSweep 7s cubic-bezier(.45,0,.25,1) infinite}
}
/* Without the animation the sweep would sit at x=0, off the rail and visibly
   wrong, so its resting place is the incident. */
.irf-sweep{transform:translateX(150px)}
.irf-sweep-line{stroke:rgba(255,255,255,.5);stroke-width:1}
.irf-sweep-dot{fill:#fff}

@keyframes irfBurst{
  0%,100%{opacity:.5;transform:scale(.88)}
  50%    {opacity:1;transform:scale(1.06)}
}
/* Starts at the incident, not at the left edge: the response begins when the
   incident does. Holds briefly at the close before repeating. */
@keyframes irfSweep{
  0%      {transform:translateX(150px);opacity:0}
  6%      {opacity:1}
  86%,100%{transform:translateX(564px);opacity:0}
}

@media (max-width:1080px){
  .irfig{max-width:520px;justify-self:center;margin-top:34px}
}

/* =========================================================================
   INCIDENT RESPONSE - THE NIST LIFECYCLE SPLIT ROLL          R5, 22 Sep 2026

   Ported from a React component built on GSAP ScrollTrigger and Tailwind.
   None of that stack exists here, so the geometry is CSS transforms set from
   about sixty lines in main.js, and the pin is position:sticky rather than a
   scripted one. Same route .tcar took from React/framer-motion at I4.1.

   THE STATIC STATE IS THE REAL ONE. Everything below the .is-live line is an
   enhancement. Without the script, under 1080px, or with reduced motion on,
   each phase is one two-column row in a plain stack: heading left, card
   right, nothing hidden and nothing pinned. That is also why a phase is ONE
   .lif-item holding both halves rather than two parallel columns - two
   columns would put all four headings before all four cards in the source.
   ========================================================================= */

/* overflow-x:CLIP, and the distinction is the whole thing.

   overflow:hidden here broke the pin outright. Any overflow value except
   visible and clip makes the element a scroll container, so the sticky stage
   inside it sticks to a box that is itself scrolling with the page - which
   is to say it does not stick. Measured with it on: stageTop tracked the
   block top exactly, 0 down to -1998, through the entire pin.

   Leaving it visible was no good either. At the extremes of the arc a card
   sits 558px right of home, which measured 266px of horizontal overflow on
   the document at 1440. body{overflow-x:hidden} hides the scrollbar but the
   document is still that wide.

   clip is the one value that clips WITHOUT creating a scroll container, so
   the pin survives and the overflow goes. Paired with y visible, which is
   the one pairing the spec allows, because the stage has to be able to
   stick past this box vertically. Browsers without it fall back to no
   clipping, which is exactly where this started. */
.lifesec{overflow-x:clip;overflow-y:visible}
.lifesec .sec-head{max-width:62ch;margin-bottom:26px}
/* Closer than the house 60px, because the lede here is not introducing a
   grid that starts immediately - it is introducing a frame whose first
   card sits another (frameHeight - cardHeight) / 2 below its top edge.
   The house spacing plus that is two gaps doing one job. */

/* ---- the static stack, and the fallback ---- */
.lifecar{position:relative;margin-top:44px}
.lifecar-stage{display:block}
.lifecar-frame{display:grid;gap:clamp(30px,4vw,54px)}

.lif-item{
  display:grid;grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);
  gap:clamp(20px,3.4vw,52px);align-items:center;
}

/* The number and the name. The number is the ONE place on this page where a
   numbered marker is honest - NIST defines these four as an ordered cycle,
   which is a stronger case than the kill-chain ordering at I4.2 on ITDR. */
.lif-head{position:relative}
.lif-n{
  display:block;font-family:var(--mono);font-weight:700;
  font-size:clamp(46px,6.4vw,104px);line-height:.86;letter-spacing:-.04em;
  color:transparent;-webkit-text-stroke:1px var(--dk-line-2);
  margin:0 0 10px;
}
.lif-t{
  font-family:var(--display);font-weight:700;
  font-size:clamp(23px,2.5vw,38px);line-height:1.08;letter-spacing:-.025em;
  color:#fff;margin:0;max-width:13ch;
}

/* The card. Follows .tcard from the ATT&CK section - a mono header line over
   a rule, then the body - without reusing the class, which is bound to the
   carousel it lives in. Same tokens, same proportions, so the two dark card
   blocks on this rebuild read as one family. */
.lif-card{
  background:linear-gradient(180deg,var(--dk-panel),var(--dk));
  border:1px solid var(--dk-line-2);
  border-radius:var(--radius-lg);
  padding:22px 24px 20px;
  box-shadow:0 8px 24px -6px rgba(0,0,0,.5);
}
.lif-card-h{
  display:flex;align-items:baseline;justify-content:space-between;gap:14px;
  flex-wrap:wrap;border-bottom:1px solid var(--dk-line);
  padding-bottom:12px;margin-bottom:14px;
}
.lif-src{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--red-3)}
.lif-ph{font-family:var(--mono);font-size:10px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--dk-mut)}
.lif-what{margin:0;color:var(--dk-soft);font-size:15px;line-height:1.6}

/* The engagement line is SEPARATED rather than bolded, because the two
   sentences are different KINDS of statement: one reports what a published
   standard says, one describes what we do. As two plain paragraphs the
   second read as more of the framework, which is the one thing this section
   must not do - see the header of the data file. */
.lif-ours{
  margin:14px 0 0;padding:13px 15px;border-radius:12px;
  background:rgba(255,255,255,.03);border:1px solid var(--dk-line);
  color:#fff;font-size:14.5px;line-height:1.55;
}
.lif-k{display:block;font-family:var(--mono);font-size:9.5px;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--dk-mut);margin-bottom:6px}

/* The rail is hidden in the static stack, where all four phases are already
   on screen and a progress indicator would be indicating nothing. */
.lif-rail{display:none}

@media (max-width:820px){
  .lif-item{grid-template-columns:1fr;gap:16px;align-items:start}
  .lif-t{max-width:none}
}

/* =========================== .is-live ===================================
   Added by main.js only at 1080px and up, with motion allowed. Everything
   above still applies; this re-homes the items onto the arc.

   THE PIN IS position:sticky. The source component asked ScrollTrigger to pin
   the stage and add spacing, which is a scripted layout change recomputed on
   every resize. A tall box with a sticky child does the same job in two
   declarations and cannot desynchronise from the scrollbar.

   THE SCROLL LENGTH IS DERIVED FROM THE DATA. --lif-n is written onto the
   block by the template from the phase count, so a fifth phase lengthens the
   pin on its own rather than needing this number edited. */
.lifecar.is-live{
  /* 52vh per step, down from 74vh on 22 Sep. With the phases inside a
     bounded frame the roll no longer has a whole viewport to cross, so it
     does not need a whole viewport of scroll to cross it.

     The clearance comes off the block height as well as the stage height,
     so the travel is still exactly (n - 1) x 52vh and the section is 93px
     shorter than it was.

     --lif-stage IS THE STAGE'S OWN HEIGHT, measured by main.js, from 22 Sep.
     It used to be a full viewport less the clearance, and this calc said so
     in longhand. The stage is content-height now (see below), so the number
     to cancel is no longer one this stylesheet can work out - the frame, the
     gap and the rail decide it. main.js measures the stage and writes it
     here, and the fallback is the old expression so the section still
     behaves if that write never happens.

     The point of cancelling it at all: travel = blockHeight - stageHeight,
     so whatever the stage is, the roll gets exactly (n - 1) x 52vh of
     scroll and the pacing does not move when the stage does. */
  height:calc(var(--lif-stage,calc(100svh - var(--lif-clear,96px))) + (var(--lif-n,4) - 1) * 52vh);
  margin-top:0;
  /* THE FRAME HEIGHT, and it is the number that controls how much of the
     phase either side you can see. That is not obvious, so it is written
     down: the active card is centred, so the room above it is
     (frameHeight - cardHeight) / 2. At 468px against a 290px card that was
     89px - which is why the neighbours were "barely visible". Nothing to do
     with opacity; there was simply nowhere for them to be.

     Now it takes what the stage can spare: a full viewport less the header
     clearance, the rail and its gap. Capped at 660 so a tall monitor does
     not stretch a 290px card into the middle of an empty field, and floored
     at 400 so a short laptop still shows something either side.

     CAPPED AT 580, down from 660 on 22 Sep. The cap IS the top and bottom
     padding Royce asked about: the active card is centred, so half of
     (frameHeight - cardHeight) sits empty above it and half below, and at
     the START of the roll there is no previous phase to fill the top half.
     660 put 185px of nothing between the lede and the first card. 580 puts
     145px, and the next phase still shows about 145px of itself once the
     roll is moving - against 83px before any of this. One number, and it
     trades those two against each other directly. */
  /* NO LONGER THE FRAME'S HEIGHT, from 22 Sep - the frame is flex:1 and
     fills the stage. Kept because main.js reads the frame's MEASURED
     height for the arc radius, and a declared value here is what that
     measurement falls back to if it ever runs before layout. */
  --lif-fh:clamp(400px,calc(100svh - 168px),580px);
}
/* THE HEADER CLEARANCE IS A STICKY OFFSET, NOT PADDING, from 22 Sep.

   It was padding-top on a stage stuck at top:0, which clears the masthead
   while pinned - and costs the same 93px of empty space when the section is
   NOT pinned, which is exactly when the reader is looking at the sentence
   above it. Measured: 127px between the lede and the frame, 93 of it this.

   top:<clearance> does the identical job while stuck and nothing at all
   before that, because a sticky offset only applies once the element
   sticks. The stage loses the same amount of height so the frame still
   fits the viewport. */
.lifecar.is-live .lifecar-stage{
  position:sticky;top:var(--lif-clear,96px);
  /* A FULL VIEWPORT, and this is a correction of my own earlier fix.

     The first attempt at Adam's "a lot of blank space underneath" shrank
     this stage to its content height, on the reasoning that a viewport-tall
     stage holding a 580px frame has a few hundred pixels spare. It measured
     clean - nothing under the rail inside the stage - and it made the
     problem WORSE on screen, because the empty space was never inside the
     stage.

     THE BLOCK IS 2,880px TALL. That is what the pin is made of. While the
     stage is stuck, every pixel of the viewport the stage does not occupy
     shows the block behind it, which is the same dark gradient with nothing
     on it. Shrink the stage to 628px on a 1,337px window and you have not
     removed 600px of dead dark, you have moved it out of the stage and left
     it on screen exactly where it was.

     So the stage fills the viewport, and the emptiness is dealt with where
     it actually lives - inside the frame, which now fills the stage. See
     below. */
  height:calc(100svh - var(--lif-clear,96px));box-sizing:border-box;
  /* A flex column holding the frame and the rail. The rail used to sit
     outside this box and place itself with a calc against the frame height;
     it was wrong by 490px and rendered ABOVE the frame. A gap cannot be
     wrong about where the frame ends.

     CENTRED, and this is the third position on it - so the reasoning for
     landing back where Royce started is worth writing down.

     He centred it, then moved to flex-start because centring left about
     240px of empty dark above the frame. flex-start did not remove that
     240px, it moved it below the rail, where it read as the page having
     stopped - which is what Adam asked about.

     What changed is the amount. The frame is no longer a fixed 580 in a
     viewport-tall stage with hundreds of pixels spare; it is sized to its
     own contents and takes most of the stage, so the leftover is small.
     Centring splits what is left evenly above and below instead of putting
     all of it at one end, and a small balanced margin reads as composition
     where the same pixels at one end read as a gap. */
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:20px;
}

/* THE FRAME, and it is the whole of Royce's note on 22 Sep: the phases
   should look like they are in ONE PLACE. A bounded box that clips does
   two things at once - it gives the roll a visible home, and it means the
   phase before and the phase after are CUT OFF by its edges rather than
   flying off the page. The cut edge is what shows the motion. That is the
   thing the ATT&CK deck on ITDR gets right and the first version of this
   did not: at a 558px radius the neighbours were 558px out and 558px down,
   which is to say nowhere.

   overflow:hidden is SAFE here and was not safe on the section. The sticky
   element is the stage, which is this box's ANCESTOR. Clipping inside a
   sticky element is fine; clipping around one is what killed the pin. */
/* THE FRAME PAINTS NOTHING AT ALL. No fill, no border, no radius.

   It earns its keep by bounding the roll and fading its edges, and it needs
   to be invisible to do either - Royce, twice, and the second time was the
   one that found the real fault.

   MASK, NOT A PAINTED GRADIENT, and that is the fix rather than a
   refinement. The edges used to be two pseudo-elements filled with
   var(--ink), which is #12151d - a BLUE-grey. The section behind them is a
   red-black gradient, rgb(24,12,15) down to rgb(11,5,7). So the fades were
   painting the wrong colour over a moving target, and no fixed colour could
   ever have matched a gradient anyway. A mask fades the CONTENT and draws
   nothing, so it blends with whatever is behind it, gradient or not.

   The stops are asymmetric on purpose: 9% at each end is enough to take the
   hard edge off a card sliding out without eating the part of it that is
   there to be seen. */
/* THE FRAME FILLS THE STAGE, 22 Sep, and this is where the blank space
   actually goes.

   It was a fixed height - clamp(400px, 100svh - 168px, 580px) - inside a
   viewport-tall stage, so on any tall window several hundred pixels of the
   stage sat under the rail with nothing in them. That is the band in Adam's
   screenshot: the rail at 749px and flat black to 1,337.

   flex:1 removes it by construction rather than by arithmetic. There is no
   leftover to place, at any window height, so the question the note below
   argues about - top-align or centre - stops existing.

   WHAT THE 580 CAP WAS PROTECTING, because it was not arbitrary: the active
   card is centred in the frame, so half of (frame - card) is empty above it,
   and at the START of the roll there is no previous phase to fill that half.
   660 put 185px of nothing between the lede and the first card.

   That cost is still real and it is now paid at the top of the frame instead
   of the bottom of the stage. Three things make it the better trade:
   the mask already fades the top 9% to nothing, so the band it lands in is
   not flat black; it is TRANSIENT, gone the moment the roll starts moving,
   where the old gap was there at every scroll position; and mid-roll the
   room is filled, because RY resolves to the tallest card and the phases
   above and below abut the active one instead of sitting a fixed distance
   out. Three cards of content, roughly 870px of it, in a frame that is
   about 1,150px on a tall window with 104px of fade at each end.

   min-height rather than the old 400px floor: at 1080px wide and a very
   short window the stage can offer less than a card, and flex:1 would
   happily hand it 200px. */
.lifecar.is-live .lifecar-frame{
  position:relative;display:block;width:100%;flex:none;
  /* BACK TO A DECLARED HEIGHT, and main.js measures the number into it -
     2.95 x the tallest card, which is exactly the three the arc can hold.
     flex:1 was the 22 Sep attempt at Adam's gap and it overshot: filling
     the stage on a tall window made a 1,200px box for 870px of cards, so
     the empty space moved from under the rail to inside the frame. */
  height:var(--lif-fh,460px);min-height:340px;
  overflow:hidden;
  border:0;background:none;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 9%,#000 91%,transparent 100%);
          mask-image:linear-gradient(180deg,transparent 0,#000 9%,#000 91%,transparent 100%);
}

/* The two painted edge gradients were removed on 22 Sep. The mask on the
   frame does the same job without drawing anything - see above. */

/* Each phase now occupies the whole stage, and the grid columns are what put
   its two halves left and right. JS moves them along the arc from there, so
   every transform is an OFFSET FROM HOME rather than an absolute position -
   which is why none of the geometry has to know about the page layout. */
/* Each phase fills the FRAME now, not the stage. Its two halves still get
   their home positions from the grid columns; the script only offsets them
   from there.

   NO SIDE PADDING, restored 22 Sep. Adding the frame had quietly narrowed
   the content inside it - 36px of padding a side plus a tighter card took
   about 110px out of the measure. The frame was meant to bound the roll,
   not shrink the copy. */
.lifecar.is-live .lif-item{
  position:absolute;inset:0;
  align-items:center;
}
.lifecar.is-live .lif-head,
.lifecar.is-live .lif-card{
  will-change:transform,opacity;
  transform-origin:50% 50%;
}
.lifecar.is-live .lif-card{max-width:560px}
.lifecar.is-live .lif-n{font-size:clamp(52px,5.4vw,86px)}

/* The active phase is the only one that gets the red number and the lit
   border. A class rather than another stop on the opacity ramp, because it is
   a THRESHOLD: a card is either the one being read or it is not. */
.lifecar.is-live .lif-item.is-on .lif-n{-webkit-text-stroke:1px var(--red-3)}
.lifecar.is-live .lif-item.is-on .lif-card{
  border-color:rgba(255,59,82,.42);
  box-shadow:0 24px 60px -14px rgba(0,0,0,.75),0 0 0 1px rgba(255,59,82,.18);
}

/* The rail, which is the one thing that survives the move from four visible
   cards to one: the old .cards-4 layout showed the whole sequence at a
   glance. It sticks to the bottom of the stage rather than the block, so it
   does not scroll away under the phase it is describing. */
.lifecar.is-live .lif-rail{
  display:flex;gap:10px;align-items:center;justify-content:center;
  position:static;margin:0;padding:0;flex:none;
  pointer-events:none;
}
.lifecar.is-live .lif-rail li{
  list-style:none;
  font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.1em;
  color:var(--dk-mut);border:1px solid var(--dk-line);border-radius:100px;
  background:rgba(12,15,22,.72);padding:5px 11px;
  transition:color .3s,border-color .3s,background .3s;
}
.lifecar.is-live .lif-rail li.is-on{
  color:#fff;border-color:rgba(255,59,82,.5);background:rgba(195,0,23,.22);
}

@media (prefers-reduced-motion:reduce){
  .lifecar.is-live .lif-rail li{transition:none}
}

/* =========================================================================
   INCIDENT RESPONSE - THE COVERAGE BAND                      RP4, 22 Sep 2026

   Three centres against one day, every bar full. It replaces four big-number
   cards that were not four of the same thing: a count, a schedule, a year and
   a certification in four identical boxes, with "ISO" sitting where a number
   should be.

   THE BAND MAKES THE CLAIM INSTEAD OF ASSERTING IT. Follow-the-sun would draw
   as staggered bars with the handovers showing as gaps between them. These are
   continuous and simultaneous, which is the difference approved-facts.js calls
   the stronger claim - an incident does not change hands because a shift ended
   somewhere else.

   Light, not dark. The lifecycle above it is the page's dark surface and two
   adjacent dark sections read as one long band rather than two sections.
   ========================================================================= */

.socband{margin-top:clamp(28px,4vw,44px)}

/* The hour marks. justify-between so 00:00 and 24:00 sit on the bar's ends
   rather than floating inside them. */
.socband-axis{
  display:flex;justify-content:space-between;
  font-family:var(--mono);font-size:10px;letter-spacing:.1em;
  color:var(--fg-mut);margin:0 0 10px;
  padding-left:var(--soc-label,132px);
}

.socband-rows{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.socband-row{
  display:grid;
  grid-template-columns:var(--soc-label,132px) minmax(0,1fr) auto;
  align-items:center;gap:14px;
}
.socband-place{font-weight:600;font-size:15.5px;color:var(--ink)}

/* The bar. A full-width fill with hour ticks drawn over it, so the eye reads
   "all of it" rather than "a bar". The ticks are a repeating-linear-gradient
   rather than elements, because they carry no meaning of their own. */
.socband-bar{
  position:relative;display:block;height:34px;border-radius:7px;
  background:linear-gradient(90deg,var(--red),#e0303f);
  box-shadow:0 6px 18px -8px rgba(195,0,23,.55);
}
.socband-bar::after{
  content:"";position:absolute;inset:0;border-radius:inherit;
  background:repeating-linear-gradient(90deg,
    rgba(255,255,255,.22) 0 1px, transparent 1px calc(100% / 24));
}

.socband-state{
  font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--red);white-space:nowrap;
}

.socband-note{margin:0;font-size:14.5px;line-height:1.6;color:var(--fg-soft);max-width:54ch}

/* THE BAND FOOT. Both of these were loose paragraphs sitting under the band
   with nothing holding them to it. One block, under a rule that spans the
   band, two columns: who is on it, and why it is continuous. */
.socband-foot{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(20px,3vw,44px);
  margin-top:clamp(20px,2.4vw,28px);padding-top:clamp(18px,2.2vw,24px);
  border-top:1px solid var(--line);
}
.socband-who span{
  display:block;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--red);
  margin-bottom:8px;
}
.socband-who p{
  margin:0;font-size:clamp(16px,1.7vw,18.5px);line-height:1.45;color:var(--ink);
}

/* The icon holder inside a .cap. Added for Incident Response, the second
   page to take the component; ITDR's three caps carry no icon, so this rule
   never applies to them. It takes the card's own --c, which ties it to the
   border arc and the dash instead of leaving a third accent floating. */
.cap .ico{
  width:42px;height:42px;border-radius:12px;display:grid;place-items:center;
  margin:0 0 16px;color:var(--c);
  background:color-mix(in srgb,var(--c) 14%,transparent);
  border:1px solid color-mix(in srgb,var(--c) 34%,transparent);
}
.cap .ico svg{width:20px;height:20px}

/* The label column collapses first: at narrow widths the place name goes above
   its bar rather than beside it, so the bar keeps its length and the band
   still reads as a full day. */
@media (max-width:820px){
  .socband{--soc-label:0px}
  .socband-row{grid-template-columns:1fr auto;gap:8px 12px}
  .socband-place{grid-column:1;grid-row:1}
  .socband-state{grid-column:2;grid-row:1;align-self:center}
  .socband-bar{grid-column:1 / -1;grid-row:2;height:26px}
  .socband-rows{gap:18px}
  .socband-foot{grid-template-columns:1fr;gap:16px}
}

@media (prefers-reduced-motion:reduce){.socband-bar{box-shadow:none}}

/* ---------- partner lockup ----------
   What a partner's own marketing looks like: their mark, then ours under it.

   THE SLOT IS DASHED AND EMPTY ON PURPOSE. A filled box with a fake logo in
   it reads as a real customer and invites the question of who it is; an
   obviously-empty slot reads as "this is where yours goes", which is the
   thing being communicated. The dash is the oldest convention there is for
   a placeholder and needs no caption.

   Sized against the real thing: the slot is the proportion of a typical
   supplier wordmark, and the endorsement line below is deliberately smaller,
   because in an endorsement lockup the endorser never outweighs the brand. */
.plock{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:clamp(28px,3.4vw,44px) clamp(22px,3vw,40px);box-shadow:var(--shadow-sm);text-align:center}
.plock-slot{display:flex;align-items:center;justify-content:center;
  height:clamp(64px,7vw,86px);border:2px dashed var(--line-2);border-radius:12px;
  font-family:var(--mono);font-size:12px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--fg-mut);background:var(--mist)}
.plock-by{display:flex;flex-direction:column;align-items:center;gap:5px;padding-top:18px;margin-top:18px;
  border-top:1px solid var(--line)}
.plock-by-k{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.18em;
  text-transform:uppercase;color:var(--fg-mut)}
.plock-by-p{font-family:var(--display);font-weight:800;font-size:clamp(17px,2vw,21px);
  letter-spacing:-.02em;color:var(--ink)}

.plock-pts{list-style:none;margin:24px 0 0;padding:0;display:grid;gap:14px}
.plock-pts li{display:grid;gap:3px;padding-left:18px;position:relative}
.plock-pts li::before{content:"";position:absolute;left:0;top:7px;width:7px;height:7px;
  border-radius:50%;background:var(--red-wash);box-shadow:inset 0 0 0 1.5px var(--red)}
.plock-pts strong{font-size:14.5px;letter-spacing:-.01em;color:var(--ink)}
.plock-pts span{font-size:14px;line-height:1.6;color:var(--fg-soft)}
.plock-note{margin:18px 0 0;font-size:13px;line-height:1.6;color:var(--fg-mut)}


/* ===================================================================
   THE LIGHT ISLAND RESET — last in the file on purpose.

   .formcard is a white card that every page puts inside a dark hero:
   contact, demo, partners, book a meeting, the workshops. Everything in it
   inherits the dark section's text colours and then sits on paper.

   The card resets what it knows about near its own rules, and that was enough
   until a SECTION-level rule reached into it. `.sec-dark .sp-body
   p:not(.pullquote)` is (0,3,1); the card's own `.formcard .formnote` is
   (0,2,0), so on the partner page the note rendered --dk-soft on white at a
   contrast ratio of 1.84 - invisible - and no amount of rewriting the card's
   own rule could win.

   So the reset is stated at the same specificity as the rules it has to beat,
   and placed last, where source order settles the tie. Two rules rather than a
   pile of !important, which would win the argument and then lose the next one
   somewhere else.

   Found 22 Sep 2026 by walking every text node on twelve pages and comparing
   its computed colour against its computed ground - the same sweep that found
   --fg-mut under AA and the eyebrows failing on dark sections.
   =================================================================== */
/* THE ELEMENT QUALIFIERS ARE LOAD-BEARING, not tidiness. The rule that reaches
   into the card is `.sec-dark .sp-body p:not(.pullquote)` - three classes and
   an element, (0,3,1), because a :not() argument counts toward specificity.
   Written as `.sec-dark .formcard .formnote` this reset is (0,3,0) and STILL
   loses, which it did on the partner page after the first attempt at this fix.
   With the element it ties at (0,3,1), and being last in the file it wins. */
.sec-dark .formcard p:not(.pullquote),.sec-dark .formcard li,
.darkband .formcard p:not(.pullquote),.darkband .formcard li{color:var(--fg-soft)}
.sec-dark .formcard p.formnote,.sec-dark .formcard .muted,
.darkband .formcard p.formnote,.darkband .formcard .muted{color:var(--fg-mut)}
.sec-dark .formcard h1,.sec-dark .formcard h2,
.sec-dark .formcard h3,.sec-dark .formcard h4{color:var(--ink)}

/* The grouped-by-topic Learning Hub CSS was here and is gone with its
   template, 23 Sep 2026 — see the note on the /learning-hub/ route in
   server.js. The hub is one flat grid with a toolbar again; nothing else used
   .lh-row or .lh-head, so it is deleted rather than left to rot. */

/* ---------- .darkstage: a dark panel set into a light section ----------
   Added 23 Sep 2026, extracted the moment it was needed twice.

   IT IS NOT CALLED .stage. That name is already taken twice on the cybershield
   page alone — .gallery .stage and #stages .stage — and a bare .stage would
   have collided with both from a thousand lines away. That is the .rec bug
   from 22 Sep, which cost an afternoon, so the name is checked now.

   WHAT IT IS FOR. A light section whose payload is a mechanism rather than an
   argument. Paper cards on a near-white ground sit at about 1.06:1, so a
   section built entirely from them has no subject — everything is the same
   weight and the eye has nowhere to land. One dark panel gives the section a
   subject and leaves everything around it quiet.

   USE IT ONCE PER PAGE. Two would be two subjects, which is none. */
.darkstage{position:relative;isolation:isolate;overflow:hidden;
  padding:clamp(26px,3.4vw,42px);
  background:var(--dk);border:1px solid var(--dk-line);border-radius:var(--radius-lg);
  color:var(--dk-fg);box-shadow:0 34px 80px -44px rgba(10,16,32,.6)}
/* The ambient light is set by whoever uses the base rather than by the base,
   because what the two corners MEAN is the user's business. On the deployment
   block it is ours/yours.

   ONE USER AT PRESENT. Pricing had it for an afternoon on 23 Sep and Adam took
   it back off: "i dont like that on pricing, go back to what it was." The base
   stays extracted — it was worth extracting the moment it was wanted twice,
   and the reason it came back off pricing was that page's own rhythm, not the
   component. */
.darkstage::after{content:"";position:absolute;top:0;left:0;right:0;height:2px;
  background:var(--seam-grad);pointer-events:none}
.dep-stage::before{content:"";position:absolute;inset:0;
  z-index:-1;pointer-events:none;
  background:
    radial-gradient(780px 360px at 20% -12%, color-mix(in srgb,var(--red) 20%,transparent), transparent 62%),
    radial-gradient(660px 340px at 88% 112%, color-mix(in srgb,var(--blue) 18%,transparent), transparent 60%)}

/* ---------- CYBERSHIELD AI: buying the platform ---------- */
/* REBUILT the same day it shipped, 23 Sep 2026. Adam, on the first version:
   "it blends in with the background too much."

   He was right, and the cause was hierarchy rather than contrast. Every block
   in the section had the same treatment - a faint panel, one radius, one
   hairline border, on a ground almost the same colour. Raising every border by
   a notch would only have produced a louder version of the same flat page.
   What the section actually lacked was a subject: the four hosting shapes are
   its whole payload and they sat in the same visual register as the prose.

   So the shapes get a dark stage to themselves and everything around them
   stays quiet. Three registers now, each meaning something:
     .dep-claim  the two promises (single tenant, any region). Paper, RGB rail,
                 matched pair, so the second is recognised as the same kind of
                 statement as the first.
     .dep-stage  the mechanism. Dark, lit red from the ours corner and blue
                 from the yours corner.
     .dep-row    who does what. Paper, owner-coloured edge.

   Card rails here are --seam-grad-v, not red. That is the ruling from 22 Sep
   on the package boxes: rails on this site are the RGB seam. */

/* The two promises. One treatment, so the pair reads as a pair. */
.dep-claim{position:relative;background:linear-gradient(180deg,var(--paper) 0%,var(--mist) 100%);
  border:1px solid var(--line);border-radius:0 var(--radius) var(--radius) 0;overflow:hidden;
  padding:22px 26px;margin-top:clamp(26px,3vw,36px);box-shadow:var(--shadow-sm)}
.dep-claim::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--seam-grad-v)}
.dep-claim .k{display:block;font-family:var(--display);font-weight:700;
  font-size:17.5px;letter-spacing:-.02em;margin-bottom:8px}
.dep-claim p{margin:0;color:var(--fg-soft);font-size:15.5px;line-height:1.65;max-width:80ch}
/* The caveat is styled as part of the claim, not as small print under it. An
   honest limit loses its value if it is designed to be skipped. */
.dep-caveat{margin-top:10px !important;padding-top:10px;border-top:1px dashed var(--line-2);
  color:var(--fg-mut) !important;font-size:14px !important}

/* ---- the dark stage ----
   .darkstage is the shared base and lives in the DARK SURFACES block near the
   top of this file. Everything below is what the deployment block does with
   it. The ambient light is doing the same job the pills do: red is our side,
   blue is yours, and the panel is lit from those two corners. */
.dep-stage{margin-top:clamp(30px,3.5vw,44px)}
.dep-stage-head h3{font-size:clamp(21px,2.3vw,26px);letter-spacing:-.025em;margin:0 0 10px;color:var(--dk-fg)}
.dep-stage-head p{margin:0 0 clamp(22px,2.6vw,30px);color:var(--dk-soft);
  font-size:15.5px;line-height:1.65;max-width:74ch}

/* The two movable components. They are a pair and read as one idea, so one
   row and not a grid that could wrap to three. */
.dep-parts{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;
  margin-bottom:clamp(20px,2.4vw,28px)}
.dep-part{background:color-mix(in srgb,var(--dk-fg) 4%,transparent);
  border:1px solid var(--dk-line);border-radius:var(--radius);padding:16px 18px}
.dep-part .pk{display:flex;align-items:center;gap:9px;font-family:var(--display);
  font-weight:700;font-size:15.5px;margin-bottom:6px;color:var(--dk-fg)}
.dep-part .pk svg{width:16px;height:16px;flex:none;color:var(--dk-mut)}
.dep-part p{margin:0;color:var(--dk-mut);font-size:13.5px;line-height:1.6}
@media (max-width:760px){.dep-parts{grid-template-columns:1fr}}

.dep-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;align-items:stretch}
@media (max-width:1040px){.dep-grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){.dep-grid{grid-template-columns:1fr}}
.dep-shape{display:flex;flex-direction:column;background:var(--dk-panel);
  border:1px solid var(--dk-line-2);border-radius:var(--radius);padding:18px;
  /* --dk-line-2 at rest, not --dk-line. --dk-panel on --dk is only 1.09:1, so
     on a dark stage the EDGE is what separates a card from its ground, not the
     fill — and a hairline at 10% white is not an edge. Exactly the complaint
     that caused this rebuild, one level down. */
  box-shadow:inset 0 1px 0 color-mix(in srgb,var(--dk-fg) 7%,transparent);
  transition:transform .3s var(--ease),border-color .3s,background .3s}
.dep-shape:hover{transform:translateY(-4px);border-color:var(--dk-line-2);
  background:color-mix(in srgb,var(--dk-panel) 86%,var(--dk-fg))}
.dep-shape h4{font-size:16.5px;letter-spacing:-.015em;margin:0 0 8px;color:var(--dk-fg)}
.dep-shape > p{margin:0 0 16px;color:var(--dk-mut);font-size:13.5px;line-height:1.6;flex:1}

/* The track is the card's payload: a boundary with a lane either side, so
   "who holds what" is answered by position before it is read. The dashed seam
   is the same line in all four cards - that is what makes them comparable at a
   glance rather than four separate lists. */
.dep-track{display:grid;grid-template-columns:1fr 1px 1fr;gap:12px;
  padding-top:14px;border-top:1px solid var(--dk-line)}
.dep-seam{display:block;width:1px;transform-origin:top;
  background:repeating-linear-gradient(180deg,var(--dk-line-2) 0 4px,transparent 4px 9px)}
.dep-track .lane{display:flex;flex-direction:column;gap:7px;align-items:flex-start;min-width:0}
.dep-track .sk{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:var(--dk-mut)}
.tagpill{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.06em;
  border-radius:100px;padding:4px 10px;white-space:nowrap;border:1px solid transparent;
  transition:transform .25s var(--ease)}
/* --red-3 and --blue-dk, not --red and --blue: these sit on --dk-panel, which
   is exactly the mistake the .term rail made in September. */
.tagpill.ours{color:var(--red-3);background:color-mix(in srgb,var(--red-3) 12%,transparent);
  border-color:color-mix(in srgb,var(--red-3) 36%,transparent)}
.tagpill.yours{color:var(--blue-dk);background:color-mix(in srgb,var(--blue-dk) 12%,transparent);
  border-color:color-mix(in srgb,var(--blue-dk) 36%,transparent)}
.tagpill.none{color:var(--dk-mut);background:transparent;border:1px dashed var(--dk-line-2)}
/* Hover nudges each lane away from the seam. Small, and it says the one thing
   the card exists to say. */
.dep-shape:hover .lane.is-ours .tagpill{transform:translateX(-3px)}
.dep-shape:hover .lane.is-yours .tagpill{transform:translateX(3px)}

/* ---- the entrance ----
   Driven entirely off .reveal's own .in class from main.js, so no new
   JavaScript and no second observer. --i is the card index, --j the pill index
   within its lane.

   The pills animate OUT FROM THE SEAM into their lane. That is the one piece
   of motion here carrying meaning rather than decoration: it shows the two
   components being placed on a side, which is the whole idea of the block.

   Fill mode is BACKWARDS, never forwards. Forwards would leave the animation
   owning `transform` for the life of the page, and animations beat ordinary
   declarations, so the hover lift would silently stop working. */
@keyframes depRise{from{opacity:0;transform:translateY(16px)}}
@keyframes depSeamDraw{from{transform:scaleY(0)}}
@keyframes depPillOurs{from{opacity:0;transform:translateX(12px) scale(.94)}}
@keyframes depPillYours{from{opacity:0;transform:translateX(-12px) scale(.94)}}
.dep-stage .dep-shape{opacity:0}
.dep-stage.in .dep-shape{opacity:1;
  animation:depRise .55s var(--ease) backwards;animation-delay:calc(var(--i) * 90ms)}
.dep-stage.in .dep-seam{
  animation:depSeamDraw .5s var(--ease) backwards;animation-delay:calc(var(--i) * 90ms + 230ms)}
.dep-stage.in .lane.is-ours .tagpill{
  animation:depPillOurs .45s var(--ease) backwards;
  animation-delay:calc(var(--i) * 90ms + 350ms + var(--j) * 70ms)}
.dep-stage.in .lane.is-yours .tagpill{
  animation:depPillYours .45s var(--ease) backwards;
  animation-delay:calc(var(--i) * 90ms + 350ms + var(--j) * 70ms)}

/* ---- who runs what ---- */
.dep-h{font-size:clamp(20px,2.2vw,25px);letter-spacing:-.02em;
  margin:clamp(38px,4.5vw,56px) 0 8px}
.dep-who{display:grid;gap:12px;margin-top:20px}
.dep-row{position:relative;display:grid;grid-template-columns:132px 1fr;gap:18px;
  align-items:start;background:var(--paper);border:1px solid var(--line);
  border-radius:0 var(--radius) var(--radius) 0;overflow:hidden;padding:18px 20px 18px 23px;
  box-shadow:var(--shadow-sm);transition:transform .3s var(--ease),box-shadow .3s}
/* The edge carries the owner's colour, and it is the only rail in the section
   that is a single colour rather than the seam - because here the colour is
   carrying information rather than decorating an edge. */
.dep-row::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px}
.dep-row.is-ours::before{background:var(--red)}
.dep-row.is-yours::before{background:var(--blue)}
.dep-row:hover{transform:translateX(3px);box-shadow:var(--shadow)}
.dep-row .owner{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;padding:5px 10px;border-radius:100px;text-align:center;white-space:nowrap}
.dep-row.is-ours .owner{color:var(--red-2);background:color-mix(in srgb,var(--red) 9%,transparent);
  border:1px solid color-mix(in srgb,var(--red) 26%,transparent)}
.dep-row.is-yours .owner{color:var(--blue-ink);background:color-mix(in srgb,var(--blue) 9%,transparent);
  border:1px solid color-mix(in srgb,var(--blue) 26%,transparent)}
.dep-row h4{font-size:16px;letter-spacing:-.01em;margin:0 0 5px}
.dep-row p{margin:0;color:var(--fg-mut);font-size:14px;line-height:1.6}
.dep-who .dep-row{opacity:0}
.dep-who.in .dep-row{opacity:1;animation:depRise .5s var(--ease) backwards;
  animation-delay:calc(var(--i) * 80ms)}
@media (max-width:620px){
  .dep-row{grid-template-columns:1fr;gap:10px}
  .dep-row .owner{justify-self:start}
}

/* Motion is a layer, never the content. Everything above resolves to its
   finished state with every animation removed. */
@media (prefers-reduced-motion:reduce){
  .dep-stage .dep-shape,.dep-who .dep-row{opacity:1}
  .dep-stage.in .dep-shape,.dep-stage.in .dep-seam,.dep-stage.in .tagpill,
  .dep-who.in .dep-row{animation:none}
  .dep-shape:hover,.dep-row:hover{transform:none}
  .dep-shape:hover .tagpill{transform:none}
}

/* ---------- custom tooling: the included / charged tag ---------- */
/* Two panels on a dark section, one included and one quoted as development.
   The tag is the whole point of the pair, so it sits above the heading rather
   than after it — a reader deciding whether to keep reading is deciding on
   that word.

   --green-dk and --amber, not --green and --amber-ink: these sit on
   --dk-panel. Amber measures 8.1 there and the green 9.4. */
.tool-tag{display:inline-block;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;border-radius:100px;
  padding:5px 11px;margin-bottom:14px;border:1px solid transparent}
.tool-tag.is-in{color:var(--green-dk);
  background:color-mix(in srgb,var(--green-dk) 12%,transparent);
  border-color:color-mix(in srgb,var(--green-dk) 36%,transparent)}
.tool-tag.is-dev{color:var(--amber);
  background:color-mix(in srgb,var(--amber) 12%,transparent);
  border-color:color-mix(in srgb,var(--amber) 38%,transparent)}

/* ================= THE CARD RAIL =================
   ONE TREATMENT FOR EVERY ICON CARD, light and dark. Added 24 Sep 2026, on
   Adam looking at the dark service grid on the home page and then at the
   pricing page: "we have this type of box with a coloured line and a tilt to
   the icon with a box: then on pricing we have plain boxes, can you do same
   effects, so its consistent between light and dark".

   He was pointing at .svc, which has had this since the services grid was
   rebuilt. Everything else had some subset of it: .baseline had a rail in the
   wrong place and a broken hover colour, .deliv had the icon fill but no rail
   and no tilt, and the generic .card and the pricing .basecard had neither.
   Four families, four different amounts of the same idea.

   WHAT THE TREATMENT IS:
     a 2px rail down the LEFT edge, --seam-grad-v, wiping down on hover
     the icon holder filling with its own colour and tilting -6 degrees
     the card lifting 5px

   WHY THE LEFT EDGE AND WHY THE SEAM. The vertical seam gradient is already
   the site's rail: .svc, .vs-cell, .hubstat, .prenote, .pnote, .arttoc and the
   pricing claim cards all use it, all on the left. It was the top-rail
   families that were the exception, not this.

   WHAT IT DELIBERATELY DOES NOT TOUCH: the per-card COLOUR CYCLE on .baseline
   and .deliv. The rail is the brand mark and is identical everywhere; the icon
   colour is how a set marks itself as a set. Two devices, two jobs, and a card
   can carry both. Adding this rail is what finally lets them be told apart.

   .svc is not listed below because it already has all of it. */
.card,.basecard,.deliv{position:relative;overflow:hidden}
.card::before,.basecard::before,.baseline .card::before,.deliv::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:2px;z-index:1;
  background:var(--seam-grad-v);transform:scaleY(0);transform-origin:top;
  transition:transform .38s var(--ease)}
.card:hover::before,.basecard:hover::before,.deliv:hover::before{transform:scaleY(1)}

/* The holders animate the same way whether they are called .ico or .ic — the
   two names are historical and not worth a rename across twenty templates. */
.card .ico,.basecard .ic,.deliv .ic{
  transition:background .3s var(--ease),color .3s,border-color .3s,transform .35s var(--ease)}
.card:hover .ico,.basecard:hover .ic,.baseline .card:hover .ico,.deliv:hover .ic{
  transform:rotate(-6deg)}
/* Red is the resting colour for every family that does not cycle, so the fill
   is red for them. The cycling families set their own above, and those rules
   sit later in the file for .deliv and earlier for .baseline — both win on
   specificity, which is why neither needs repeating here. */
.card:hover .ico,.basecard:hover .ic{background:var(--red);border-color:var(--red);color:#fff}
.baseline .card:hover .ico{background:var(--red);border-color:var(--red);color:#fff}
.baseline .card:nth-child(3n+2):hover .ico{background:var(--blue);border-color:var(--blue);color:#fff}
.baseline .card:nth-child(3n):hover .ico{background:var(--green);border-color:var(--green);color:#fff}

/* ON DARK. The rail is the same gradient — it reads on --dk-panel as well as
   on paper, which is the whole point of a gradient that carries all six hues.
   The icon fills --red-3 rather than --red: --red on a dark panel is the
   .term mistake from September. */
.sec-dark .card:hover .ico,.darkband .card:hover .ico{
  background:var(--red-3);border-color:var(--red-3);color:var(--dk)}

@media (prefers-reduced-motion:reduce){
  .card::before,.basecard::before,.baseline .card::before,.deliv::before{transition:none}
  .card:hover .ico,.basecard:hover .ic,.baseline .card:hover .ico,.deliv:hover .ic{transform:none}
  .basecard:hover,.deliv:hover,.baseline .card:hover{transform:none}
}

/* ---------- the quoting tool ----------  .pcalc-*
   ${ADMIN_PATH}/pricing/, behind the studio session, rendered in the public
   layout on Adam's instruction. It borrows the site's card vocabulary rather
   than inventing an admin one, because the person using it is about to talk to
   a customer about the same packages the public pages describe.

   Two columns that become one: the form is the thing you touch and the output
   is the thing you read, and on a laptop they want to be side by side with the
   output sticky, so a number never scrolls out of view while an input is
   being changed. */
.pcalc-hero{padding-bottom:clamp(40px,5vw,64px)}
.pcalc-warn{position:relative;overflow:hidden;background:var(--paper);
  border:1px solid var(--line);border-radius:0 var(--radius) var(--radius) 0;
  padding:18px 22px;margin-bottom:clamp(22px,2.6vw,30px);box-shadow:var(--shadow-sm)}
.pcalc-warn::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--amber)}
.pcalc-warn .k{display:block;font-family:var(--mono);font-size:10.5px;font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;color:var(--amber-ink);margin-bottom:7px}
.pcalc-warn p{margin:0;color:var(--fg-soft);font-size:15px;line-height:1.6;max-width:84ch}
.pcalc-warn code{font-family:var(--mono);font-size:13px;background:var(--bg-2);
  border:1px solid var(--line);border-radius:5px;padding:1px 6px}

.pcalc{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(320px,.95fr);
  gap:clamp(18px,2.4vw,28px);align-items:start}
@media (max-width:920px){.pcalc{grid-template-columns:1fr}}

/* ---- inputs ---- */
.pcalc-form{display:grid;gap:16px;margin:0}
/* .pcf, not .pf. .pf is the home page's proof strip — .pf-badges and
   .pf-text — and a bare .pf here would have restyled it from two thousand
   lines away. That is the .rec bug, and it was caught by grepping the
   templates for the class name before this shipped rather than by anybody
   noticing the home page had changed. */
.pcf{border:1px solid var(--line);border-radius:var(--radius);background:var(--paper);
  box-shadow:var(--shadow-sm);padding:16px 18px 14px;margin:0;min-width:0}
.pcf[hidden]{display:none}
.pcf legend{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--fg-mut);padding:0 0 8px}
.pcf input[type=number],.pcf select{width:100%;font-family:var(--display);font-weight:600;
  font-size:17px;letter-spacing:-.01em;color:var(--ink);background:var(--mist);
  border:1px solid var(--line);border-radius:10px;padding:11px 13px;
  transition:border-color .2s,box-shadow .2s}
.pcf input[type=number]:focus,.pcf select:focus{outline:none;border-color:var(--red);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--red) 14%,transparent)}
.pcf-note{margin:8px 0 0;color:var(--fg-mut);font-size:12.5px;line-height:1.5}

/* The package choice is a two-up segmented control rather than a select: it is
   the decision the whole quote hangs off and it should not be hidden behind a
   dropdown. */
.segs{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.seg{position:relative;cursor:pointer}
.seg input{position:absolute;opacity:0;width:0;height:0}
.segbody{display:block;border:1px solid var(--line);border-radius:10px;
  background:var(--mist);padding:12px 14px;transition:border-color .2s,background .2s,box-shadow .2s}
.segname{display:block;font-family:var(--display);font-weight:700;font-size:16.5px;
  letter-spacing:-.015em;color:var(--ink)}
.segunit{display:block;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--fg-mut);margin-top:4px}
.seg input:checked + .segbody{border-color:var(--red);background:var(--red-wash);
  box-shadow:0 0 0 1px var(--red) inset}
.seg input:checked + .segbody .segunit{color:var(--red-2)}
.seg input:focus-visible + .segbody{outline:2px solid var(--red);outline-offset:2px}

/* ---- output ---- */
.pcalc-out{position:sticky;top:96px;background:var(--dk);color:var(--dk-fg);
  border:1px solid var(--dk-line);border-radius:var(--radius-lg);overflow:hidden;
  box-shadow:0 34px 80px -44px rgba(10,16,32,.6)}
@media (max-width:920px){.pcalc-out{position:static}}
.pcalc-out::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;
  background:var(--seam-grad)}
.po-head{padding:24px 26px 18px;border-bottom:1px solid var(--dk-line)}
.po-k{display:block;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--dk-mut);margin-bottom:10px}
.po-total{font-size:clamp(30px,4vw,44px);line-height:1;letter-spacing:-.035em;
  margin:0;color:var(--dk-fg);font-variant-numeric:tabular-nums}
.po-sub{margin:9px 0 0;color:var(--dk-mut);font-size:13px;line-height:1.5}

.po-lines{margin:0;padding:0}
.po-line{display:flex;align-items:baseline;justify-content:space-between;gap:18px;
  padding:11px 26px;border-bottom:1px solid var(--dk-line)}
.po-line[hidden]{display:none}
.po-lines .po-line:last-child,.po-apart .po-line{border-bottom:0}
.po-line dt{color:var(--dk-soft);font-size:14px;margin:0}
.po-line dt span{display:block;font-family:var(--mono);font-size:10.5px;color:var(--dk-mut);margin-top:3px}
.po-line dd{margin:0;font-family:var(--display);font-weight:600;font-size:15px;
  color:var(--dk-fg);white-space:nowrap;font-variant-numeric:tabular-nums}
.po-line.po-strong dt,.po-line.po-strong dd{font-size:16px;color:var(--dk-fg);font-weight:700}
/* Quarter and month are VIEWS of the annual figure, not prices of their own,
   so they sit indented and quieter under it. Giving them equal weight invites
   somebody to quote the monthly number as the commitment. */
.po-line.po-sub-line{padding-top:7px;padding-bottom:7px}
.po-line.po-sub-line dt{padding-left:14px;font-size:13px;color:var(--dk-mut)}
.po-line.po-sub-line dd{font-size:13.5px;font-weight:500;color:var(--dk-soft)}
.po-muted{color:var(--dk-mut) !important;font-weight:500 !important;font-size:13px !important}

/* The apart block is visually apart, and that is the point. A customer who
   sees time and materials inside an annual figure will hold us to the annual
   figure, so the two never share a total. */
.po-apart{background:color-mix(in srgb,var(--dk-fg) 4%,transparent);
  border-top:1px solid var(--dk-line);padding-top:16px}
.po-apart > .po-k{padding:0 26px}
.po-apart .po-line{padding-top:8px;padding-bottom:8px}

.po-foot{display:flex;align-items:center;gap:12px;padding:16px 26px 20px;
  border-top:1px solid var(--dk-line)}
.po-foot .btn-ghost{background:transparent;color:var(--dk-fg);border-color:var(--dk-line-2);
  box-shadow:none}
.po-foot .btn-ghost:hover{background:color-mix(in srgb,var(--dk-fg) 8%,transparent);
  border-color:var(--dk-fg)}
.po-copied{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--green-dk)}
.po-copied[hidden]{display:none}

/* ---- context cards ---- */
.pcalc-ctx{display:grid;grid-template-columns:1fr 1fr;gap:16px;
  margin-top:clamp(24px,3vw,34px)}
@media (max-width:760px){.pcalc-ctx{grid-template-columns:1fr}}
.pkg-facts{list-style:none;margin:12px 0 0;padding:0;display:grid;gap:8px}
.pkg-facts li{position:relative;padding-left:18px;color:var(--fg-soft);
  font-size:14.5px;line-height:1.55}
.pkg-facts li:empty{display:none}
.pkg-facts li::before{content:"";position:absolute;left:0;top:8px;width:7px;height:7px;
  border-radius:2px;background:var(--red)}

/* The btn-sm the footer uses exists only on the pricing page's own buttons
   elsewhere; declared here so this page does not depend on that. */
.pcalc .btn-sm,.po-foot .btn-sm{padding:9px 16px;font-size:13.5px}

/* ---------- the quoting tool: settings ----------  .cog*, and the bits the
   calculator grew when it stopped being four fields.

   THE COG OPENS A SHEET, NOT A PAGE. Editing a rate is something you do WHILE
   looking at a quote — the whole reason to change a number is that the number
   underneath it is wrong — so the quote stays behind it rather than being
   navigated away from and back to. */
.pcalc-bar{display:flex;align-items:center;justify-content:space-between;gap:16px;
  margin-bottom:clamp(16px,2vw,22px)}
.pcalc-h{font-size:clamp(20px,2.4vw,26px);letter-spacing:-.024em;margin:0}
.cog{display:inline-flex;align-items:center;gap:9px;font-family:var(--mono);
  font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--fg-mut);background:var(--paper);border:1px solid var(--line);
  border-radius:100px;padding:9px 16px 9px 13px;cursor:pointer;
  box-shadow:var(--shadow-sm);transition:color .2s,border-color .2s,transform .3s var(--ease)}
.cog svg{width:16px;height:16px;transition:transform .5s var(--ease)}
.cog:hover{color:var(--red);border-color:var(--line-2)}
.cog:hover svg{transform:rotate(60deg)}
.cog:focus-visible{outline:2px solid var(--red);outline-offset:3px}

.pcalc-saved{display:flex;align-items:center;gap:12px;background:var(--paper);
  border:1px solid var(--line);border-left:3px solid var(--green);
  border-radius:0 var(--radius) var(--radius) 0;padding:13px 20px;
  margin-bottom:18px;color:var(--fg-soft);font-size:15px;box-shadow:var(--shadow-sm)}
.pcalc-saved .k{font-family:var(--mono);font-size:10.5px;font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;color:var(--green-ink)}

.cogwrap{position:fixed;inset:0;z-index:120;display:flex;justify-content:flex-end}
.cogwrap[hidden]{display:none}
.cogveil{position:absolute;inset:0;background:rgba(9,12,20,.55);backdrop-filter:blur(2px)}
.cogpanel{position:relative;display:flex;flex-direction:column;width:min(680px,100%);
  background:var(--bg);border-left:1px solid var(--line-2);
  box-shadow:-30px 0 80px -40px rgba(10,16,32,.6)}
@media (prefers-reduced-motion:no-preference){
  .cogpanel{animation:cogIn .32s var(--ease) backwards}
  @keyframes cogIn{from{transform:translateX(24px);opacity:0}}
}
.cog-head{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;
  padding:22px 26px 18px;border-bottom:1px solid var(--line);background:var(--paper)}
.cog-head h2{font-size:21px;letter-spacing:-.02em;margin:0 0 5px}
.cog-head p{margin:0;color:var(--fg-mut);font-size:13.5px;line-height:1.5}
.cog-close{flex:none;width:36px;height:36px;display:grid;place-items:center;
  border:1px solid var(--line);border-radius:9px;background:var(--bg);color:var(--fg-mut);
  cursor:pointer;transition:color .2s,border-color .2s}
.cog-close svg{width:17px;height:17px}
.cog-close:hover{color:var(--red);border-color:var(--line-2)}

.cog-body{flex:1;overflow-y:auto;padding:4px 26px 26px}
.cogsec{padding:22px 0;border-bottom:1px solid var(--line)}
.cogsec:last-child{border-bottom:0}
.cogsec h3{font-size:16px;letter-spacing:-.015em;margin:0 0 6px}
.cog-note{margin:0 0 14px;color:var(--fg-mut);font-size:13px;line-height:1.55;max-width:70ch}
.cog-note:last-child{margin-bottom:0;margin-top:12px}

.cog-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
@media (max-width:620px){.cog-grid{grid-template-columns:1fr 1fr}}
.cogf{display:block;min-width:0}
.cogf span{display:block;font-family:var(--mono);font-size:9.5px;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--fg-mut);margin-bottom:6px}
.cogf input,.cog-region input,.cog-hrow input{width:100%;font-family:var(--body);
  font-size:15px;color:var(--ink);background:var(--paper);border:1px solid var(--line);
  border-radius:8px;padding:9px 11px;transition:border-color .2s,box-shadow .2s}
.cogf input:focus,.cog-region input:focus,.cog-hrow input:focus{outline:none;
  border-color:var(--red);box-shadow:0 0 0 3px color-mix(in srgb,var(--red) 14%,transparent)}
.cogf input::placeholder,.cog-region input::placeholder{color:var(--fg-faint, #8b93a3)}

.cog-bands{display:grid;gap:10px}
.cog-band{display:grid;grid-template-columns:1fr 1fr 34px;gap:12px;align-items:end}
.band-x{width:34px;height:37px;display:grid;place-items:center;border:1px solid var(--line);
  border-radius:8px;background:var(--paper);color:var(--fg-mut);cursor:pointer;
  transition:color .2s,border-color .2s}
.band-x svg{width:15px;height:15px}
.band-x:hover{color:var(--red);border-color:var(--line-2)}

.cog-hunts{display:grid;gap:16px;margin-top:6px}
.cog-hk{display:block;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;color:var(--red);margin-bottom:8px}
.cog-hrow{display:grid;grid-template-columns:1.2fr 1fr;gap:10px;margin-bottom:8px}

.cog-regions{display:grid;gap:8px}
.cog-rhead,.cog-region{display:grid;grid-template-columns:1.4fr .7fr 1fr .9fr;gap:10px;align-items:center}
.cog-rhead span{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-mut)}
@media (max-width:620px){
  .cog-rhead{display:none}
  .cog-region{grid-template-columns:1fr 1fr}
}

.cog-foot{display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:16px 26px;border-top:1px solid var(--line);background:var(--paper)}
.cog-foot > div{display:flex;gap:10px}
.cog-stamp{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--fg-mut)}

/* ---- calculator additions ---- */
.pcf-row{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media (max-width:560px){.pcf-row{grid-template-columns:1fr}}
.pcf .inline{display:flex;align-items:center;gap:8px}
.pcf .inline input{flex:1}
.pcf .suffix{font-family:var(--mono);font-size:13px;font-weight:700;color:var(--fg-mut)}
/* SCOPED TO .pcf, and .inline above it likewise. Both names are already taken
   in the admin templates — analyse.njk and authors.njk use .chk, content.njk
   uses .inline. The admin loads admin.css and never home.css, so nothing would
   have broken today; the scoping is because "nothing breaks today" is exactly
   what .rec had going for it until somebody added the second stylesheet. */
.pcf .chk{display:flex;align-items:center;gap:10px;cursor:pointer;padding:9px 0}
.pcf .chk input{width:18px;height:18px;accent-color:var(--red);flex:none}
.pcf .chk span{font-family:var(--display);font-weight:600;font-size:15.5px;color:var(--ink)}
.segunit{min-height:1em}
.po-fx{margin-left:auto;font-family:var(--mono);font-size:10px;letter-spacing:.06em;
  color:var(--dk-mut);text-align:right}

.hunt-list{margin:10px 0 0;padding:0}
.hunt-row{display:grid;grid-template-columns:1fr auto;gap:14px;align-items:baseline;
  padding:8px 0;border-bottom:1px solid var(--line)}
.hunt-row:last-child{border-bottom:0}
.hunt-row dt{margin:0;color:var(--fg-soft);font-size:14px;line-height:1.4}
.hunt-row dd{margin:0;font-family:var(--mono);font-size:11.5px;font-weight:700;
  letter-spacing:.04em;color:var(--red-2);text-align:right;white-space:nowrap}

/* ---- live FX, in the settings sheet ---- */
.fxbar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:14px}
.fxbar .btn-ghost svg{width:15px;height:15px}
.fxmsg{font-family:var(--mono);font-size:10.5px;letter-spacing:.04em;line-height:1.5;
  color:var(--fg-mut);flex:1;min-width:0}
.fxmsg.is-busy{color:var(--fg-mut)}
.fxmsg.is-ok{color:var(--green-ink)}
.fxmsg.is-bad{color:var(--red-2)}
/* The staleness banner is the amber one, not the red: a rate a month old is
   something to look at, not something that has gone wrong. */
.pcalc-warn.is-fx::before{background:var(--amber)}

/* ---- contract terms, full width ----
   OUT OF THE QUOTE PANEL, 24 Sep 2026 on Adam: "can you display the pricing
   over 3 years better, not in those little boxces, hard to see". Three tiles
   inside a 550px panel gave each about 170px, which is not enough for a figure
   somebody reads across a table. Full width is roughly 300px each, and the
   annual number gets set at the size it deserves.

   The saving shows in MONEY as well as per cent. The percentage is the
   discount; the money is what somebody signs for. */
.termblock{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;
  margin-top:clamp(24px,3vw,34px)}
@media (max-width:760px){.termblock{grid-template-columns:1fr}}
.termcard{position:relative;cursor:pointer;min-width:0}
.termcard input{position:absolute;opacity:0;width:0;height:0}
.tcbody{display:flex;flex-direction:column;height:100%;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:20px 22px 18px;box-shadow:var(--shadow-sm);
  transition:border-color .25s,box-shadow .25s,transform .25s var(--ease)}
.termcard:hover .tcbody{transform:translateY(-3px);box-shadow:var(--shadow);
  border-color:var(--line-2)}
.tctop{display:flex;align-items:center;justify-content:space-between;gap:10px;
  margin-bottom:14px}
.tclabel{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;color:var(--fg-mut)}
.tcoff{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.06em;
  color:var(--green-ink);background:color-mix(in srgb,var(--green) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--green) 26%,transparent);
  border-radius:100px;padding:3px 9px;white-space:nowrap}
.tcoff.is-list{color:var(--fg-mut);background:transparent;border-color:var(--line)}
.tcper{font-family:var(--display);font-weight:700;
  font-size:clamp(26px,3.4vw,36px);line-height:1;letter-spacing:-.03em;color:var(--ink);
  font-variant-numeric:tabular-nums}
.tcunit{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--fg-mut);margin-top:7px}
.tcfoot{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;margin-top:auto;
  padding-top:14px;border-top:1px solid var(--line)}
.tctotal{font-size:13.5px;color:var(--fg-soft);font-variant-numeric:tabular-nums}
.tcsave{font-family:var(--mono);font-size:11px;font-weight:700;color:var(--green-ink)}
/* The chosen term, which drives the quote above it. */
.termcard input:checked + .tcbody{border-color:var(--red);
  box-shadow:0 0 0 1px var(--red) inset, var(--shadow)}
.termcard input:checked + .tcbody .tclabel{color:var(--red)}
.termcard input:focus-visible + .tcbody{outline:2px solid var(--red);outline-offset:3px}
@media (prefers-reduced-motion:reduce){.termcard:hover .tcbody{transform:none}}

/* term rows in the settings sheet */
.cog-term{display:grid;grid-template-columns:.8fr 1.2fr .8fr;gap:12px}
@media (max-width:620px){.cog-term{grid-template-columns:1fr 1fr}}
.cog-chk{padding:0 0 10px}

/* ---- where flat band pricing prices backwards ---- */
.cliffbox{background:color-mix(in srgb,var(--amber) 9%,transparent);
  border:1px solid color-mix(in srgb,var(--amber) 30%,transparent);
  border-radius:10px;padding:14px 16px;margin:0 0 14px}
.cliffbox[hidden]{display:none}
.cliff-k{display:block;font-family:var(--mono);font-size:10px;font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;color:var(--amber-ink);margin-bottom:8px}
.cliffbox p{margin:0 0 6px;font-size:13.5px;line-height:1.55;color:var(--fg-soft)}
.cliffbox p:last-child{margin-bottom:0}
.cliff-why{color:var(--fg-mut) !important;font-size:12.5px !important}

/* ---- the aggression dial, on the quote ----
   A slider because it is a position rather than a figure — the point is to
   nudge it and watch the total move, which a number field does not invite.
   The readout carries the precision the track cannot. */
.dial{display:flex;align-items:center;gap:16px}
.dial input[type=range]{flex:1;min-width:0;height:4px;-webkit-appearance:none;appearance:none;
  background:linear-gradient(90deg,var(--line-2),var(--red));border-radius:4px;outline:none}
.dial input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
  width:22px;height:22px;border-radius:50%;background:var(--paper);
  border:2px solid var(--red);box-shadow:var(--shadow-sm);cursor:pointer;
  transition:transform .15s var(--ease)}
.dial input[type=range]::-webkit-slider-thumb:hover{transform:scale(1.12)}
.dial input[type=range]::-moz-range-thumb{width:20px;height:20px;border-radius:50%;
  background:var(--paper);border:2px solid var(--red);box-shadow:var(--shadow-sm);cursor:pointer}
.dial input[type=range]:focus-visible{outline:2px solid var(--red);outline-offset:6px}
.dialout{flex:none;min-width:76px;text-align:right;font-family:var(--display);
  font-weight:700;font-size:20px;letter-spacing:-.02em;color:var(--ink);
  font-variant-numeric:tabular-nums}
.dialout.is-under{color:var(--green-ink)}
.dialout.is-over{color:var(--red-2)}
