/* Illustration LAYOUT and animation CSS, split out of the inline <style>.
   Parsing it before first paint spent LCP budget on pictures nobody had
   scrolled to yet, so it is loaded non-render-blocking; see the <link> in
   index.html. The first of these modules is in the page's third section
   (#how), not halfway down, so "deferred" here buys a head start measured in
   one screenful — treat the budget accordingly.

   WHAT DOES NOT BELONG IN THIS FILE: the rules that give an inline SVG its
   width, its height or its fill and stroke. Those now live in the critical
   <style> in index.html, under "illustration base". They were here, and the
   result was that every diagram on the page spent the fetch rendering as a
   black shape in a default 300x150 box and then resized the document when
   this file finally landed. A deferred stylesheet can own how a picture
   moves; it cannot own whether the picture is visible. If you add a module,
   put its geometry and its paint up there and only its layout and keyframes
   here. */
/* Each module is its own containment context so the container queries
   below resolve against the panel column, not the window. */
.gxa-wrap,.gxb-wrap,.gxc-oh,.gxe-cst,.gxf-devnotes,.gxg-gap{
  container-type:inline-size;
}



/* ===== How it learns your site — pipeline diagram ===== */
.gxa-wrap{--gxa-lab:34px;box-sizing:border-box;width:100%;max-width:100%;overflow:hidden;padding:clamp(12px,2vw,20px);border:1px solid var(--line);border-radius:14px;background:linear-gradient(180deg,var(--paper) 0%,var(--bg) 100%);box-shadow:0 2px 6px rgba(11,34,57,.05),0 12px 28px rgba(11,34,57,.07);}
.gxa-wrap *{box-sizing:border-box;}
.gxa-flow{display:flex;align-items:stretch;justify-content:center;}
.gxa-stage{flex:1 1 0;min-width:0;display:flex;flex-direction:column;}
.gxa-card{flex:0 0 auto;padding:clamp(6px,1vw,12px);border:1px solid var(--line);border-radius:12px;background:var(--paper);}
.gxa-lab{flex:0 0 var(--gxa-lab);display:flex;align-items:flex-start;justify-content:center;margin:0;padding:9px 2px 0;font-size:.72rem;line-height:1.15;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--cyan-ink);text-align:center;}

/* connectors */
/* --gxa-run is this connector's own width, restated as a length so the packet
   dot can travel it with translateX instead of `left`. It must track the
   flex-basis on the same line; they are one measurement written twice because
   a transform cannot read its parent's used width. */
.gxa-link{position:relative;--gxa-run:clamp(16px,4vw,58px);flex:0 0 var(--gxa-run);min-width:16px;}
.gxa-link::before{content:"";position:absolute;left:1px;right:1px;top:calc((100% - var(--gxa-lab))/2);border-top:1.5px dashed var(--line-2);}
.gxa-link::after{content:"";position:absolute;right:0;top:calc((100% - var(--gxa-lab))/2 - 4.5px);width:0;height:0;border-top:4.5px solid transparent;border-bottom:4.5px solid transparent;border-left:6px solid var(--line-2);}
/* --gxa-rot exists because a keyframe that sets `transform` replaces the base
   transform outright. The reverse packet is a DIAMOND — a square turned 45deg —
   and the moment its travel moved into `transform` it silently squared off.
   Measured: bounding box 12.7px (rotated) became 9px (not). Carrying the
   rotation as a variable means both keyframes can restate it without either of
   them knowing which shape they are moving. */
.gxa-pk{position:absolute;--gxa-rot:0deg;left:-4.5px;top:calc((100% - var(--gxa-lab))/2 - 4.5px);width:9px;height:9px;border-radius:50%;background:var(--brand);opacity:0;transform:rotate(var(--gxa-rot));}
.gxa-rev{--gxa-rot:45deg;border-radius:2px;background:var(--brand-deep);}

/* The line-art, passage, index and answer paint for this module is in the
   critical <style> in index.html — see the file header. */

/* wrap to two rows on small screens: row 1 -> return sweep -> row 2 */
@media (max-width:720px){
  .gxa-flow{flex-wrap:wrap;}
  .gxa-stage{flex:1 1 38%;}
  .gxa-link{--gxa-run:clamp(14px,5vw,40px);flex:0 0 var(--gxa-run);}
  /* The return sweep spans the whole module. 100cqi is exactly .gxa-flow's
     width here: .gxa-wrap is the inline-size container, so cqi is its content
     box, which is what this row fills. */
  .gxa-l2{--gxa-run:100cqi;flex:0 0 100%;height:34px;}
  .gxa-l2::before{top:calc(50% - 1px);}
  .gxa-l2::after{right:auto;left:0;top:calc(50% - 4.5px);border-left:0;border-right:6px solid var(--line-2);}
  .gxa-l2 .gxa-pk{top:calc(50% - 4.5px);}
}

@keyframes gxa-scan-k{0%,2%{opacity:0;transform:translateY(-46px)}5%{opacity:1}15%{opacity:1}18%,100%{opacity:0;transform:translateY(178px)}}
@keyframes gxa-fade-a{0%{opacity:1}3%,7%{opacity:.25}15%,100%{opacity:1}}
@keyframes gxa-fade-b{0%{opacity:1}6%,24%{opacity:.24}32%,100%{opacity:1}}
@keyframes gxa-fade-c{0%{opacity:1}7%,40%{opacity:.16}48%,100%{opacity:1}}
@keyframes gxa-fade-d{0%{opacity:1}8%,47%{opacity:.24}55%,100%{opacity:1}}
@keyframes gxa-fade-e{0%{opacity:1}9%,65%{opacity:0}74%,100%{opacity:1}}
@keyframes gxa-fade-f{0%{opacity:1}9%,78%{opacity:.26}86%,100%{opacity:1}}
@keyframes gxa-draw-a{0%{stroke-dashoffset:0}9%,63%{stroke-dashoffset:130}72%,100%{stroke-dashoffset:0}}
@keyframes gxa-draw-b{0%{stroke-dashoffset:0}10%,80%{stroke-dashoffset:100}89%,100%{stroke-dashoffset:0}}
@keyframes gxa-pop-a{0%{opacity:1;transform:scale(1)}9%,63%{opacity:0;transform:scale(.6)}72%{opacity:1;transform:scale(1.05)}77%,100%{opacity:1;transform:scale(1)}}
@keyframes gxa-pop-b{0%{opacity:1;transform:scale(1)}10%,88%{opacity:0;transform:scale(.92)}94%{opacity:1;transform:scale(1.04)}97%,100%{opacity:1;transform:scale(1)}}
@keyframes gxa-pop-c{0%{opacity:1;transform:scale(1)}11%,92%{opacity:0;transform:scale(.7)}97%{opacity:1;transform:scale(1.06)}99%,100%{opacity:1;transform:scale(1)}}
/* `left` -> translateX. The dot used to run from left:-4.5px to
   calc(100% - 4.5px), i.e. exactly one connector width, so the travel is
   --gxa-run and the -4.5px centring offset stays on the base rule where it
   belongs rather than being re-stated in both keyframes. */
