
    /* ==========================================================================
       SKYTERRA GROUP — "INTELLIGENCE ACROSS SCALES"
       Light cinematic design system. Spine: SCALE-ZOOM. Canvas: warm white.
       ========================================================================== */

    /* ---------- 1. TOKENS ---------- */
    :root{
      /* surfaces */
      --paper:        #FFFFFF;   /* warm white — the canvas */
      --paper-white:  #FFFFFF;   /* the theatre */
      --paper-blue:   #EFF4FA;   /* extremely pale blue */
      --paper-edge:   #F1F5F9;   /* cool gray edge */
      --navy:         #0B2E4A;   /* the one earned dark passage (footer) */

      /* ink */
      --ink:          #0E2A42;   /* 15:1 on paper */
      --ink-2:        #33546E;   /* 7.5:1 on paper */
      --ink-3:        #55697C;   /* 4.6:1 on paper — captions only */
      --on-navy:      #FFFFFF;   /* 14:1 on navy */
      --on-navy-2:    #C3D5E3;   /* 9:1 on navy */

      /* brand */
      --brand:        #004C97;   /* 8.1:1 on paper — the only accent safe for text */
      --sky:          #0096D6;   /* graphics only (3.2:1 — never text on light) */
      --sky-soft:     #89C2D9;   /* graphics + text on navy (7.2:1) */
      --teal:         #40916C;   /* secondary graphic accent */


      /* lines */
      --line:         rgba(14,42,66,.11);
      --line-2:       rgba(14,42,66,.20);
      --line-3:       rgba(14,42,66,.34);

      /* type */
      --f-display: 'Space Grotesk', 'Poppins', system-ui, -apple-system, sans-serif;
      --f-body:    'Poppins', system-ui, -apple-system, sans-serif;
      --f-serif:   'Poppins', system-ui, -apple-system, sans-serif;

      --fs-display: clamp(2.125rem, 1.10rem + 5.2vw, 6.25rem);
      --fs-h2:      clamp(1.75rem, 1.12rem + 2.35vw, 3.25rem);
      --fs-h3:      clamp(1.125rem, 1.02rem + 0.52vw, 1.5rem);
      --fs-state:   clamp(1.375rem, 0.95rem + 1.9vw, 2.5rem);
      --fs-lead:    clamp(1.0625rem, 1.00rem + 0.42vw, 1.3125rem);
      --fs-body:    clamp(1rem, 0.975rem + 0.13vw, 1.0625rem);
      --fs-small:   clamp(0.9375rem, 0.92rem + 0.09vw, 1rem);
      --fs-label:   0.8125rem;
      --fs-micro:   0.6875rem;   /* 11px — eyebrows, rail numbers, chips */
      --fs-meta:    0.78125rem;  /* 12.5px — tags, captions, table meta */

      /* space */
      --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px;
      --s7:48px; --s8:64px; --s9:96px; --s10:128px;
      --gutter: clamp(20px, 4.6vw, 76px);
      --maxw: 1360px;
      --scene-py: clamp(32px, 3.3vw, 58px);   /* doubles at every seam */

      /* chrome */
      --nav-h: 72px;
      --r-card: 14px; --r-btn: 10px; --r-chip: 8px;

      /* motion */
      --d1: 170ms; --d2: 320ms; --d3: 620ms;
      --e-out: cubic-bezier(.22,1,.36,1);
      --e-io:  cubic-bezier(.65,0,.35,1);
    }

    /* ---------- 2. RESET / BASE ---------- */
    *,*::before,*::after{ box-sizing:border-box; }
    html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
    body{
      margin:0;
      background-color:#FFFFFF;              /* explicit color — light-theme gate */
      color:var(--ink);
      font-family:var(--f-body);
      font-size:var(--fs-body);
      line-height:1.62;
      -webkit-font-smoothing:antialiased;
      overflow-x:hidden;                      /* propagates to viewport; sticky stays live */
    }
    img{ max-width:100%; display:block; }
    h1,h2,h3,h4{ margin:0; font-family:var(--f-display); color:var(--ink);
                 font-weight:600; letter-spacing:-.02em; line-height:1.08; }
    p{ margin:0; }
    ul,ol{ margin:0; padding:0; list-style:none; }
    a{ color:var(--brand); text-decoration:none; }
    button{ font:inherit; color:inherit; background:none; border:0; }

    :focus-visible{
      outline:3px solid var(--brand);
      outline-offset:3px;
      border-radius:var(--r-btn);
    }
    .site-footer :focus-visible{ outline-color:var(--sky-soft); }

    .skip-link{
      position:absolute; left:-9999px; top:0; z-index:99999;
      background:var(--navy); color:#fff; padding:12px 20px;
      font-family:var(--f-display); font-size:var(--fs-label);
      letter-spacing:.1em; text-transform:uppercase; min-height:44px;
      display:inline-flex; align-items:center;
    }
    .skip-link:focus{ left:0; }

    .sr-only{
      position:absolute; width:1px; height:1px; padding:0; margin:-1px;
      overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
    }
    .sprite{ position:absolute; width:0; height:0; overflow:hidden; }

    /* ---------- 3. SHARED PARTS ---------- */
    .wrap{ max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gutter); }


    .accent{ font-family:var(--f-serif); font-style:italic; font-weight:400;
             letter-spacing:-.01em; color:var(--brand); }

    .btn{
      display:inline-flex; align-items:center; gap:12px;
      min-height:52px; padding:0 26px;
      font-family:var(--f-display); font-weight:500; font-size:var(--fs-small);
      letter-spacing:.055em; text-transform:uppercase;
      border:1px solid transparent; border-radius:var(--r-btn);
      box-shadow:0 2px 0 rgba(9,35,61,.08), 0 7px 16px rgba(9,35,61,.08);
      transition:background var(--d2) var(--e-out), border-color var(--d2) var(--e-out),
                                   color var(--d2) var(--e-out),
                                   transform var(--d2) var(--e-out);
    }
    .btn .arw{ transition:transform var(--d2) var(--e-out); }
    .btn--primary,
    .btn--ghost{ background:linear-gradient(135deg, #013C79, var(--brand)); color:#fff;
      border:1px solid rgba(255,255,255,.26);
      box-shadow:inset 0 1px 0 rgba(255,255,255,.22), 0 8px 18px rgba(0,76,151,.22); }
    @media (hover:hover) and (pointer:fine){
      .btn--primary:hover,
      .btn--ghost:hover{ background:linear-gradient(135deg, #012F60, #013F80); color:#fff;
        border-color:rgba(255,255,255,.34); transform:translateY(-2px); }
      .btn:hover .arw{ transform:translateX(5px); }
    }
    .btn:active{ transform:translateY(0); }
    @media (prefers-reduced-motion: reduce){ .btn:hover, .btn:active{ transform:none; } }
    @media (forced-colors: active){ .btn{ border-color:ButtonText; box-shadow:none; } }

    /* reveal — JS only, fully neutralised under reduced motion */
    .js .rv{ opacity:0; transform:translate3d(0,20px,0); will-change:opacity,transform; }
    .js .rv{ transition:opacity 620ms var(--e-out), transform 620ms var(--e-out);
             transition-delay:var(--rvd,0ms); }
    .js .rv.in{ opacity:1; transform:none; will-change:auto; }

    /* ---------- 4. ADMIN BAR (internal chrome — minimal, not part of the design) ---------- */
    .admin-bar{
      position:relative; z-index:60;
      background:#12283C; color:#C3D5E3;
      display:flex; align-items:center; justify-content:space-between;
      gap:12px; padding:0 clamp(14px,3vw,26px);
      font-family:var(--f-body); font-size:var(--fs-meta); letter-spacing:.02em;
      min-height:36px;
    }
    .admin-bar span{ display:inline-flex; align-items:center; gap:8px; min-height:36px; }
    .admin-bar a{
      color:#DCE9F3; display:inline-flex; align-items:center; gap:8px;
      min-height:30px; padding:0 6px; border-radius:var(--r-btn);
    }
    .admin-bar .ico{ width:14px; height:14px; flex:none; }
    @media (pointer:fine){ .admin-bar a:hover{ color:#fff; } }

    /* ---------- 5. HEADER / NAV ---------- */
    .site-header{
      position:sticky; top:0; z-index:50;
      background:rgba(255,255,255,.97);
      border-bottom:1px solid var(--line);
    }
    .header-inner{
      max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gutter);
      height:var(--nav-h); display:flex; align-items:center; justify-content:space-between; gap:20px;
    }
    .header-logo{ display:inline-flex; align-items:center; min-height:44px; }
    .header-logo img{ height:34px; width:auto; }
    .header-nav{ display:flex; align-items:center; gap:clamp(14px,2.1vw,34px); }
    .header-nav a{
      position:relative; display:inline-flex; align-items:center;
      min-height:44px; padding:0 2px;
      font-family:var(--f-display); font-size:var(--fs-label); font-weight:500;
      letter-spacing:.14em; text-transform:uppercase; color:var(--ink-2);
      transition:color var(--d2) var(--e-out);
    }
    .header-nav a::after{
      content:""; position:absolute; left:0; right:0; bottom:calc(50% - 14px);
      height:1.5px; background:var(--brand);
      transform:scaleX(0); transform-origin:left;
      transition:transform var(--d2) var(--e-out);
    }
    .header-nav a.active{ color:var(--ink); }
    .header-nav a.active::after{ transform:scaleX(1); }
    @media (pointer:fine){
      .header-nav a:hover{ color:var(--brand); }
      .header-nav a:hover::after{ transform:scaleX(1); }
    }
    .nav-toggle{
      display:none; width:46px; height:46px; align-items:center; justify-content:center;
      border:1px solid var(--line-2); border-radius:var(--r-btn); color:var(--ink);
      touch-action:manipulation; -webkit-tap-highlight-color:transparent;
    }
    .nav-toggle .ico{ width:20px; height:20px; }
    .btn, .zone-btn, .footer-social-icons a, .mail, .rail button{
      touch-action:manipulation; -webkit-tap-highlight-color:transparent;
    }

    /* ---------- 6. SCENE 1 — COLD OPEN (hero) ---------- */
    .hero{
      /* TRANSPARENT so the page-wide webbing shows through. An opaque section
         that is also positioned paints ABOVE a `position:fixed; z-index:0`
         backdrop, which is what hid the lattice everywhere it mattered. The
         page still reads white because <body> carries --paper. */
      position:relative; overflow:hidden; background:transparent;
      --band-h: clamp(150px, 21vh, 250px);
      padding-top:clamp(46px,6.4vh,86px);
      padding-bottom:calc(var(--band-h) + 34px);
    }
    /* Transparent: at --maxw this column is the whole viewport bar ~40px, so
       papering it hid the field across the entire hero. The paper moves onto
       the two blocks that actually carry text (below), which keeps the copy off
       the webbing -- operator, 2026-09-03: the webbing must not sit under
       content -- while the field stays visible around and between them. */
    .hero-inner{ position:relative; z-index:3; max-width:var(--maxw);
                 margin-inline:auto; padding-inline:var(--gutter);
                 background:transparent; }

    .hero-title{
      font-size:var(--fs-display); font-weight:600; line-height:.95;
      letter-spacing:-.036em; max-width:100%;
      margin:0 0 clamp(30px,4vw,58px);
      /* Paper plate for the headline. The ring is the same colour, so nothing
         reads as a box; it just keeps lattice lines off the glyph edges. */
      background:var(--paper); box-shadow:0 0 0 14px var(--paper);
    }
    .hero-title .ln{ display:block; }
    .hero-title .ln--2{ color:var(--brand); }

    .hero-foot{
      display:grid; grid-template-columns:minmax(0,1.18fr) minmax(0,.82fr);
      gap:clamp(26px,3.4vw,60px); align-items:start;
      /* Indented off the headline so the two read as separate beats. On the row
         rather than the lede alone, so the CTA stays aligned with the copy once
         the grid stacks below 1024. */
      padding-left:clamp(14px,1.8vw,30px);
    }
    .hero-lede p{ max-width:52ch; color:var(--ink-2); }
    .hero-lede .lead{
      font-size:var(--fs-lead); color:var(--ink); line-height:1.42;
      letter-spacing:-.012em; margin-bottom:var(--s4);
      font-family:var(--f-display); font-weight:400;
      /* Same paper plate as the headline: measured 2,420 inked pixels landing
         on this paragraph once .hero-inner stopped masking the field. */
      background:var(--paper); box-shadow:0 0 0 12px var(--paper);
    }
    /* The CTA is the hero-foot grid's SECOND column, not a block under the
       lede. Nested inside .hero-lede it left that column empty, which is
       where the band of dead space to the right of the copy came from. */
    /* Centred in the space it owns (operator, 2026-09-03): the button is
       248px inside a 471px column and was flush left and top-aligned, so it
       sat 223px off-centre horizontally and 69px above the middle of the
       copy beside it. Both axes are centred while the row is two columns;
       once it stacks the button returns to the copy's left edge. */
    .hero-cta{ align-self:center; margin-top:0;
               display:flex; justify-content:center; }
    /* Two CTAs now, one under the other. The stack goes INSIDE .hero-cta
       rather than turning that box into a column: .hero-cta owns the hero's
       centring contract (and the rule that returns it to the copy's left edge
       once the row stacks), and re-flowing it would change what those two
       declarations mean on the cross axis. */
    .hero-cta-stack{ display:flex; flex-direction:column; align-items:center;
                     gap:12px; max-width:100%; }
    /* The second label is long. Let the button take the width it is given and
       wrap inside it instead of pushing the hero wider -- .btn sets no
       vertical padding because it never wrapped before, so the padding is
       added HERE, on both buttons, rather than on one button's treatment. */
    .hero-cta-stack .btn{ max-width:100%; padding-block:12px; text-align:center; }

    /* ---------- 6A. TALK TO US (booking dialog) ----------
       Operator, 2026-10-13: visitors book a free session with Jonathan
       on their own, from the very top of the homepage. The scheduler is his
       Calendly event, which checks his connected calendars for busy time and
       writes the booking to his calendar. It loads only when the dialog
       opens, so the page carries no third-party frame or cookie until a
       visitor asks for one. Without JS (or without <dialog>) the button is a
       plain link to the same scheduler. */
    .book{
      width:min(820px, calc(100vw - 32px)); max-width:none;
      max-height:calc(100dvh - 32px); margin:auto; padding:0;
      border:0; border-radius:var(--r-card); background:var(--paper-white); color:var(--ink);
      box-shadow:0 24px 60px rgba(9,35,61,.28);
      overflow:auto; overscroll-behavior:contain;
    }
    .book:not([open]){ display:none; }
    .book::backdrop{ background:rgba(11,46,74,.55); }
    .book-head{
      position:sticky; top:0; z-index:2; background:var(--paper-white);
      display:flex; align-items:flex-start; justify-content:space-between; gap:18px;
      padding:clamp(20px,2.4vw,30px) clamp(20px,2.6vw,34px) 6px;
    }
    .book-head h2{ font-family:var(--f-display); font-weight:600; font-size:var(--fs-h3);
                   letter-spacing:-.01em; line-height:1.2; margin:0 0 6px; color:var(--ink); }
    .book-head p{ margin:0; max-width:60ch; color:var(--ink-2); font-size:var(--fs-small); }
    .book-close{
      flex:none; width:46px; height:46px; display:inline-flex; align-items:center; justify-content:center;
      border:0; border-radius:var(--r-btn); background:var(--paper-blue); color:var(--ink); cursor:pointer;
      touch-action:manipulation; -webkit-tap-highlight-color:transparent;
    }
    .book-close .ico{ width:20px; height:20px; }
    @media (pointer:fine){ .book-close:hover{ background:#E1EBF5; } }
    .book-frame{ position:relative; min-height:660px; }
    .book-frame iframe{ position:relative; z-index:1; display:block; width:100%; height:700px; border:0; }
    /* Behind the frame and never clickable: Calendly paints over it as soon as
       it renders, long before the frame's load event (which waits for its
       third-party scripts and can take tens of seconds on slow links). */
    .book-wait{ position:absolute; inset:0; z-index:0; pointer-events:none;
                display:flex; align-items:flex-start; justify-content:center;
                padding-top:120px; color:var(--ink-2); font-size:var(--fs-small); }
    .book-frame.ready .book-wait{ display:none; }
    .book-foot{ margin:0; padding:4px clamp(20px,2.6vw,34px) clamp(18px,2vw,26px);
                color:var(--ink-2); font-size:var(--fs-small); }
    .book-foot a{ color:var(--brand); text-decoration:underline; text-underline-offset:3px; }
    html.book-open{ overflow:hidden; }
    @media (max-width:760px){
      .book{ width:100vw; max-height:100dvh; height:100dvh; border-radius:0; }
      .book-frame{ min-height:0; }
    }


    /* photographic horizon band */
    .hero-band{
      position:absolute; left:0; right:0; bottom:0; height:var(--band-h);
      z-index:1; overflow:hidden;
    }
    .hero-band video{
      position:absolute; inset:0; width:100%; height:100%;
      object-fit:cover; object-position:50% 62%;
      opacity:0; transition:opacity 900ms var(--e-out);
    }
    .hero-band video.playing{ opacity:1; }
    .hero-band img{
      width:100%; height:100%; object-fit:cover; object-position:50% 62%;
    }
    .hero-band::after{
      content:""; position:absolute; inset:0; pointer-events:none;
      background:linear-gradient(to bottom, #FFFFFF 0%, rgba(255,255,255,.72) 26%,
                                            rgba(255,255,255,0) 62%);
    }
    .hero-band-note{
      position:absolute; left:0; right:0; bottom:calc(var(--band-h) - 4px); z-index:3;
      max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gutter);
      background:var(--paper);
      font-family:var(--f-display); font-size:var(--fs-micro); font-weight:500;
      letter-spacing:.17em; text-transform:uppercase; color:var(--ink-3);
    }

    /* ---------- 6B. HERO CAPABILITIES ---------- */
    .caps{
      display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
      gap:clamp(18px,2.2vw,34px);
      margin-top:clamp(34px,4.4vw,66px);
      margin-bottom:clamp(18px,2.2vw,30px);
    }
    .cap-card{ display:flex; flex-direction:column; background:var(--paper); }
    .cap-shot{
      margin:0 0 clamp(14px,1.5vw,20px);
      aspect-ratio:16/10; overflow:hidden;
      border-radius:var(--r-card); background:var(--paper-edge);
      border:1px solid var(--line);
      box-shadow:0 1px 0 rgba(9,35,61,.04), 0 12px 28px rgba(9,35,61,.07);
    }
    .cap-shot img{
      width:100%; height:100%; object-fit:cover; display:block;
      transition:transform 700ms var(--e-out);
    }
    @media (hover:hover) and (pointer:fine){
      .cap-card:hover .cap-shot img{ transform:scale(1.04); }
    }
    @media (prefers-reduced-motion: reduce){
      .cap-shot img{ transition:none; }
      .cap-card:hover .cap-shot img{ transform:none; }
    }
    .cap-card h3{
      font-size:var(--fs-h3); letter-spacing:-.018em; margin-bottom:6px;
    }
    .cap-card p{ color:var(--ink-2); font-size:var(--fs-small); max-width:34ch; }

    /* ---------- 8b. SCENE 3 — NETWORK and ASSISTANT ---------- */
    .duo{ background:transparent; padding-block:var(--scene-py);
          border-top:1px solid var(--line); }
    /* The deck's three-part positioning belongs on the public home page, next
       to the network and assistant that make the promise concrete. This is a
       heading and a list, not another row of cards: the existing pair remains
       the section's visual anchor and the copy costs no new chrome. */
    .platform-frame{ margin-bottom:var(--st-sp-6); background:var(--st-surface); }
    .platform-frame__head{ display:flex; align-items:flex-end;
                           justify-content:space-between; gap:var(--st-sp-5);
                           margin-bottom:var(--st-sp-5); }
    .platform-frame__eyebrow{ margin-bottom:var(--st-sp-2);
                              font-family:var(--f-display);
                              font-size:var(--fs-micro); font-weight:600;
                              letter-spacing:.18em; text-transform:uppercase;
                              color:var(--st-muted); }
    .platform-frame__title{ font-size:var(--fs-h2); color:var(--st-ink); }
    .platform-pillars{ display:grid;
                       grid-template-columns:repeat(3,minmax(0,1fr));
                       gap:var(--st-sp-5); }
    .platform-pillar{ min-width:0; }
    .platform-pillar h3{ font-size:var(--fs-h3); color:var(--st-blue-deep); }
    .platform-pillar p{ margin-top:var(--st-sp-2); max-width:34ch;
                        color:var(--st-slate); font-size:var(--fs-small);
                        line-height:1.5; }
    @media (max-width:719px){
      .platform-frame__head{ display:block; margin-bottom:var(--st-sp-4); }
      .platform-pillars{ grid-template-columns:minmax(0,1fr);
                         gap:var(--st-sp-4); }
      .platform-pillar{ display:grid; grid-template-columns:5.5rem minmax(0,1fr);
                        align-items:start; gap:var(--st-sp-3); }
      .platform-pillar p{ margin-top:0; max-width:none; }
    }
    /* This rule is the STACKED composition -- network above, assistant below,
       same width, same edges -- and it is what every width under 1280 gets.
       Section 8C at the end of this block turns it back into the two columns
       the operator asked for on wide screens: Our Network on the left, the
       assistant on the right, tops and bottoms level.
       Mobile-first is deliberate and is not just house style here. The stacked
       state is the one that has to be right on the narrow frames where the
       diagram reflows, and writing it as the base means the desktop pair is a
       single additive block that can be read (and, if it ever has to be,
       deleted) in one piece. It is also why every collapse target below is
       stated on the ROW axis: at these widths there is no second track to give
       up, and reclaiming the block above you is the only thing engaging can
       mean. 8C restates the ones that change. */
    .duo-grid{ display:grid; grid-template-columns:minmax(0,1fr);
               /* Two explicit rows -- diagram, then assistant -- and they are
                  `1fr auto` rather than `auto auto` for one reason: row one is
                  the track that collapses when the assistant is engaged, and a
                  track can only ANIMATE between two interpolable values. `auto`
                  and `0fr` are different types, so `auto -> 0fr` snaps: sampled
                  every frame through a collapse, the block went 700px -> 0 in a
                  single frame with 36 frames of nothing after it. `1fr -> 0fr`
                  is flex-to-flex and interpolates. In an auto-height grid a
                  `1fr` row is sized from its max-content contribution anyway, so
                  row one measures exactly what `auto` measured. */
               grid-template-rows:1fr auto;
               gap:clamp(18px,2vw,26px); align-items:start;
               transition:grid-template-rows 420ms var(--e-out),
                          row-gap 420ms var(--e-out); }
    .duo-head{ margin-bottom:clamp(12px,1.4vw,18px); text-align:center;
               background:var(--paper); }
    .duo-head h2{ font-family:var(--f-display); font-size:var(--fs-h3); font-weight:600;
                  letter-spacing:-.01em; color:var(--ink); margin:0; text-transform:uppercase; }
    .duo-head p{ margin:6px auto 0; max-width:44ch; color:var(--ink-3);
                 font-size:var(--fs-meta); line-height:1.45; text-wrap:balance; }
    .duo-col{ min-width:0; }
    /* The diagram block is the one that collapses, so it has to be the track's
       size rather than its own: `align-self:stretch` puts it on the row height
       (0 when engaged) and `overflow:hidden` clips the 700px iframe inside it
       while that row closes. Without the stretch, `align-items:start` above
       keeps the block content-sized and it simply overflows a zero-height row,
       still fully painted.
       `overflow:hidden` does the same job on the other axis in 8C, where the
       track that closes is a column and what it clips is a frame deliberately
       held at 561px. Both collapses depend on this one declaration. */
    .duo-col--net{ align-self:stretch; min-height:0; overflow:hidden; }

    /* ---------- 8B-0. THE ASSISTANT FILLS THE COLUMN IT IS GIVEN ----------
       `.stg-network` and `.stg-card` both carry `margin:0 auto` from the embed
       block. An auto INLINE margin on a grid or flex item suppresses stretch and
       sizes the box to fit-content instead. Idle that is invisible: the pitch
       paragraph's max-content is wider than the column, so fit-content saturates
       and the card reads as stretched. It stopped being invisible the moment
       anything hid that paragraph -- the card snapped to 350px inside a 1193px
       column, measured 547x511 idle -> 300x663 engaged, the "very narrow and
       overly tall container" in the report.
       This is now UNCONDITIONAL, and load-bearing rather than defensive. The
       pitch paragraph the old fit-content accidentally relied on has been
       DELETED outright, so on a stacked page the widest remaining max-content
       in the card is the words "ASK ME A QUESTION" -- without an explicit width
       the assistant would sit as a ~300px pill under a 1227px diagram at every
       viewport, not just an engaged one. Explicit width restores stretch and
       leaves the auto margins doing the only job they were written for, which
       is also what makes the card's edges line up with the diagram's. */
    .duo-col--eco .stg-network,
    .duo-col--eco .stg-network .stg-card{ width:100%; }

    /* `.stg-card` ships `transition:all .3s ease` alongside a hover lift, and
       `all` meant the width and height the eco-active retrack produces were
       animated by it too -- two transitions of different durations racing over
       the same box. Ease only the shadow -- the affordance that survives -- and
       leave the size change to the grid.
       The visible flicker was downstream of the collapse above, not of the
       hover rule: the card shrank out from under a stationary pointer, which
       un-hovered it (the #004C97 edge fading 0.96 -> 0 alpha, the card dropping
       its 5px lift) and then re-hovered once it settled. A card that keeps its
       column never leaves the cursor, so the hover state simply stays put. */
    .duo-col--eco .stg-explorer-card{ transition:box-shadow .3s ease; }

    /* A transformed ancestor becomes the containing block for position:fixed
       descendants, and the pop-out works by promoting .stg-frame -- a DESCENDANT
       of this card -- to a fixed layer. With the lift applied, `inset` resolved
       against the CARD instead of the viewport, and because the card is
       fit-content and the frame had just left normal flow, the pop-out came out
       0x600 and off to one side. Worse, it oscillated: the collapsed frame moved
       out from under the pointer, hover dropped, transform cleared, the frame
       snapped to viewport size back under the pointer, hover re-entered, and it
       collapsed again. That is the flashing over-sized panel in the report. The
       hover affordance stays -- edge and shadow -- it just may not be a
       transform on the one card that hosts a fixed layer. */
    .stg-network .stg-explorer-card:hover{ transform:none; }

    /* First-paint skeleton for the self-measuring diagram, and the ONLY height
       it has until its first message lands. It used to be an inline
       height:2200px on the element. Beside the assistant that was survivable --
       a too-tall neighbour in the other column -- but ABOVE it, 2200px of
       placeholder is 2200px the assistant starts pushed down by, and the
       correction lands ~0.2s later as one large upward jump. Measured on the
       stacked layout at 1024: the assistant's top moved 4004px -> 2301px when
       the real height arrived.
       These numbers are the settled content heights this document actually
       reports, measured across the frame widths the host produces: 658-702px
       wherever the quadrants pair 2x2 in a STACKED host, and ~1050-1140px once
       they stack into one column. 700 stays right for all of those.
       It is deliberately NOT retuned for the two-column composition, even
       though that one now settles ~50px higher: the same declaration serves a
       1024 frame that settles at 658px, and moving the base to 730 would trade
       a 42px first-paint correction there for a 72px one. Section 8C overrides
       it inside the desktop block instead, where the taller number is the only
       one that can apply.
       617px is the STACKED host width at which the frame crosses the diagram's
       own 560px stacking threshold (stacked, the host hands the frame roughly
       0.908 of the viewport, --gutter being clamp(20px,4.6vw,76px) a side), so
       below 1280 the two thresholds describe the same moment. Above it they do
       not, and cannot: a two-column host is never within 600px of 617. That is
       why 8C keeps every half above 560 by construction rather than leaving
       this media query to catch it -- keyed to the viewport, it never would.
       The reporter still writes the exact height INLINE, which beats both of
       these -- this only has to be close, and it also means a reader whose JS
       never runs gets a sensibly-sized frame instead of a 2200px hole. */
    .duo-col--net iframe{ display:block; width:100%; border:0; height:700px; }
    @media (max-width:617px){ .duo-col--net iframe{ height:1100px; } }
    /* The block ships its own scoped .stg-network styles verbatim. Only the
       type is re-pointed at the house faces, the way the runtime mirrors are,
       and the padding is trimmed for a half-width column. */
    .stg-network, .stg-network h2, .stg-network h3{ font-family:var(--f-body) !important; }
    .stg-network h2, .stg-network h3, .stg-network .stg-title{ font-family:var(--f-display) !important; }
    .stg-network{ max-width:none !important; }
    .stg-network .stg-card{ padding:clamp(14px,1.5vw,20px) clamp(14px,1.6vw,22px) !important;
                            margin:0 auto !important; max-width:none !important; }
    .stg-network .stg-title{ font-size:clamp(1rem,1.4vw,1.2rem) !important; }
    /* ONE line: "Explore - Ask me a question" replaced the eyebrow + title pair,
       which cost two lines of thumb-scroll between the network diagram and the
       assistant on phones (operator, 2026-09-25). Held to one line down to 360px. */
    .stg-explorer-card .stg-title{ white-space:nowrap; margin:0 !important; }
    @media (max-width:430px){
      .stg-explorer-card .stg-title{ font-size:clamp(.82rem,3.9vw,1rem) !important; letter-spacing:.04em !important; }
    }
    /* `.stg-network .stg-desc` used to be sized here. The only .stg-desc on this
       page was the assistant's pitch paragraph, which the operator asked us to
       remove, so the override has nothing left to size. The block's own base
       rule stays where the verbatim embed put it. */
    .stg-network .stg-eyebrow{ margin-bottom:4px !important; }
    /* The assistant shipped at 620px as a standalone block, then got cut to
       clamp(360px,42vh,440px) so it would sit level with a diagram that only
       had half the page, then went back to being large when it became the
       second of two full-width blocks -- which is what the operator asked for
       ("keep the AI Assistant at its current large desktop size").
       It stays large now that the two columns are back, and the old 440px cap
       does NOT come with them. That number existed because the two columns had
       to be equalised by shrinking the taller one; 8C equalises them with a
       flex chain that GROWS this frame into whatever the diagram's column is
       tall instead. Measured at 1280: the clamp asks for 560px and the chain
       hands it 568.6px, ending the card exactly level with the diagram.
       The number is picked against the diagram, because "similar scale" is the
       other half of the same ask. Measured on the merged layout, the quadrant
       block renders 578px tall at 1440 and 575px at 1280, so clamp(560px,64vh,
       700px) puts the frame at 576px on a 900-tall display -- within 2px of the
       block above it, which is what makes the two read as a matched pair rather
       than as a diagram with an app bolted under it. 64vh keeps that
       relationship as the display grows, the 560px floor keeps the conversation
       usable on a short laptop, and 700px caps it under the 760px maximum the
       embedded block already used.
       READ THAT AS THE STACKED RULE ONLY. Beside the diagram rather than under
       it, "within 2px on a 900-tall display" is the whole problem: a
       viewport-driven height agrees with a content-driven neighbour at exactly
       one viewport height. 64vh saturates at the 700px cap on a tall monitor
       while the diagram has not moved, and the pair ended 133.5px apart at
       1440x1400 from these same numbers. Section 8C therefore replaces this
       basis entirely above 1280 and lets the row set the height; the clamp
       below still governs every width where the two blocks are stacked and
       neither can pull on the other.
       Phones get a lower floor: 560px of chat under four stacked quadrant cards
       is further than anyone will scroll. `!important` because the whole .stg-*
       family is overridden that way here -- the verbatim block ships its own
       sizing and this file re-points it. */
    .stg-network .stg-embed{ height:clamp(560px,64vh,700px) !important; }
    .stg-network .stg-frame{ margin-top:4px !important; }
    .stg-network .stg-fallback{ min-height:var(--st-tap-primary);
                                padding:8px 10px !important;
                                display:flex; align-items:center; justify-content:center;
                                font-size:var(--fs-meta) !important; }
    @media (max-width:560px){
      .stg-network .stg-embed{ height:clamp(520px,72vh,620px) !important; }
    }

    /* ---------- 8B. THE ASSISTANT TAKES THE SPACE ----------
       Once someone actually starts talking to the assistant it needs the room
       the conversation is about to use, and the diagram does not need to be on
       screen while they type. It is one click back via #ecoRestore.

       These rules are the STACKED collapse, so what is reclaimed is the HEIGHT
       of the block above -- and it runs at every width rather than only above
       1025px the way the original did, because on a phone the diagram above you
       is exactly the thing worth reclaiming. Section 8C adds the sideways
       collapse the two-column composition needs on top of it.
       Collapsing downward is strictly safer than collapsing sideways, and the
       difference is worth stating where the two rules can be read together: a
       height collapse never changes the frame's width, so the self-measuring
       diagram inside it stays honest throughout. A width collapse does, which
       is why 8C floors the width of the network column's children and why
       handleNetworkHeight() throws away anything reported while the section is
       engaged. Neither of those is needed at these widths; both are load-
       bearing at the others. */
    .eco-restore{
      display:none; align-items:center; gap:8px; margin-top:12px;
      /* 44px, not the 40px it shipped with: the old control only ever appeared
         above 1025px, where nobody is tapping it. It now appears at every
         width, phones included, so it has to clear the 44px touch target the
         rest of this page holds to. */
      min-height:44px; padding:0 14px; border:1px solid var(--line);
      border-radius:999px; background:var(--paper); color:var(--ink-2);
      font-family:var(--f-display); font-size:var(--fs-meta); font-weight:500;
      letter-spacing:.02em; cursor:pointer;
    }
    .eco-restore:hover{ border-color:var(--navy); color:var(--navy); }
    .eco-restore .ico{ width:16px; height:16px; }
    /* The button appearing is itself a layout event. In its old home -- the eco
       .duo-head, grid row 1 for BOTH columns -- an extra 40px control plus its
       12px gap shoved the card, and the conversation being read inside it,
       62px down the page in a single frame. It now lives inside the card
       instead, so it can only push the frame below it, and it still animates
       in over the same 420ms rather than appearing at full height.
       `hidden` still governs whether the control exists at all, so it stays out
       of the accessibility tree and the tab order until it is real. */
    .duo.eco-active .eco-restore{
      /* Block-level, not inline-flex: an INLINE box generates a line box
         whatever its own height is, so the control still added a 27.5px step on
         the frame it appeared. fit-content plus auto inline margins is the same
         centred pill the inline box was. */
      display:flex; width:fit-content; margin-inline:auto;
      overflow:hidden; height:44px;
      /* `backwards` matters: with the default fill-mode the control paints at
         its full 40px for the one frame between being un-hidden and the
         animation's first sample, which is a 27px jolt on its own. */
      animation:ecoRestoreIn 420ms var(--e-out) backwards;
    }
    /* min-height:44px is on the base rule and CLAMPS the animated height, so
       without collapsing it too the keyframe is a no-op and the 40px arrives in
       one frame anyway -- measured. */
    @keyframes ecoRestoreIn{
      from{ height:0; min-height:0; margin-top:0; opacity:0; border-width:0; }
    }
    .duo.eco-active .stg-fallback{ display:none; }
    /* The eyebrow and the title have done their job the moment somebody starts
       typing; after that they are just height the conversation could use.
       (.stg-desc used to be listed here too -- that paragraph no longer
       exists.) */
    .duo.eco-active .stg-explorer-card .stg-eyebrow,
    .duo.eco-active .stg-explorer-card .stg-title{ display:none; }

    /* The transition itself lives on the BASE .duo-grid rule above, not here:
       one declared on `.duo.eco-active` can only run while the class is
       present, so removing it -- clicking Show network -- snapped the layout
       back in a single frame. Same 420ms in both directions now.
       The engaged state is only the target values.
       Note what is deliberately NOT here: `max-height:0` on the block, and
       `height:0 !important` on its children. The DELETED width collapse -- the
       original two-column build's, not 8C's -- needed both, because a column
       that was 0 WIDE was still 2200px TALL and kept driving the row. Here the
       row IS the thing being animated, and either of those declarations clips
       the block to nothing on frame one -- which is precisely how the first
       version of this collapse ended up snapping. The row track closing is the
       whole animation; let it do the work.
       8C forbids the same two declarations for the same reason, so this is not
       advice that stops at the hinge: whichever axis is closing, the track
       closing IS the animation. What 8C needs INSTEAD of them -- a resting-width
       floor on the network column's children, and a discard in
       handleNetworkHeight() -- has no analogue here, because a height collapse
       never moves the width the diagram measures itself against. */
    .duo-col--net{ transition:opacity 220ms var(--e-out); }
    .duo.eco-active .duo-grid{ grid-template-rows:0fr auto; row-gap:0; }
    /* visibility is delayed to the end of the fade so the block does not blink
       out before it has finished dissolving, and pointer-events go immediately
       so nothing inside a closing block is clickable. */
    .duo.eco-active .duo-col--net{
      opacity:0; visibility:hidden; pointer-events:none;
      transition:opacity 220ms var(--e-out), visibility 0s linear 220ms;
    }
    /* Nothing above it to match any more, so the assistant sizes to its own
       content -- and gets more of the viewport than it does idle. */
    .duo.eco-active .duo-col--eco{ align-self:start; }
    .duo.eco-active .stg-network .stg-embed{
      height:clamp(620px,82vh,860px) !important; max-height:none;
    }
    @media (max-width:560px){
      .duo.eco-active .stg-network .stg-embed{ height:clamp(560px,80vh,760px) !important; }
    }

    /* ---------- 8C. TWO COLUMNS, FROM 1280 UP ----------
       The operator asked for the side-by-side reading back: Our Network on the
       left -- heading, standfirst, the 2x2 quadrants and the overlap row --
       and the assistant on the right, tops level and bottoms level. Everything
       above this rule is the STACKED composition, and it stays exactly as it
       is: it is what every width below 1280 gets, and it is still the base the
       cascade falls back to.

       1280 is not a taste call, it is the narrowest viewport at which a HALF
       column is still wide enough for the diagram inside it. The frame is not
       the viewport: `.wrap` is `max-width:1360px` with `clamp(20px,4.6vw,76px)`
       of padding a side, so the grid's content box is 1162px at a 1280 viewport
       and 1227px at 1440 (the --maxw ceiling, not 0.908 x 1440). Halved, minus
       the `clamp(18px,2vw,26px)` gap, that is 568px and 601px -- both clear of
       the 560px frame width at which the embedded diagram stacks its quadrants
       into one column. 1280 is the tightest point of the whole band: 1300 ->
       577, 1360 -> 604, 1600 -> 620, 2560 -> 583 (the ultrawide block at the end
       of this sheet re-tunes --maxw/--gutter, and the halves stay above 560 all
       the way out).
       Solve half(vw) = 560 and the crossover is a 1261.3px viewport, so the
       hinge is the contractual 1280 rather than the arithmetic 1262. But the
       8px of margin that buys is a NO-SCROLLBAR figure, and it is the reason the
       floor below is not decoration. `vw` units are resolved against the
       viewport INCLUDING a classic scrollbar while the layout box excludes it,
       so on a 1280px window with a 17px bar the half computes to 559.8px -- one
       side of 560, and the diagram would stack. The floor is what actually
       holds the boundary there; the hinge only holds it on overlay scrollbars. */
    @media (min-width:1280px){
      .duo-grid{
        /* The network column LEADS. 1.06fr against 1fr is the operator's
           "slightly larger and wider" as a number rather than a feeling: at
           1440 it moves the split from 600.8/600.8 to 618.3/583.3, so the
           diagram gains 17.5px and the assistant gives up the same. 1.06 is
           the ceiling, not a first guess -- the pair has to stay READABLE as
           two columns of one section rather than as a main and a sidebar, and
           the assistant's own frame is the constraint that sets it: at the
           1280 hinge 1.06 leaves the chat 551.7px, and the explorer's reply
           tables start wrapping their second column under ~540px.
           The floor below still applies to the LEFT track, which is now the
           wider of the two -- so the sideways collapse gets MORE headroom over
           the diagram's 560px stacking threshold than it had at parity
           (584.7px at the hinge, up from 568.2px), not less. */
        grid-template-columns:minmax(0,1.06fr) minmax(0,1fr);
        /* ONE row. The stacked base declares `1fr auto` for its collapsing row
           track; left in place beside a second column it would put a phantom
           second track under the pair and charge the section a row-gap for it. */
        grid-template-rows:auto;
        /* Bottoms level: both columns take the row's height, and the flex chain
           below passes that height down to the assistant frame. */
        align-items:stretch;
        /* Engaging collapses a COLUMN here, not a row -- so the animated
           property changes with the axis. Both endpoints are <flex> values
           (1fr -> 0fr) because `auto -> 0fr` is not interpolable and snaps in a
           single frame, and the gap is animated alongside the track or the
           assistant stops 26px short of the edge it just won. */
        transition:grid-template-columns 420ms var(--e-out),
                   column-gap 420ms var(--e-out);
      }
      /* THE FLOOR THAT MAKES A SIDEWAYS COLLAPSE SAFE.
         The diagram measures ITSELF and posts its height to this page. Squeeze
         its frame toward zero width and it re-measures against nothing: at the
         560px threshold its quadrants stack one-per-row and it reports 1251px
         for 546px of content (measured on staging). The old two-column build
         hit that on every engage and patched it downstream, in the message
         handler.
         Pinning the resting width of the network column's own children fixes it
         upstream instead: the track animates 601px -> 0 and clips them
         (`.duo-col--net` is `overflow:hidden`), while the frame inside never
         goes under 561px and so never crosses its own breakpoint. Sampled frame
         by frame through a collapse, the iframe held 561px wide and 700px tall
         throughout. It also stops the standfirst re-wrapping on its way out,
         which used to change the height of the row both columns share.
         `minmax(0,1fr)` on the tracks above is what keeps this floor from
         inflating the grid: a `1fr` track would otherwise take its minimum from
         its content's min-width. */
      .duo-col--net > .duo-head,
      .duo-col--net > iframe{ min-width:561px; }

      /* THE HEADINGS, NOT JUST THE BOXES.
         `align-items:stretch` already starts both columns on the same line --
         measured, `.duo-head` and `.stg-explorer-card` share a top to within
         0.0px. What it cannot do is start their TEXT on the same line, because
         only one of them is a padded panel: the assistant's first ink sits
         below the card's 2px transparent border and its own block padding,
         while the network's h2 begins immediately. Measured on master that was
         61.6 against 83.6 at 1440 -- 22px of visible misalignment between two
         boxes a harness happily called level, and the operator's "including
         their headings".
         The offset is therefore DERIVED from the card rather than eyeballed:
         `.stg-card` ships `border:2px solid transparent` and this file gives it
         `padding:clamp(14px,1.5vw,20px)` in the block direction, so those two
         terms ARE the distance. Restating them keeps the two sides bound
         together -- retune the card's padding and the heading follows it, which
         a hard-coded 22px would not do. Checked as a number by
         network_layout_check.py (`headingRowDelta`), not by eye. */
      /* The desktop skeleton. Two columns is the one composition where the
         diagram is taller than the 700px the base rule assumes -- the enlarged
         quadrant cards settle it at ~745px at the hinge and ~760px at 1600 --
         and it is also the composition where being wrong costs the most, since
         the assistant now takes ITS height from this column. Overriding here
         rather than on the base rule leaves every stacked width (a 1024 frame
         still settles at 658px) with the skeleton that actually fits it. */
      .duo-col--net > iframe{ height:750px; }

      .duo-col--net > .duo-head{
        padding-top:calc(2px + clamp(14px,1.5vw,20px));
        /* And the space it costs is taken back on the other side. 18px between
           the standfirst and the diagram was tuned for the stacked composition
           where the block below is the full page wide; beside the assistant it
           is one of the "unnecessary empty space" gaps the operator named.
           13px still reads as a deliberate break rather than a collision, and
           the pair nets out 5px taller, not 22px. */
        margin-bottom:clamp(10px,1vw,13px);
      }

      /* The assistant is the shorter column, so it is the one that stretches.
         `align-items:stretch` only sizes the COLUMN; without this chain the
         card keeps its content height and the difference reads as dead white
         space inside a full-height box -- the exact artefact the shared subgrid
         row used to exist to remove. The subgrid does NOT come back: it needed
         a header in BOTH columns to have something to align, and the right-hand
         header was deleted on operator request. A flex chain needs no second
         header and leaves no empty one behind.
         `flex:1 0 auto` on the frame keeps the embed's own clamp as the basis
         and lets it GROW into the extra height -- it may never shrink below the
         height it asks for. */
      .duo-col--eco{ display:flex; }
      .duo-col--eco > .stg-network{ display:flex; flex-direction:column;
                                    flex:1 1 auto; min-width:0; }
      .duo-col--eco .stg-explorer-card{ display:flex; flex-direction:column;
                                        flex:1 1 auto; }
      .duo-col--eco .stg-frame{ display:flex; flex-direction:column; flex:1 1 auto; }
      /* WHY THIS IS NOT `flex:1 0 auto` ANY MORE.
         `height:clamp(560px,64vh,700px)` on the base rule is a VIEWPORT-driven
         number, and as a flex basis it decides how tall the assistant column
         WANTS to be. The diagram's height is CONTENT-driven and effectively
         constant across the desktop band (~751px at 1440). Two columns sized by
         two unrelated quantities can only agree by coincidence, and they do:
         they agree at ~900px of viewport height and nowhere else. Measured at
         1440: the bottoms are 9.4px apart on a 900-tall window, and 133px apart
         on a 1400-tall one, because 64vh saturates at the 700px cap while the
         diagram has not moved. That second number is the state the operator
         screenshotted, and the reason the existing harness never caught it is
         that it only ever samples 860/900-tall viewports.
         Dropping the viewport out of the basis fixes it at the source. With
         `height:auto` the basis is the iframe's intrinsic size, the floor below
         is what keeps the conversation usable if the diagram ever shrinks, and
         `flex-grow:1` takes the frame to whatever the row actually is. The
         NETWORK column therefore sets the row at every viewport height and the
         assistant lands exactly on its bottom edge -- which is the whole of
         "vertically aligned and visually balanced on desktop".
         A BASIS, NOT A FLOOR, and that distinction is the one thing this
         rule may not get wrong. `test_the_assistant_is_no_longer_floored_
         against_a_diagram_it_shares_a_row_with` forbids a `min-height` on the
         idle frame outright, and it is right to: a min-height the diagram
         cannot reach stops behaving like a floor and becomes the row height
         instead, which is exactly how `clamp(360px,42vh,440px)` once held the
         row open past the end of a shortened diagram and put 125px of dead
         white space under it at 1440. A flex BASIS cannot do that -- it is
         where the frame starts before growing, not a size it insists on.
         420px, flat, is chosen for headroom rather than for looks, because
         nothing here ever renders at 420: the column's max-content is
         420 + ~201px of card chrome = ~621px against a network column of
         ~830px at the hinge, so the assistant has 200px of margin before it
         could become the taller of the two and reopen that artefact from the
         other direction. What the reader actually sees is the row: ~633px at
         1440, comfortably inside the 560-700px band the stacked rule uses.
         Engaged (`.duo.eco-active .stg-network .stg-embed`, (0,3,0)) and
         popped-out (`body.npe-popout ... .stg-embed`, (0,4,1)) both outrank
         this rule, so neither state changes. */
      .duo-col--eco .stg-embed{
        flex:1 1 auto; height:420px !important; max-height:none;
      }

      /* Engaged: the left track closes and the gap closes with it, so the
         assistant ends up owning the whole content width rather than stopping
         one gap short of it. The row track is restated because the stacked
         `grid-template-rows:0fr auto` sets a declaration nothing here would
         otherwise replace, and this rule can only replace it by SOURCE ORDER:
         both selectors are `.duo.eco-active .duo-grid`, i.e. (0,3,0), and a
         media query contributes no specificity of its own. Move this block
         above 8B and the desktop grid silently collapses on the row axis
         instead of the column one.
         Deliberately NOT here: `max-height:0` on the block or `height:0` on its
         children. Either one lands the network at nothing on frame one and
         there is no animation left to watch -- the track closing IS the
         animation, and the floor above is what keeps the clipped content
         honest while it happens.
         The price of leaving them out is worth writing down: the collapsed
         column keeps its content height behind `overflow:hidden`, so it still
         sizes the row both columns share. Where the engaged assistant is the
         SHORTER of the two -- a short viewport, where `clamp(620px,82vh,860px)`
         floors at 620px while the diagram is still ~760px -- the difference
         reads as dead space under the card. `align-self:stretch` on the engaged
         column would close it -- by growing the chat frame past the "engaged
         frame is more than 0.92 of the viewport" ceiling npe_host_check.py
         calls "something is inflating the row", which is the other half of the
         original report. The gap is the deliberate side of that trade, not an
         oversight. */
      .duo.eco-active .duo-grid{
        grid-template-columns:minmax(0,0fr) minmax(0,1fr);
        grid-template-rows:auto; column-gap:0;
      }
    }

    @media (prefers-reduced-motion: reduce){
      .duo-grid, .duo-col--net,
      .duo.eco-active .duo-grid, .duo.eco-active .duo-col--net{ transition:none; }
      /* grid-template-rows is what animates now; naming it explicitly keeps
         this escape hatch honest if the property changes again. */
      .duo.eco-active .eco-restore{ animation:none; }
      /* The page-wide reduced-motion rule only shortens transition-DURATION; it
         never cancels a hover transform, so the lift still ran here. */
      .stg-network .stg-card, .stg-network .stg-card:hover{
        transition:none !important; transform:none !important;
      }
    }

    /* Pop-out. The iframe is NEVER moved or cloned: only the wrapper it already
       lives in is promoted to a fixed layer, so the conversation, the open SSE
       stream and the microphone permission all survive opening and closing. */
    body.npe-popout{ overflow:hidden; }
    .npe-scrim{
      display:none; position:fixed; inset:0; z-index:9998;
      background:rgba(9,35,61,.55); backdrop-filter:blur(2px);
    }
    body.npe-popout .npe-scrim{ display:block; }
    body.npe-popout .duo-col--eco .stg-frame{
      position:fixed; z-index:9999; inset:clamp(12px,3vh,40px) clamp(12px,3vw,56px);
      display:flex; flex-direction:column; border-radius:14px; overflow:hidden;
      box-shadow:0 24px 70px rgba(9,35,61,.35); background:#fff;
    }
    body.npe-popout .duo-col--eco .stg-frame .stg-embed{
      flex:1; height:auto !important; min-height:0 !important; max-height:none !important;
    }
    body.npe-popout .duo-col--eco .stg-frame .stg-fallback{ display:none; }

    /* ---------- 9. SCENE 4 — THE FIELD (#expertise) ---------- */
    .field{ background:transparent; padding-block:var(--scene-py);
            scroll-margin-top:calc(var(--nav-h) + 8px); }
    .film{
      position:relative; width:100%; aspect-ratio:21/9; overflow:hidden;
      background:var(--paper-edge); border-radius:var(--r-chip);
      margin-bottom:clamp(40px,5vw,86px);
    }
    .film video, .film img{
      position:absolute; inset:0; width:100%; height:100%;
      object-fit:cover; object-position:50% 58%;
    }
    .film video{ z-index:1; opacity:0; transition:opacity 900ms var(--e-out); }
    .film video.playing{ opacity:1; }
    @media (prefers-reduced-motion: reduce){ .film video{ transition:none; } }
    .film-note{
      position:absolute; left:0; bottom:0; z-index:3;
      display:flex; align-items:center; gap:14px;
      background:var(--navy); color:var(--on-navy);
      padding:12px clamp(16px,2vw,26px);
      font-family:var(--f-display); font-size:var(--fs-micro); font-weight:500;
      letter-spacing:.17em; text-transform:uppercase;
    }
    .film-note b{ color:var(--sky-soft); font-weight:500; }

    .field-head{
      display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
      gap:clamp(20px,3vw,64px); align-items:end;
      margin-bottom:clamp(36px,4.4vw,72px);
      background:var(--paper);
    }
    .field-head h2{ font-size:var(--fs-h2); letter-spacing:-.03em; }
    .field-head p{ color:var(--ink-2); font-size:var(--fs-small); padding-bottom:6px; }

    .values{ border-top:1px solid var(--line-2); }
    .value{
      display:grid; grid-template-columns:64px minmax(0,1.05fr) minmax(0,1.35fr);
      gap:clamp(16px,2.4vw,44px); align-items:start;
      padding:clamp(24px,2.9vw,42px) 0;
      border-bottom:1px solid var(--line);
      transition:background var(--d2) var(--e-out);
      background-color:var(--paper);
    }
    .value .vn{
      font-family:var(--f-display); font-size:var(--fs-meta); font-weight:500;
      letter-spacing:.14em; color:var(--ink-3); padding-top:5px;
      transition:color var(--d2) var(--e-out);
    }
    .value h3{ font-size:var(--fs-h3); letter-spacing:-.02em;
               display:flex; align-items:center; gap:14px; }
    .value p{ color:var(--ink-2); max-width:46ch; }
    @media (pointer:fine){
      /* background-image, not the shorthand: the shorthand resets
         background-color and would drop the row's paper onto the field. */
      .value:hover{ background-image:linear-gradient(rgba(0,76,151,.028),rgba(0,76,151,.028)); }
      .value:hover .vn{ color:var(--brand); }
    }

    /* ---------- 10. HELP BAND ---------- */
    .help{ background:transparent; padding-block:clamp(26px,2.6vw,44px); }
    .help-inner{
      display:grid; grid-template-columns:minmax(0,1fr) auto;
      gap:clamp(22px,3vw,56px); align-items:center;
      background:var(--paper-white);
    }
    .help h2{
      font-family:var(--f-display); font-size:var(--fs-label); font-weight:500;
      letter-spacing:.19em; text-transform:uppercase; color:var(--ink-2);
      margin-bottom:12px;
    }
    .help p{ font-size:var(--fs-lead); color:var(--ink); max-width:42ch;
             font-family:var(--f-display); font-weight:400; letter-spacing:-.012em; }
    .mail{
      display:inline-flex; align-items:center; gap:12px; min-height:52px;
      font-family:var(--f-display); font-size:var(--fs-h3);
      font-weight:500; letter-spacing:-.02em; color:var(--brand);
      border-bottom:1px solid rgba(0,76,151,.3); padding-bottom:2px;
      transition:border-color var(--d2) var(--e-out);
    }
    @media (pointer:fine){ .mail:hover{ border-color:var(--brand); } }

    /* Featured updates stay static on the plain-white page surface. */
    .reel{ background:transparent; padding:clamp(22px,3vw,40px) 0 0; }
    .reel-head{ max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gutter);
      display:flex; align-items:center; gap:16px; margin-bottom:clamp(12px,1.6vw,20px);
      background:var(--paper); background-clip:content-box; }
    .reel-head .rule{ flex:1; height:1px; background:var(--line); }
    .reel-band{ overflow:hidden; border-block:1px solid var(--line); background:transparent; }
    .reel-track{
      display:flex; width:max-content; margin:0; list-style:none;
      padding:clamp(14px,1.7vw,22px) 0;
      animation:reel-scroll 62s linear infinite;
    }
    @media (hover:hover) and (pointer:fine){
      .reel-band:hover .reel-track,
      .reel-band:focus-within .reel-track{ animation-play-state:paused; }
      .reel-card:hover{ border-color:var(--sky); transform:translateY(-2px); }
    }
    .reel-track li{ flex:0 0 auto; margin-right:clamp(12px,1.4vw,18px); }
    .reel-card{
      display:grid; grid-template-columns:132px minmax(0,1fr);
      gap:0 14px; align-items:start;
      width:clamp(320px,30vw,428px); min-height:100%;
      padding:16px 18px 18px; background:var(--paper-white);
      border:1px solid var(--line-2); border-radius:var(--r-card);
      text-decoration:none; color:var(--ink);
      transition:border-color var(--d2) var(--e-out), transform var(--d2) var(--e-out);
    }
    /* the thumbnail spans all three text rows on the left */
    .rc-thumb{
      grid-column:1; grid-row:1 / span 3;
      width:132px; aspect-ratio:4/3; object-fit:cover;
      border-radius:var(--r-chip); background:var(--paper-edge);
      border:1px solid var(--line);
    }
    .reel-card .rc-tag,
    .reel-card .rc-title,
    .reel-card .rc-sub{ grid-column:2; }
    .reel-card .rc-tag{ margin-bottom:6px; }
    .reel-card .rc-title{ margin-bottom:6px; }
    @media (max-width:640px){
      .reel-card{ grid-template-columns:96px minmax(0,1fr); width:clamp(280px,86vw,360px); }
      .rc-thumb{ width:96px; }
    }
    .motion-toggle{ min-height:32px; padding:5px 9px; border:1px solid rgba(0,76,151,.34); background:transparent;
      color:var(--brand); font:600 var(--fs-micro)/1 var(--f-display); letter-spacing:.1em; text-transform:uppercase; cursor:pointer; }
    .motion-toggle[aria-pressed="true"]{ border-color:var(--brand); color:var(--brand); }
    .rc-tag{ font-family:var(--f-display); font-size:var(--fs-micro); font-weight:600;
      letter-spacing:.14em; text-transform:uppercase; color:var(--brand); }
    .rc-title{ font-size:var(--fs-small); font-weight:600; line-height:1.35; }
    .rc-sub{ font-size:var(--fs-meta); color:var(--ink-3); }
    @keyframes reel-scroll{ from{ transform:translateX(0); } to{ transform:translateX(-50%); } }
    @media (prefers-reduced-motion: reduce){
      .reel-track{ animation:none; width:auto; }
      .reel-band{ overflow-x:auto; }
      .reel-track li.dup{ display:none; }
    }

    /* ---------- 13. FOOTER — the single earned dark passage ---------- */
    .site-footer{ background:var(--navy); color:var(--on-navy-2);
                  padding-block:clamp(56px,6.4vw,104px) clamp(36px,4vw,64px); }
    .footer-grid{
      display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1fr) minmax(0,.7fr) minmax(0,.7fr);
      gap:clamp(28px,3.4vw,64px);
    }
    .footer-logo img{ height:44px; width:auto; margin-bottom:22px; filter:brightness(0) invert(1); }
    .footer-tagline{ color:var(--on-navy-2); font-size:var(--fs-small); max-width:42ch; }
    .site-footer h4{
      font-family:var(--f-display); font-size:var(--fs-label); font-weight:500;
      letter-spacing:.18em; text-transform:uppercase; color:var(--sky-soft);
      margin-bottom:18px;
    }
    .footer-social-icons{ display:flex; gap:10px; }
    .footer-social-icons a{
      width:44px; height:44px; display:inline-flex; align-items:center; justify-content:center;
      border:1px solid rgba(255,255,255,.22); border-radius:var(--r-btn);
      color:var(--on-navy);
      transition:background var(--d2) var(--e-out), border-color var(--d2) var(--e-out);
    }
    .footer-social-icons .ico{ width:16px; height:16px; display:block; }
    @media (pointer:fine){
      .footer-social-icons a:hover{ background:rgba(255,255,255,.12);
                                    border-color:var(--sky-soft); }
    }
    .footer-contact-info a,
    .footer-links a, .footer-bottom-links a{
      display:inline-flex; align-items:center; min-height:44px;
      color:var(--on-navy-2); font-size:var(--fs-small);
      transition:color var(--d2) var(--e-out);
    }
    .footer-contact-info p{ margin:0; }
    .footer-links, .footer-bottom-links{ display:flex; flex-direction:column; }
    @media (pointer:fine){
      .footer-contact-info a:hover,
      .footer-links a:hover, .footer-bottom-links a:hover{ color:#fff; }
    }
    .footer-rule{ height:1px; background:rgba(255,255,255,.14);
                  margin:clamp(38px,4.4vw,72px) 0 22px; }
    .footer-base{
      display:flex; flex-wrap:wrap; gap:12px 26px; justify-content:space-between;
      font-family:var(--f-display); font-size:var(--fs-micro); letter-spacing:.14em;
      text-transform:uppercase; color:rgba(255,255,255,.62);
    }

    /* ---------- 14. RESPONSIVE ---------- */
    @media (max-width:1200px){
      .stage-body{ grid-template-columns:138px minmax(0,1fr) minmax(260px,330px); }
    }

    /* --- tablet & below: nav becomes a drawer --- */
    @media (max-width:1024px){
      :root{ --nav-h:62px; }
      .nav-toggle{ display:inline-flex; }
      .header-nav{ display:none; }
      .header-nav.open{
        display:flex; flex-direction:column; align-items:stretch; gap:0;
        position:absolute; left:0; right:0; top:var(--nav-h);
        background:var(--paper-white); border-bottom:1px solid var(--line);
        box-shadow:0 18px 40px -24px rgba(14,42,66,.35);
        padding:6px var(--gutter) 14px;
      }
      .header-nav.open a{ min-height:52px; border-bottom:1px solid var(--line); }
      .header-nav.open a::after{ display:none; }
      /* Six rows now, not five. On a short or landscape phone the drawer is
         taller than the viewport, and opening it locks body scroll -- so the
         last items (Our App, Contact) were unreachable. Bounded height plus
         its own scroller; invisible at ordinary portrait heights, where the
         drawer is far shorter than this ceiling. */
      .header-nav.open{ max-height:calc(100vh - var(--nav-h) - 44px);
                        overflow-y:auto; overscroll-behavior:contain;
                        -webkit-overflow-scrolling:touch; }

      .hero-foot{ grid-template-columns:1fr; gap:clamp(26px,4vw,40px); }
      /* stacked: the CTA shares the copy's left edge again */
      .hero-cta{ justify-content:flex-start; align-self:start; }
      /* full width once stacked: centred content-width buttons leave the
         long second label one word per line on a narrow phone */
      .hero-cta, .hero-cta-stack{ width:100%; }
      .hero-cta-stack{ align-items:stretch; }
      .hero-cta-stack .btn{ justify-content:center; }
      .caps{ grid-template-columns:repeat(3,minmax(0,1fr)); gap:18px; }

      .field-head{ grid-template-columns:1fr; align-items:start; gap:14px; }
      .value{ grid-template-columns:44px minmax(0,1fr); gap:14px 18px; }
      .value p{ grid-column:2; max-width:52ch; }
      .help-inner{ grid-template-columns:1fr; gap:22px; align-items:start; }
      .footer-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); }
    }

    @media (max-width:760px){
      .caps{ grid-template-columns:1fr; gap:26px; }
      .film{ aspect-ratio:16/10; }
      .footer-grid{ grid-template-columns:1fr; gap:34px; }
    }

    @media (max-width:480px){
      :root{ --nav-h:58px; }
      body{ line-height:1.6; }
      .admin-bar{ min-height:44px; font-size:var(--fs-meta); }
      .admin-bar span{ min-height:44px; }
      .admin-bar a{ min-height:44px; }
      .hero{ --band-h:clamp(132px,20vh,190px); }
      .hero-title{ line-height:1.0; letter-spacing:-.03em; }
      .hero-band-note{ font-size:var(--fs-micro); letter-spacing:.14em; }
      .btn{ min-height:48px; width:100%; justify-content:space-between; }
      .hero-cta .btn{ max-width:340px; }
      .value{ grid-template-columns:1fr; gap:10px; }
      .value .vn{ padding-top:0; }
      .value p{ grid-column:1; }
      .zone-btn{ min-height:56px; }
      .footer-social-icons a{ width:46px; height:46px; }
      .mail{ font-size:var(--fs-lead); }
    }


    /* ---------- 14b. ULTRAWIDE — keep editorial measure, grow the stage ---------- */
    @media (min-width:1600px){
      :root{
        /* 1400px is the ceiling: past it the cards and copy read as
           disconnected islands on a 2560px monitor (operator, 2026-09-03). */
        --maxw: 1400px;
        --gutter: clamp(32px, 4.2vw, 104px);
      }
      .hero-lede p{ max-width:50ch; }
      .value p{ max-width:40ch; }
      .help p{ max-width:40ch; }
      .footer-tagline{ max-width:40ch; }
    }

    /* ---------- 15. REDUCED MOTION — the page must be complete with zero motion ---------- */
    @media (prefers-reduced-motion: reduce){
      html{ scroll-behavior:auto; }
      .js .rv{ opacity:1 !important; transform:none !important;
               transition:none !important; will-change:auto !important; filter:none !important; }
      *,*::before,*::after{
        animation-duration:.001ms !important;
        animation-iteration-count:1 !important;
        transition-duration:.001ms !important;
        scroll-behavior:auto !important;
      }
      .btn:hover .arw,
    }
    
    /* PAGE WEBBING — one continuous lattice backdrop behind the whole page,
       the same motif and settings the Projects page already uses. It is
       `fixed` so the webbing does not restart or stop at section seams as the
       reader scrolls; `pointer-events:none` so it never intercepts a click. */
    .page-field{ position:fixed; inset:0; z-index:0; opacity:.62; pointer-events:none; }
    /* .62 not .5 -- operator asked for the webbing a little more present
       2026-09-03. Still a backdrop: the lines land ~0.031 alpha over the
       paper, and it stays behind every card because .site-header/main/
       .site-footer sit in their own z-index:1 layer. */
    /* Its own compositor layer. A full-viewport position:fixed canvas is
       repainted on every scroll frame otherwise, which is what made the page
       judder: dropped frames went from ~0% to ~5% while scrolling Projects. */
    .page-field{ will-change:transform; transform:translateZ(0);
                 backface-visibility:hidden; contain:layout paint; }
    .page-field canvas{ display:block; width:100%; height:100%; }
    /* A fixed z-index:0 backdrop is a POSITIONED element, so it paints after
       every ordinary in-flow background -- the webbing was landing on top of
       cards, images and even the header logo. Lifting the three page regions
       into their own layer puts the field strictly behind the content, where
       the containers' own backgrounds mask it. */
    .site-header, main, .site-footer{ position:relative; z-index:1; }
    /* ...but they must not all land on the SAME level. `main` comes later in
       the DOM, so at an equal z-index it paints OVER the header -- and the
       mobile drawer is absolutely positioned inside the header. Measured
       2026-09-04 at 390px: elementFromPoint at the centre of every drawer row
       returned page content (SECTION.app-scene, H1.app-title, P.app-lead), so
       not one nav link was clickable on a phone. The header's own rule already
       asked for 50; restore just the z-index -- deliberately NOT `position`,
       which would re-stick five shipped pages. 50 keeps it under the
       z-index:60 admin bar and over the z-index:0 webbing field. */
    .site-header{ z-index:50; }