@keyframes gxa-fx{0%{transform:translateX(0) rotate(var(--gxa-rot));opacity:0}1.5%,8.5%{opacity:1}10%,100%{transform:translateX(var(--gxa-run)) rotate(var(--gxa-rot));opacity:0}}
@keyframes gxa-fx-r{0%{transform:translateX(var(--gxa-run)) rotate(var(--gxa-rot));opacity:0}1.5%,8.5%{opacity:1}10%,100%{transform:translateX(0) rotate(var(--gxa-rot));opacity:0}}
html.mo .gxa-scan{animation:gxa-scan-k 9s linear infinite;}
html.mo .gxa-crawl,html.mo .gxa-docs{animation:gxa-fade-a 9s var(--e-out) infinite;}
html.mo .gxa-docs{animation-delay:.18s;}
html.mo .gxa-cuts,html.mo .gxa-pbars{animation:gxa-fade-b 9s var(--e-out) infinite;}
html.mo .gxa-chips rect{animation:gxa-fade-b 9s var(--e-out) infinite;}
html.mo .gxa-chips rect:nth-child(2){animation-delay:.08s;}
html.mo .gxa-chips rect:nth-child(3){animation-delay:.16s;}
html.mo .gxa-chips rect:nth-child(4){animation-delay:.24s;}
html.mo .gxa-chips rect:nth-child(5){animation-delay:.32s;}
html.mo .gxa-grid>g{animation:gxa-fade-c 9s var(--e-out) infinite;}
html.mo .gxa-grid>g:nth-child(2){animation-delay:.07s;}
html.mo .gxa-grid>g:nth-child(3){animation-delay:.14s;}
html.mo .gxa-grid>g:nth-child(4){animation-delay:.21s;}
html.mo .gxa-grid>g:nth-child(5){animation-delay:.28s;}
html.mo .gxa-grid>g:nth-child(6){animation-delay:.35s;}
html.mo .gxa-q{animation:gxa-fade-d 9s var(--e-out) infinite;}
html.mo .gxa-pull{animation:gxa-draw-a 9s var(--e-out) infinite;}
html.mo .gxa-pull+.gxa-pull{animation-delay:.12s;}
html.mo .gxa-hit{animation:gxa-pop-a 9s var(--e-out) infinite;}
html.mo .gxa-h2{animation-delay:.14s;}
html.mo .gxa-mchips{animation:gxa-fade-e 9s var(--e-out) infinite;}
html.mo .gxa-ans{animation:gxa-fade-f 9s var(--e-out) infinite;}
html.mo .gxa-write{animation:gxa-draw-b 9s var(--e-out) infinite;}
html.mo .gxa-lines path:nth-child(2){animation-delay:.12s;}
html.mo .gxa-lines path:nth-child(3){animation-delay:.24s;}
html.mo .gxa-cites{animation:gxa-pop-b 9s var(--e-out) infinite;}
html.mo .gxa-ck{animation:gxa-pop-c 9s var(--e-out) infinite;}
html.mo .gxa-pk{animation-duration:9s;animation-timing-function:linear;animation-iteration-count:infinite;}
html.mo .gxa-fwd{animation-name:gxa-fx;}
html.mo .gxa-rev{animation-name:gxa-fx-r;}
html.mo .gxa-l1 .gxa-fwd{animation-delay:1.35s;}
html.mo .gxa-l2 .gxa-fwd{animation-delay:2.9s;}
html.mo .gxa-l3 .gxa-rev{animation-delay:4.9s;}
html.mo .gxa-l3 .gxa-fwd{animation-delay:7s;}
@media (max-width:720px){
html.mo .gxa-l2 .gxa-fwd{animation-name:gxa-fx-r;}
}

.gxb-wrap{--gxb-dur:7.2s;--gxb-ease:var(--e-out);width:100%}
.gxb-wrap,.gxb-wrap *{box-sizing:border-box}
.gxb-grid{display:grid;grid-template-columns:1fr;gap:14px}
/* THE ANSWERED PANEL CARRIES THE ARGUMENT, so it carries the colour. It and
   .gxb-panel--no were #FFFFFF and #EDF1F8: two greys four percent apart, on a
   pale grey band. The diagram exists to compare two outcomes and the
   comparison was invisible.
   Brand tint and a cyan edge, which is the language the lit navy bands and the
   source chips already speak. Paint only: no layout property here, so nothing
   can reflow, and it is unchanged under html.rm.
   #0B1524 on #F2F6FF is 16.4:1 and #4E5773 is 7.1:1. */
.gxb-panel{min-width:0;display:flex;flex-direction:column;gap:8px;padding:12px 14px 14px;background:linear-gradient(168deg,#F7FAFF 0%,#EEF4FF 100%);border:1px solid rgba(0,71,255,.22);border-radius:14px;box-shadow:0 2px 6px rgba(11,34,57,.05),0 12px 28px rgba(4,24,74,.10),inset 0 1px 0 rgba(255,255,255,.9)}
/* And the other side gets FLATTER, not louder. A comparison is the distance
   between two things; accenting both closes it again. No inner highlight, no
   coloured edge, a cooler ground. #4E5773 on #E9EDF4 is 6.6:1. */
.gxb-panel--no{background:#E9EDF4;border-color:var(--line-2);box-shadow:inset 0 1px 0 rgba(255,255,255,.5)}
.gxb-cap{display:flex;align-items:center;gap:8px;margin:0;font-size:.72rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--cyan-ink)}
.gxb-cap::before{content:"";flex:none;width:10px;height:10px;border-radius:50%;background:var(--brand)}
.gxb-cap--no{color:var(--mut)}
.gxb-cap--no::before{background:transparent;border:1.5px solid var(--mut);border-radius:2px;transform:rotate(45deg)}
.gxb-art{width:100%}
.gxb-foot{display:flex;justify-content:flex-end;min-height:24px}
.gxb-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;padding:5px 10px;border-radius:8px;font-size:.72rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;line-height:1.1}
.gxb-chip--src{background:var(--tint);border:1px solid var(--cyan);color:var(--brand-deep)}
.gxb-chip--no{background:var(--paper);border:1px dashed var(--line-2);color:var(--mut)}
/* Both SVG boxes and every fill and stroke in this module are in the critical
   <style> in index.html. Only the transform origins the keyframes below need
   are still here. */
.gxb-hl,.gxb-cite,.gxb-ok,.gxb-no,.gxb-n1,.gxb-n2,.gxb-n3,.gxb-ans,.gxb-ans2,.gxb-fray{transform-box:fill-box;transform-origin:50% 50%}
.gxb-fray{transform-origin:100% 0}
@media (min-width:660px){.gxb-grid{grid-template-columns:1fr 1fr;gap:18px}}
html.mo .gxb-hl{animation:gxb-k-hl var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-thread{animation:gxb-k-draw var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-n1{animation:gxb-k-pop var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-n2,html.mo .gxb-n3{animation:gxb-k-pop2 var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-ans{animation:gxb-k-rise var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-cite{animation:gxb-k-cite var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-ok{animation:gxb-k-stamp var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-chip--src{animation:gxb-k-chip var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-slot{animation:gxb-k-slot var(--gxb-dur) ease-in-out infinite}
html.mo .gxb-ans2{animation:gxb-k-rise2 var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-thread-no{animation:gxb-k-draw2 var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-fray{animation:gxb-k-fray var(--gxb-dur) ease-in-out infinite}
html.mo .gxb-strike{animation:gxb-k-strike var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-no{animation:gxb-k-stamp2 var(--gxb-dur) var(--gxb-ease) infinite}
html.mo .gxb-chip--no{animation:gxb-k-chip2 var(--gxb-dur) var(--gxb-ease) infinite}
@keyframes gxb-k-hl{0%,3%{opacity:0;transform:scale(.97)}12%,90%{opacity:1;transform:scale(1)}96%,100%{opacity:0;transform:scale(.97)}}
@keyframes gxb-k-draw{0%,10%{stroke-dashoffset:78;opacity:0}14%{opacity:1}32%,90%{stroke-dashoffset:0;opacity:1}96%,100%{stroke-dashoffset:78;opacity:0}}
@keyframes gxb-k-pop{0%,8%{opacity:0;transform:scale(.82)}16%,90%{opacity:1;transform:scale(1)}96%,100%{opacity:0;transform:scale(.82)}}
@keyframes gxb-k-pop2{0%,28%{opacity:0;transform:scale(.82)}36%,90%{opacity:1;transform:scale(1)}96%,100%{opacity:0;transform:scale(.82)}}
@keyframes gxb-k-rise{0%,22%{opacity:0;transform:translateY(9px)}36%,90%{opacity:1;transform:translateY(0)}96%,100%{opacity:0;transform:translateY(9px)}}
@keyframes gxb-k-cite{0%,42%{opacity:0;transform:scale(.84)}52%,90%{opacity:1;transform:scale(1)}96%,100%{opacity:0;transform:scale(.84)}}
@keyframes gxb-k-stamp{0%,52%{opacity:0;transform:scale(.84)}62%,90%{opacity:1;transform:scale(1)}96%,100%{opacity:0;transform:scale(.84)}}
@keyframes gxb-k-chip{0%,58%{opacity:0;transform:translateY(7px)}68%,90%{opacity:1;transform:translateY(0)}96%,100%{opacity:0;transform:translateY(7px)}}
@keyframes gxb-k-slot{0%,100%{opacity:.7}50%{opacity:1}}
@keyframes gxb-k-rise2{0%,16%{opacity:0;transform:translateY(9px)}30%,56%{opacity:1;transform:translateY(0)}70%,90%{opacity:.68;transform:translateY(0)}96%,100%{opacity:0;transform:translateY(9px)}}
@keyframes gxb-k-draw2{0%,28%{stroke-dashoffset:40;opacity:0}32%{opacity:.75}44%,90%{stroke-dashoffset:0;opacity:.75}96%,100%{stroke-dashoffset:40;opacity:0}}
@keyframes gxb-k-fray{0%,44%{opacity:0;transform:rotate(0deg)}54%{opacity:.65;transform:rotate(0deg)}68%{opacity:.45;transform:rotate(3deg)}80%{opacity:.65;transform:rotate(-2deg)}90%{opacity:.55;transform:rotate(0deg)}96%,100%{opacity:0;transform:rotate(0deg)}}
@keyframes gxb-k-strike{0%,54%{stroke-dashoffset:148;opacity:0}58%{opacity:.5}70%,90%{stroke-dashoffset:0;opacity:.5}96%,100%{stroke-dashoffset:148;opacity:0}}
@keyframes gxb-k-stamp2{0%,60%{opacity:0;transform:scale(.84)}70%,90%{opacity:1;transform:scale(1)}96%,100%{opacity:0;transform:scale(.84)}}
@keyframes gxb-k-chip2{0%,68%{opacity:0;transform:translateY(7px)}78%,90%{opacity:1;transform:translateY(0)}96%,100%{opacity:0;transform:translateY(7px)}}

.gxc-oh{--gxc-oh-loop:9s;--gxc-oh-gap:clamp(16px,2.4vw,28px);width:100%;max-width:100%;padding:clamp(14px,2vw,22px);border:1px solid var(--line);border-radius:14px;background:var(--paper);box-shadow:0 2px 6px rgba(11,34,57,.05),0 12px 28px rgba(11,34,57,.07);color:var(--ink)}
.gxc-oh,.gxc-oh *{box-sizing:border-box}
.gxc-oh i{font-style:normal}
.gxc-oh__grid{display:grid;grid-template-columns:1.1fr .95fr 1.05fr;gap:var(--gxc-oh-gap);align-items:center}
.gxc-oh__zone{display:flex;flex-direction:column;gap:12px;min-width:0}
.gxc-oh__zone+.gxc-oh__zone{border-left:1px solid var(--line);padding-left:clamp(12px,1.6vw,20px)}
.gxc-oh__zone--day{align-items:center;text-align:center}
.gxc-oh__key{display:flex;flex-wrap:wrap;justify-content:center;gap:4px 18px}
.gxc-oh__keyitem{display:inline-flex;align-items:center;gap:6px}
.gxc-oh__micro{margin:0;font-size:.72rem;line-height:1.25;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--cyan-ink)}
.gxc-oh__micro--late{color:var(--ink)}
.gxc-oh__now{transform-box:view-box;transform-origin:150px 150px;transform:rotate(165deg)}
.gxc-oh__pcard{display:flex;align-items:center;gap:11px;padding:12px 13px;border:1px solid var(--line);border-radius:8px;background:var(--paper)}
.gxc-oh__pcard--ghost{background:var(--bg)}
.gxc-oh__pcard .gxc-oh__micro{flex:1 1 auto;min-width:0}
.gxc-oh__skel{display:flex;flex:1 1 auto;flex-direction:column;gap:5px;min-width:0}
.gxc-oh__skel i{display:block;height:6px;border-radius:3px;background:var(--line-2)}
.gxc-oh__skel i:first-child{width:64%}
.gxc-oh__skel i:last-child{width:38%}
.gxc-oh__sw{position:relative;flex:0 0 auto;width:44px;height:24px;border:1px solid var(--brand-deep);border-radius:12px;background:var(--brand-deep)}
.gxc-oh__sw i{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:var(--paper);transform:translateX(20px)}
.gxc-oh__sw--off{background:var(--bg);border-color:var(--line-2)}
.gxc-oh__sw--off i{background:var(--mut);transform:none}
.gxc-oh__phone{transform-box:fill-box;transform-origin:center}
.gxc-oh__q{display:flex;flex-direction:column;gap:8px;width:100%;max-width:280px}
/* THE ROW HAS TO BE ALLOWED TO WRAP, and this is a contrast fix, not a
   layout preference. Measured at 1024 with a pixel-sampling audit that reads
   the real painted ground: the three-column .gxc-oh__grid gives this zone
   107px, .gxc-oh__q is 89px, the bar is already at its 34px min-width — and
   the 82px .gxc-oh__flag is `flex:0 0 auto`, so it cannot shrink and simply
   hangs 36px past the white card, putting "Overdue" in --ink on the NAVY BAND
   at 1.09:1. At 1280 it still hangs over by 7px. Nothing about the type change
   caused it: the overflow is byte-identical at font-weight 600 and 700.
   Wrapping drops the flag under its own bar when the zone is too narrow and
   costs nothing when it is not. */
.gxc-oh__qrow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.gxc-oh__dot{flex:0 0 auto;width:14px;height:14px;border-radius:50%;background:var(--tint);border:1.5px solid var(--line-2)}
.gxc-oh__bar{position:relative;flex:1 1 auto;min-width:34px;height:8px;border-radius:4px;background:var(--tint);overflow:hidden}
.gxc-oh__bar i{position:absolute;inset:0;border-radius:4px;background:var(--brand);transform-origin:left center;transform:scaleX(.45)}
.gxc-oh__qrow--late .gxc-oh__bar i{background:var(--ink);transform:scaleX(1)}
.gxc-oh__flag{display:inline-flex;align-items:center;gap:5px;flex:0 1 auto;min-width:0}
@media (max-width:900px){
.gxc-oh__grid{grid-template-columns:1fr;gap:18px}
.gxc-oh__zone+.gxc-oh__zone{border-left:0;padding-left:0;border-top:1px solid var(--line);padding-top:18px}
}
html.mo .gxc-oh__now{animation:gxc-oh-sweep var(--gxc-oh-loop) linear infinite}
html.mo .gxc-oh__hub-on,html.mo .gxc-oh__pres-on{animation:gxc-oh-duty var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__hub-off,html.mo .gxc-oh__pres-off{animation:gxc-oh-duty-off var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__badge--on{animation:gxc-oh-dim-on var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__badge--off{animation:gxc-oh-dim-off var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__sw--live{animation:gxc-oh-track var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__sw--live i{animation:gxc-oh-knob var(--gxc-oh-loop) var(--e-out) infinite}
html.mo .gxc-oh__strike{animation:gxc-oh-strike var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__fill{animation:gxc-oh-fill var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__over{animation:gxc-oh-over var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__phone{animation:gxc-oh-buzz var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__waves{animation:gxc-oh-waves var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__flag{animation:gxc-oh-flag var(--gxc-oh-loop) var(--e-out) infinite}
html.mo .gxc-oh__qrow .gxc-oh__bar i{animation:gxc-oh-bar-ok var(--gxc-oh-loop) ease-in-out infinite}
html.mo .gxc-oh__qrow--late .gxc-oh__bar i{animation:gxc-oh-bar-late var(--gxc-oh-loop) ease-in-out infinite}
@keyframes gxc-oh-sweep{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
@keyframes gxc-oh-duty{0%,35%{opacity:0}39%,78%{opacity:1}82%,100%{opacity:0}}
@keyframes gxc-oh-duty-off{0%,35%{opacity:1}39%,78%{opacity:0}82%,100%{opacity:1}}
@keyframes gxc-oh-dim-on{0%,35%{opacity:.38}39%,78%{opacity:1}82%,100%{opacity:.38}}
@keyframes gxc-oh-dim-off{0%,35%{opacity:1}39%,78%{opacity:.38}82%,100%{opacity:1}}
@keyframes gxc-oh-track{0%,35%{background-color:var(--bg);border-color:var(--line-2)}40%,78%{background-color:var(--brand-deep);border-color:var(--brand-deep)}82%,100%{background-color:var(--bg);border-color:var(--line-2)}}
@keyframes gxc-oh-knob{0%,35%{transform:translateX(0);background-color:var(--mut)}40%,78%{transform:translateX(20px);background-color:var(--paper)}82%,100%{transform:translateX(0);background-color:var(--mut)}}
@keyframes gxc-oh-strike{0%{stroke-dashoffset:39;opacity:0}7%{opacity:1}22%,90%{stroke-dashoffset:0;opacity:1}97%,100%{stroke-dashoffset:0;opacity:0}}
@keyframes gxc-oh-fill{0%,6%{stroke-dashoffset:207.35}48%{stroke-dashoffset:82.94}62%,88%{stroke-dashoffset:58.06}97%,100%{stroke-dashoffset:207.35}}
@keyframes gxc-oh-over{0%,50%{stroke-dashoffset:82.94}62%,88%{stroke-dashoffset:58.06}94%,100%{stroke-dashoffset:82.94}}
@keyframes gxc-oh-waves{0%,60%{opacity:0}66%,82%{opacity:1}90%,100%{opacity:0}}
@keyframes gxc-oh-buzz{0%,62%{transform:translateX(0)}64%{transform:translateX(-1.4px)}66%{transform:translateX(1.4px)}68%{transform:translateX(-1px)}70%{transform:translateX(.8px)}72%,100%{transform:translateX(0)}}
@keyframes gxc-oh-flag{0%,56%{opacity:0;transform:translateY(4px)}64%,88%{opacity:1;transform:translateY(0)}96%,100%{opacity:0;transform:translateY(4px)}}
@keyframes gxc-oh-bar-ok{0%,8%{transform:scaleX(0)}46%,100%{transform:scaleX(.45)}}
@keyframes gxc-oh-bar-late{0%,8%{transform:scaleX(0)}62%,88%{transform:scaleX(1)}97%,100%{transform:scaleX(0)}}

/* THE gxd FIVE-STATION FLOW IS GONE, markup and all. It drew, in 24
   @keyframes of which 22 were infinite, the sentence the three-item
   .crm-steps row above now says in three lines — beside a screenshot that
   already photographs the same board. It was the largest single animation
   block on the page. Stage 7, spec 1: "demote the gxd flow to a
   three-item micro-benefit row." */

@property --gxe-na{syntax:"<integer>";inherits:true;initial-value:436}
@property --gxe-nb{syntax:"<integer>";inherits:true;initial-value:299}
@property --gxe-nc{syntax:"<integer>";inherits:true;initial-value:232}

.gxe-cst{box-sizing:border-box;width:100%;max-width:100%;overflow:hidden;padding:clamp(16px,2.4vw,27px);background:var(--paper);border:1px solid var(--line);border-radius:14px;box-shadow:0 2px 6px rgba(11,34,57,.05),0 12px 28px rgba(11,34,57,.07);color:var(--ink);display:flex;flex-direction:column;gap:clamp(12px,1.9vw,21px);min-height:clamp(250px,28.5vw,330px)}
.gxe-cst *{box-sizing:border-box;min-width:0}

.gxe-cst__lane{display:grid;grid-template-columns:14px minmax(0,1fr) minmax(0,1.06fr) clamp(42px,5vw,58px);align-items:center;column-gap:clamp(8px,1.2vw,14px)}

.gxe-cst__mic{font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--cyan-ink);line-height:1.15;white-space:nowrap}

.gxe-cst__plan .gxe-cst__mic{grid-column:2;justify-self:end}
.gxe-cst__planbar{grid-column:3/5;position:relative;height:clamp(28px,3.2vw,37px);border:1px solid var(--line);border-radius:8px;background:var(--paper);display:flex;align-items:center;justify-content:flex-end;padding:0 clamp(9px,1vw,13px)}
.gxe-cst__planbar::before,.gxe-cst__planbar::after{content:"";position:absolute;top:calc(100% + 1px);width:1.5px;height:11px;border-radius:1px;background:var(--line-2)}
.gxe-cst__planbar::before{left:-1px}
.gxe-cst__planbar::after{right:-1px}
.gxe-cst__planfill{position:absolute;left:0;top:0;bottom:0;width:100%;border-radius:7px;background:linear-gradient(90deg,var(--tint),var(--bg));transform-origin:left center}
.gxe-cst__plannum{position:relative;font-size:clamp(.78rem,1.05vw,.9rem);font-weight:800;color:var(--navy);letter-spacing:.02em;font-variant-numeric:tabular-nums;line-height:1}

.gxe-cst__legend .gxe-cst__mic:first-child{grid-column:2}
.gxe-cst__legend .gxe-cst__mic:last-child{grid-column:3/5;justify-self:end}

.gxe-cst__rows{display:flex;flex-direction:column;justify-content:space-between;gap:clamp(14px,2.4vw,28px);flex:1 1 auto}


.gxe-cst__cost{grid-column:2;display:grid;grid-template-columns:minmax(0,1fr) clamp(32px,3.6vw,44px);align-items:center;column-gap:clamp(6px,.9vw,10px)}
.gxe-cst__rail{position:relative;display:block;height:clamp(22px,3.1vw,36px);border-radius:8px;background:var(--tint)}
.gxe-cst__bar{position:absolute;left:0;top:0;bottom:0;width:var(--gxe-w);border-radius:8px;background:linear-gradient(90deg,var(--brand),var(--brand-deep));transform-origin:left center}
.gxe-cst__val{font-size:clamp(.8rem,1.1vw,.95rem);font-weight:800;color:var(--ink);text-align:right;line-height:1;font-variant-numeric:tabular-nums}

.gxe-cst__slices{grid-column:3;position:relative;height:clamp(22px,3.1vw,36px);border:1px solid var(--line);border-radius:8px;background:var(--tint);overflow:hidden}
.gxe-cst__tiles{position:absolute;inset:0;opacity:.72;background-image:linear-gradient(90deg,var(--cyan) 0 80%,transparent 80%);background-size:var(--gxe-p) 100%;background-repeat:repeat-x}

.gxe-cst__count{grid-column:4;text-align:right;line-height:1}
/* THESE THREE ARE THE PAGE'S ONLY INFINITE ANIMATIONS THAT TOUCH LAYOUT.
   gxe-cst-na/nb/nc drive a registered integer through counter() in `content`,
   so every step retypes the box -- and 0 is one glyph where 436 is three, so
   the box was also changing width 7s a loop for as long as the section stayed
   on screen. The .vis play-state gate below stops them off-screen; it cannot
   help while they are visible, which is most of the time somebody is reading
   this section.
   min-width reserves the widest value up front (tabular-nums makes every digit
   the same advance, so 3.2ch clears 436) and contain:layout makes the box its
   own layout root, so the retype cannot reach anything outside it. The count
   still counts; it just stops moving the page while it does.
   NOT `contain:style` -- that scopes counters, which is the one thing this
   element depends on.
   The CI check greps @keyframes NAMES, so it never saw these: the names are in
   below-fold.css and what it reads is index.html. Worth pointing at the
   `animation:` declarations instead. */
.gxe-cst__n{display:inline-block;min-width:3.2ch;text-align:right;contain:layout;
  font-size:clamp(1rem,1.5vw,1.3rem);font-weight:800;color:var(--brand-deep);letter-spacing:-.01em;font-variant-numeric:tabular-nums}
.gxe-cst__row--a .gxe-cst__n::after{counter-reset:gxe-ca var(--gxe-na,436);content:counter(gxe-ca)}
.gxe-cst__row--b .gxe-cst__n::after{counter-reset:gxe-cb var(--gxe-nb,299);content:counter(gxe-cb)}
.gxe-cst__row--c .gxe-cst__n::after{counter-reset:gxe-cc var(--gxe-nc,232);content:counter(gxe-cc)}

.gxe-cst__row--a{--gxe-w:51.2%;--gxe-p:4.8%}
.gxe-cst__row--b{--gxe-w:74.68%;--gxe-p:7%}
.gxe-cst__row--c{--gxe-w:96%;--gxe-p:9%}

@media (max-width:620px){
  .gxe-cst__lane{grid-template-columns:14px minmax(0,1fr) clamp(44px,13vw,56px);column-gap:9px;row-gap:8px}
  .gxe-cst__plan .gxe-cst__mic{grid-column:2;grid-row:1;justify-self:start}
  .gxe-cst__planbar{grid-column:2/4;grid-row:2}
  .gxe-cst__legend .gxe-cst__mic:last-child{grid-column:2/4;justify-self:end}
  .gxe-cst__cost{grid-column:2;grid-row:1}
  .gxe-cst__count{grid-column:3;grid-row:1}
  .gxe-cst__slices{grid-column:2/4;grid-row:2}
  .gxe-cst__row--a{--gxe-p:7.2%}
  .gxe-cst__row--b{--gxe-p:10.5%}
  .gxe-cst__row--c{--gxe-p:13.5%}
}

/* width -> scaleX, here and in gxe-cst-plan below. Both bars are gradients
   sized to their own width, so a width animation compresses the gradient and
   stretches it out as the bar grows; scaleX does exactly that on the
   compositor instead of in layout. The one visible difference is that the 8px
   corner radius is squashed horizontally while the bar is short, which at
   these sizes reads as the same sliver. */
@keyframes gxe-cst-grow{0%{transform:scaleX(0)}34%,88%{transform:scaleX(1)}100%{transform:scaleX(0)}}
@keyframes gxe-cst-tile{0%{clip-path:inset(0 100% 0 0)}34%{clip-path:inset(0 0 0 0)}88%{clip-path:inset(0 0 0 0)}100%{clip-path:inset(0 100% 0 0)}}
@keyframes gxe-cst-plan{0%{transform:scaleX(0)}30%,90%{transform:scaleX(1)}100%{transform:scaleX(0)}}
@keyframes gxe-cst-na{0%{--gxe-na:0;opacity:0}7%{opacity:1}34%{--gxe-na:436}88%{--gxe-na:436;opacity:1}96%{--gxe-na:436;opacity:0}100%{--gxe-na:0;opacity:0}}
@keyframes gxe-cst-nb{0%{--gxe-nb:0;opacity:0}7%{opacity:1}34%{--gxe-nb:299}88%{--gxe-nb:299;opacity:1}96%{--gxe-nb:299;opacity:0}100%{--gxe-nb:0;opacity:0}}
@keyframes gxe-cst-nc{0%{--gxe-nc:0;opacity:0}7%{opacity:1}34%{--gxe-nc:232}88%{--gxe-nc:232;opacity:1}96%{--gxe-nc:232;opacity:0}100%{--gxe-nc:0;opacity:0}}

html.mo .gxe-cst__bar{animation:gxe-cst-grow 7s var(--e-out) infinite both}
html.mo .gxe-cst__tiles{animation:gxe-cst-tile 7s var(--e-out) infinite both}
html.mo .gxe-cst__planfill{animation:gxe-cst-plan 7s var(--e-out) infinite both}
html.mo .gxe-cst__row--a .gxe-cst__n{animation:gxe-cst-na 7s var(--e-out) infinite both}
html.mo .gxe-cst__row--b .gxe-cst__n{animation:gxe-cst-nb 7s var(--e-out) infinite both}
html.mo .gxe-cst__row--c .gxe-cst__n{animation:gxe-cst-nc 7s var(--e-out) infinite both}
html.mo .gxe-cst__row--b .gxe-cst__bar,html.mo .gxe-cst__row--b .gxe-cst__tiles,html.mo .gxe-cst__row--b .gxe-cst__n{animation-delay:.16s}
html.mo .gxe-cst__row--c .gxe-cst__bar,html.mo .gxe-cst__row--c .gxe-cst__tiles,html.mo .gxe-cst__row--c .gxe-cst__n{animation-delay:.32s}

.gxf-devnotes{--gxf-ease:var(--e-out);width:100%;box-sizing:border-box;padding-top:clamp(16px,2.2vw,24px);border-top:1px solid var(--line)}
.gxf-devnotes *{box-sizing:border-box}
.gxf-dn-grid{display:grid;grid-template-columns:1fr;gap:clamp(20px,3vw,30px);margin:0;padding:0}
@media (min-width:620px){.gxf-dn-grid{grid-template-columns:repeat(3,1fr)}}
.gxf-dn-cell{min-width:0;margin:0;display:flex;flex-direction:column;align-items:center;gap:clamp(8px,1vw,12px)}
.gxf-dn-label{margin:0;font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--cyan-ink);text-align:center;line-height:1.25}
html.mo .gxf-a-key{animation:gxf-a-key 7s var(--gxf-ease) infinite}
html.mo .gxf-a-input{animation:gxf-a-input 7s var(--gxf-ease) infinite}
html.mo .gxf-a-hi{animation:gxf-a-hi 7s ease-in-out infinite}
html.mo .gxf-a-hi2{animation:gxf-a-hi2 7s ease-in-out infinite}
html.mo .gxf-a-flow{animation:gxf-a-flow 7s ease-in-out infinite;transform-box:fill-box;transform-origin:50% 50%}
html.mo .gxf-b-icon{animation:gxf-b-icon 7.5s var(--gxf-ease) infinite}
html.mo .gxf-b-ring{animation:gxf-b-ring 7.5s ease-out infinite}
html.mo .gxf-c-note{animation:gxf-c-note 8s var(--gxf-ease) infinite}
html.mo .gxf-c-w1{animation:gxf-c-wave 8s ease-in-out infinite}
html.mo .gxf-c-w2{animation:gxf-c-wave 8s ease-in-out .18s infinite}
html.mo .gxf-c-w3{animation:gxf-c-wave 8s ease-in-out .36s infinite}
html.mo .gxf-b-icon,html.mo .gxf-b-ring{transform-box:fill-box;transform-origin:50% 50%}
@keyframes gxf-a-key{0%,12%{transform:translateY(15px);opacity:0}28%,70%{transform:translateY(0);opacity:1}88%,100%{transform:translateY(15px);opacity:0}}
@keyframes gxf-a-input{0%,12%{transform:translateY(12px)}28%,70%{transform:translateY(0)}88%,100%{transform:translateY(12px)}}
@keyframes gxf-a-hi{0%,6%{opacity:0}18%,38%{opacity:.9}50%,100%{opacity:0}}
@keyframes gxf-a-hi2{0%,50%{opacity:0}62%,82%{opacity:.9}94%,100%{opacity:0}}
@keyframes gxf-a-flow{0%,100%{opacity:.45;transform:scaleX(1)}45%{opacity:1;transform:scaleX(1.06)}}
@keyframes gxf-b-icon{0%,8%{transform:translateY(-13px) scale(.94);opacity:0}18%{opacity:1}32%,84%{transform:translateY(0) scale(1);opacity:1}94%,100%{transform:translateY(-13px) scale(.94);opacity:0}}
@keyframes gxf-b-ring{0%,30%{transform:scale(1);opacity:0}38%{opacity:.75}56%,100%{transform:scale(1.06);opacity:0}}
@keyframes gxf-c-note{0%,8%{transform:translateY(-9px);opacity:0}20%,74%{transform:translateY(0);opacity:1}88%,100%{transform:translateY(-9px);opacity:0}}
@keyframes gxf-c-wave{0%,10%{opacity:.15}24%{opacity:.95}52%,100%{opacity:.3}}

.gxg-gap,.gxg-gap *{box-sizing:border-box}
.gxg-gap{
  --gxg-floor:58%;
  --gxg-side:24%;
  --gxg-cw:clamp(30px,3.4vw,42px);
  --gxg-ch:clamp(22px,2.4vw,28px);
  --gxg-rest:calc(var(--gxg-floor) - var(--gxg-ch) - 2px);
  --gxg-loop:9s;
  position:relative;width:100%;max-width:100%;margin:0;
  padding:clamp(12px,1.4vw,18px);
  border:1px solid var(--line);
  border-radius:14px;
  background:var(--paper);
  box-shadow:0 2px 6px rgba(11,34,57,.05),0 12px 28px rgba(11,34,57,.07);
}
.gxg-edge{position:absolute;top:0;bottom:0;width:0;border-left:1px dashed var(--line-2)}
.gxg-edge--pm{left:var(--gxg-side)}
.gxg-edge--am{left:calc(100% - var(--gxg-side))}
.gxg-void{
  position:absolute;top:var(--gxg-floor);left:var(--gxg-side);
  width:calc(100% - (var(--gxg-side) * 2));
  border-top:2px dashed var(--line-2);
}
.gxg-plinth{
  position:absolute;top:var(--gxg-floor);width:var(--gxg-side);z-index:2;
  padding:10px 6px 11px;text-align:center;
  background:linear-gradient(180deg,var(--tint),var(--paper));
  border:1px solid var(--line);border-top:0;border-radius:0 0 8px 8px;
}
.gxg-plinth--pm{left:0}
.gxg-plinth--am{right:0}
.gxg-plinth::before{
  content:"";position:absolute;left:-1px;right:-1px;top:-2px;height:3px;
  border-radius:2px;background:var(--brand);opacity:.72;
}
.gxg-lab{
  display:block;font-size:.72rem;line-height:1.25;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;color:var(--cyan-ink);
}
.gxg-ln{display:block;height:3px;border-radius:2px;background:var(--cyan)}
.gxg-ln--s{width:60%;opacity:.7}
/* .gxg-stage, .gxg-night, .gxg-chip and .gxg-badge are in the critical <style>
   in index.html: the stage because it carries this module's height, the other
   three because they are the chain that keeps the night panel and the three
   badge ticks inside it instead of escaping to the section box. The modifier
   below still overrides the base rule — it is later in the cascade, which is
   why the base must stay above the <link> and not come back down here. */
.gxg-chip--keep{
  top:var(--gxg-rest);
  border-color:var(--brand);background:var(--tint);
}
.gxg-chip--keep .gxg-ln{background:var(--brand);opacity:.6}
.gxg-axis{position:relative;height:24px;margin-top:9px}
.gxg-t{
  position:absolute;top:9px;left:var(--gxg-x);transform:translateX(-50%);
  font-size:.72rem;font-weight:700;letter-spacing:.08em;color:var(--mut);white-space:nowrap;
}
.gxg-t::before{content:"";position:absolute;left:50%;top:-9px;width:1px;height:6px;background:var(--line-2)}
/* Every falling chip carries its own `--gxg-y` — 4%, 16%, 28% ... 86% — and
   that is the REDUCED-MOTION STILL: enquiries scattered down the evening, which
   is the picture the band is making. It is not where the fall starts. The old
   `top` keyframes overrode it outright; translateY only offsets from it, so
   with motion on the chips have to be brought to one common start first or each
   one falls from its own resting place. Measured before this line: chips 3 and
   4 sat 54px and 86px below where they belong. `:not(--keep)` because the two
   caught chips genuinely do start from `--gxg-rest`. */
html.mo .gxg-chip:not(.gxg-chip--keep){top:-16%}
html.mo .gxg-chip{animation:gxg-fall var(--gxg-loop) linear var(--gxg-d,0s) infinite}
html.mo .gxg-chip--keep{animation-name:gxg-catch}
html.mo .gxg-plinth::before{animation:gxg-lit var(--gxg-loop) ease-in-out var(--gxg-d,0s) infinite}
/* -- `top` -> translateY, for the ten falling and caught chips ------------
   The largest layout-animating group on the page: ten chips animating `top`
   every frame, each forcing a layout pass inside the stage. Every stop below is
   the same pixel, because the stage height is a known value (--gxg-h, declared
   beside `height` in the critical sheet) so `top:N%` converts straight to a
   translateY fraction.

   Two things had to be carried by hand:
   - The base rule sets `transform:rotate(var(--gxg-r,0deg))`, and a keyframe
     that sets `transform` replaces it wholesale, so every moving stop restates
     the rotation or the chip snaps upright mid-fall.
   - gxg-fall's rotation used to interpolate on its own between 20% and 30%,
     because the stops between only touched `top`. Now those stops carry
     `transform`, so the intermediate angles are written out: with `linear`
     timing, 2.1deg and 4.2deg. Drop them and the chip rotates in a step.

   gxg-catch's stops are absolute lengths rather than fractions because its
   resting position --gxg-rest is `58% - chip height - 2px`, so each delta is
   the expansion of that. */
@keyframes gxg-fall{
  0%       {transform:translateY(0) rotate(0deg);opacity:0}
  2.5%     {opacity:1}
  4%       {transform:translateY(calc(var(--gxg-h) * .05)) rotate(0deg)}
  8%       {transform:translateY(calc(var(--gxg-h) * .16)) rotate(0deg)}
  12%      {transform:translateY(calc(var(--gxg-h) * .30)) rotate(0deg)}
  16%      {transform:translateY(calc(var(--gxg-h) * .48)) rotate(0deg)}
  20%      {transform:translateY(calc(var(--gxg-h) * .69)) rotate(0deg);opacity:1}
  23%      {transform:translateY(calc(var(--gxg-h) * .86)) rotate(2.1deg)}
  26%      {transform:translateY(calc(var(--gxg-h) * 1.05)) rotate(4.2deg)}
  28%      {opacity:0}
  30%      {transform:translateY(calc(var(--gxg-h) * 1.32)) rotate(7deg);opacity:0}
  30.1%,100%{transform:translateY(0) rotate(0deg);opacity:0}
}
@keyframes gxg-catch{
  0%   {transform:translateY(calc(var(--gxg-h) * -.74 + var(--gxg-ch) + 2px)) rotate(var(--gxg-r,0deg));opacity:0}
  2.5% {opacity:1}
  4%   {transform:translateY(calc(var(--gxg-h) * -.69 + var(--gxg-ch) + 2px)) rotate(var(--gxg-r,0deg))}
  8%   {transform:translateY(calc(var(--gxg-h) * -.58 + var(--gxg-ch) + 2px)) rotate(var(--gxg-r,0deg))}
  13%  {transform:translateY(calc(var(--gxg-h) * -.40 + var(--gxg-ch) + 2px)) rotate(var(--gxg-r,0deg))}
  17%  {transform:translateY(calc(var(--gxg-h) * -.22 + var(--gxg-ch) + 2px)) rotate(var(--gxg-r,0deg))}
  20%  {transform:translateY(-5px) rotate(var(--gxg-r,0deg))}
  22%  {transform:translateY(0) rotate(var(--gxg-r,0deg));opacity:1}
  90%  {transform:translateY(0) rotate(var(--gxg-r,0deg));opacity:1}
  100% {transform:translateY(-8px) rotate(var(--gxg-r,0deg));opacity:0}
}
@keyframes gxg-lit{
  0%,42%{opacity:.72}
  47%{opacity:1}
  56%{opacity:.72}
  100%{opacity:.72}
}
/* THE LINK-IN-BIO PHONE IS GONE with the #nosite band. It was the page's
   only scroll-driven (animation-timeline) module, and it also carried the
   second `.dev.phone` in the document — the one the stage-5 notes flagged as
   colliding with the new `.shot` frame. Both problems end here. The argument
   survives as the link under the hero field and the open FAQ item. */
footer{background:var(--bg);border-top:1px solid var(--line);padding:44px 0;font-size:.9rem;color:var(--mut)}
.foot{display:flex;gap:20px;flex-wrap:wrap;align-items:center}
.foot a{color:var(--mut);text-decoration:none}
.foot a:hover{color:var(--ink);text-decoration:underline}

/* == NOTHING ANIMATES OFF SCREEN ==
   The seven explainer modules are the honest exemption to "no infinite
   animations": the loop IS the explanation. Paying for all seven from first
   paint was not. Measured at the fold before this: 184 animations running, 19
   of them visible. A reader who never scrolled past the hero was driving 165
   animations for pictures four screens below her.

   `.vis` is toggled by the single observer in index.html.

   THREE THINGS HERE ARE LOAD-BEARING, each found by measuring:

   1. POSITION - must stay at the END of the file. `animation:` is a shorthand
      and resets animation-play-state to `running`, so from the top every
      module's own shorthand overrode these rules: 10 paused instead of 158.
   2. `!important` - even from the bottom, `html.mo .gxa-wrap *` scores (0,2,1)
      against `html.mo .gxa-grid>g` at (0,2,2) and lost: 51 still running.
   3. `::before`/`::after` - play-state does not inherit and `*` does not match
      pseudo-elements, so five `.gxd-capt::after` bars and two
      `.gxg-plinth::before` lamps kept running until they were named.

   DEVIATION FROM 4.8, which asks for iteration-count:1 + fill:both. Do not.
   These loops are cyclical - 100% IS 0%, the state before anything has been
   explained. `gxb-k-hl` ends at opacity 0, `gxd-fw1` at scaleX(0),
   `gxe-cst-grow` at width 0, so play-once-and-hold would leave three of the
   seven holding a blank picture. Pause/resume gives the whole saving, which is
   also why there is no "Play again" button: the diagram is always
   mid-explanation when you are looking at it.

   The reduced-motion still needs no rule: every animation here is behind
   html.mo, so html.rm renders each module's declared CSS state, which is the
   composed diagram these were drawn around. */

/* ══ STAGE 7 ══════════════════════════════════════════════════════════════
   Four rules, and three of them exist only because two blocks changed ground.
   Nothing here animates: this stage moves arguments, it does not add motion.

   Placed ABOVE the animation-play-state block below, which has to stay last —
   that is a measured constraint from stage 1, not a preference. */

/* -- the crm micro-benefit row ------------------------------------------
   Replaces the gxd five-station flow. A two-word title and exactly one
   sentence, per the density rule, which is also what stops it turning back
   into a diagram: there is no room in a <b> for a picture. */
.crm-steps{
  list-style:none;margin:0;padding:0;
  display:grid;gap:clamp(18px,2.4vw,30px);
  border-top:1px solid var(--line);padding-top:clamp(22px,3vw,32px);
}
@media (min-width:700px){ .crm-steps{grid-template-columns:repeat(3,1fr)} }
.crm-steps li{margin:0;font-size:.95rem;line-height:1.55;color:var(--mut)}
.crm-steps b{display:block;margin-bottom:5px;font-size:1rem;color:var(--ink)}

/* -- the three-up, now on navy -----------------------------------------
   #gxf-devnotes moved from a paper band into band 1. Its drawings are white
   devices and read correctly on navy without help; its two chrome values do
   not. --cyan-ink is #8F5400, which measures 2.62:1 on #071F5E — a caption
   nobody can read — and --line is a hairline tuned for white. Both re-point to
   values already validated on this ground: --quiet at 8.76:1, and the same
   rgba the navy band already uses for its own seam. */
.band--navy .gxf-dn-label{color:var(--quiet)}
.band--navy .gxf-devnotes{border-top-color:rgba(157,183,255,.20)}

/* -- a band's second-level headline -------------------------------------
   The bare h3 is 1.12rem, sized for card titles, and demoting the handover
   argument from 34px straight to 18px reads as "this matters less" rather than
   "this is a level down". Between the two, so the outline is right and the
   weight is honest. */
.h-sub{font-size:clamp(1.28rem,2.1vw,1.55rem);line-height:1.22;letter-spacing:-.012em}

/* -- the closing band's text link ---------------------------------------
   White on navy is 15.4:1; the underline is what stops it reading as a
   disabled button next to the filled one beside it. */
.close-link{align-self:center;color:#fff;font-weight:600;font-size:.97rem;
  text-decoration:underline;text-underline-offset:4px;
  text-decoration-color:rgba(255,255,255,.45)}
.close-link:hover{text-decoration-color:#fff}

/* -- the closing capture field -----------------------------------------
   The band is centred and narrow, and .ask is a flex row built for the hero's
   left column. Constrained rather than restyled, so the field a visitor meets
   at the bottom of the page is recognisably the field she met at the top. */
.ask--close{max-width:470px;margin:28px auto 0;justify-content:center}

html.mo .gxa-wrap  *,html.mo .gxa-wrap  *::before,html.mo .gxa-wrap  *::after,
html.mo .gxb-wrap  *,html.mo .gxb-wrap  *::before,html.mo .gxb-wrap  *::after,
html.mo .gxc-oh    *,html.mo .gxc-oh    *::before,html.mo .gxc-oh    *::after,
html.mo .gxe-cst   *,html.mo .gxe-cst   *::before,html.mo .gxe-cst   *::after,
html.mo .gxf-devnotes *,html.mo .gxf-devnotes *::before,html.mo .gxf-devnotes *::after,
html.mo .gxg-gap   *,html.mo .gxg-gap   *::before,html.mo .gxg-gap   *::after,
/* The three ambient loops that live outside the diagram modules and were the
   only things left running below the fold: the mascot drift, the onion-skin
   trail in the closing line, and the rolling public key in the code strip. */
html.mo .mascot img,html.mo .trail span,html.mo .trail .lead,html.mo .mini-reel{
  animation-play-state:paused !important;
}
html.mo .gxa-wrap.vis  *,html.mo .gxa-wrap.vis  *::before,html.mo .gxa-wrap.vis  *::after,
html.mo .gxb-wrap.vis  *,html.mo .gxb-wrap.vis  *::before,html.mo .gxb-wrap.vis  *::after,
html.mo .gxc-oh.vis    *,html.mo .gxc-oh.vis    *::before,html.mo .gxc-oh.vis    *::after,
html.mo .gxe-cst.vis   *,html.mo .gxe-cst.vis   *::before,html.mo .gxe-cst.vis   *::after,
html.mo .gxf-devnotes.vis *,html.mo .gxf-devnotes.vis *::before,html.mo .gxf-devnotes.vis *::after,
html.mo .gxg-gap.vis   *,html.mo .gxg-gap.vis   *::before,html.mo .gxg-gap.vis   *::after,
html.mo .mascot.vis img,html.mo .trail.vis span,html.mo .trail.vis .lead,html.mo .mini.vis .mini-reel{
  animation-play-state:running !important;
}
